Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limit nesting of json commands #2326

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions src/ripple/json/impl/json_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ namespace Json
// Implementation of class Reader
// ////////////////////////////////

constexpr unsigned Reader::nest_limit;

static
std::string
codePointToUTF8 (unsigned int cp)
Expand Down Expand Up @@ -118,8 +120,7 @@ Reader::parse ( const char* beginDoc, const char* endDoc,
nodes_.pop ();

nodes_.push ( &root );

bool successful = readValue ();
bool successful = readValue(0);
Token token;
skipCommentTokens ( token );

Expand All @@ -138,20 +139,22 @@ Reader::parse ( const char* beginDoc, const char* endDoc,
}

bool
Reader::readValue ()
Reader::readValue(unsigned depth)
{
Token token;
skipCommentTokens ( token );
if (depth > nest_limit)
return addError("Syntax error: maximum nesting depth exceeded", token);
bool successful = true;

switch ( token.type_ )
{
case tokenObjectBegin:
successful = readObject ( token );
successful = readObject(token, depth);
break;

case tokenArrayBegin:
successful = readArray ( token );
successful = readArray(token, depth);
break;

case tokenInteger:
Expand Down Expand Up @@ -427,7 +430,7 @@ Reader::readString ()


bool
Reader::readObject ( Token& tokenStart )
Reader::readObject(Token& tokenStart, unsigned depth)
{
Token tokenName;
std::string name;
Expand Down Expand Up @@ -469,7 +472,7 @@ Reader::readObject ( Token& tokenStart )

Value& value = currentValue ()[ name ];
nodes_.push ( &value );
bool ok = readValue ();
bool ok = readValue(depth+1);
nodes_.pop ();

if ( !ok ) // error already set
Expand Down Expand Up @@ -504,7 +507,7 @@ Reader::readObject ( Token& tokenStart )


bool
Reader::readArray ( Token& tokenStart )
Reader::readArray(Token& tokenStart, unsigned depth)
{
currentValue () = Value ( arrayValue );
skipSpaces ();
Expand All @@ -522,7 +525,7 @@ Reader::readArray ( Token& tokenStart )
{
Value& value = currentValue ()[ index++ ];
nodes_.push ( &value );
bool ok = readValue ();
bool ok = readValue(depth+1);
nodes_.pop ();

if ( !ok ) // error already set
Expand Down
8 changes: 5 additions & 3 deletions src/ripple/json/json_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class Reader
*/
std::string getFormatedErrorMessages () const;

static constexpr unsigned nest_limit {1000};

private:
enum TokenType
{
Expand Down Expand Up @@ -129,9 +131,9 @@ class Reader
bool readCppStyleComment ();
bool readString ();
Reader::TokenType readNumber ();
bool readValue ();
bool readObject ( Token& token );
bool readArray ( Token& token );
bool readValue(unsigned depth);
bool readObject(Token& token, unsigned depth);
bool readArray (Token& token, unsigned depth);
bool decodeNumber ( Token& token );
bool decodeString ( Token& token );
bool decodeString ( Token& token, std::string& decoded );
Expand Down
49 changes: 49 additions & 0 deletions src/test/json/json_value_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include <ripple/beast/unit_test.h>
#include <ripple/beast/type_name.h>

#include <algorithm>

namespace ripple {

struct json_value_test : beast::unit_test::suite
Expand Down Expand Up @@ -249,6 +251,52 @@ struct json_value_test : beast::unit_test::suite
}
}

void test_nest_limits ()
{
Json::Reader r;
{
auto nest = [](std::uint32_t depth)->std::string {
std::string s = "{";
for (std::uint32_t i{1}; i <= depth; ++i)
s += "\"obj\":{";
for (std::uint32_t i{1}; i <= depth; ++i)
s += "}";
s += "}";
return s;
};

{
// Within object nest limit
auto json{nest(std::min(10u, Json::Reader::nest_limit))};
Json::Value j;
BEAST_EXPECT(r.parse(json, j));
}

{
// Exceed object nest limit
auto json{nest(Json::Reader::nest_limit + 1)};
Json::Value j;
BEAST_EXPECT(!r.parse(json, j));
}
}

auto nest = [](std::uint32_t depth)->std::string {
std::string s = "{";
for (std::uint32_t i{1}; i <= depth; ++i)
s += "\"array\":[{";
for (std::uint32_t i{1}; i <= depth; ++i)
s += "]}";
s += "}";
return s;
};
{
// Exceed array nest limit
auto json{nest(Json::Reader::nest_limit + 1)};
Json::Value j;
BEAST_EXPECT(!r.parse(json, j));
}
}

void run ()
{
test_bool ();
Expand All @@ -258,6 +306,7 @@ struct json_value_test : beast::unit_test::suite
test_move ();
test_comparisons ();
test_compact ();
test_nest_limits ();
}
};

Expand Down