Limit nesting of json commands

This commit is contained in:
Howard Hinnant
2018-01-09 16:26:22 -05:00
committed by Scott Schurr
parent 718d217158
commit 0ec66b3dbc
3 changed files with 66 additions and 12 deletions

View File

@@ -29,6 +29,8 @@ namespace Json
// Implementation of class Reader // Implementation of class Reader
// //////////////////////////////// // ////////////////////////////////
constexpr unsigned Reader::nest_limit;
static static
std::string std::string
codePointToUTF8 (unsigned int cp) codePointToUTF8 (unsigned int cp)
@@ -118,8 +120,7 @@ Reader::parse ( const char* beginDoc, const char* endDoc,
nodes_.pop (); nodes_.pop ();
nodes_.push ( &root ); nodes_.push ( &root );
bool successful = readValue(0);
bool successful = readValue ();
Token token; Token token;
skipCommentTokens ( token ); skipCommentTokens ( token );
@@ -138,20 +139,22 @@ Reader::parse ( const char* beginDoc, const char* endDoc,
} }
bool bool
Reader::readValue () Reader::readValue(unsigned depth)
{ {
Token token; Token token;
skipCommentTokens ( token ); skipCommentTokens ( token );
if (depth > nest_limit)
return addError("Syntax error: maximum nesting depth exceeded", token);
bool successful = true; bool successful = true;
switch ( token.type_ ) switch ( token.type_ )
{ {
case tokenObjectBegin: case tokenObjectBegin:
successful = readObject ( token ); successful = readObject(token, depth);
break; break;
case tokenArrayBegin: case tokenArrayBegin:
successful = readArray ( token ); successful = readArray(token, depth);
break; break;
case tokenInteger: case tokenInteger:
@@ -427,7 +430,7 @@ Reader::readString ()
bool bool
Reader::readObject ( Token& tokenStart ) Reader::readObject(Token& tokenStart, unsigned depth)
{ {
Token tokenName; Token tokenName;
std::string name; std::string name;
@@ -469,7 +472,7 @@ Reader::readObject ( Token& tokenStart )
Value& value = currentValue ()[ name ]; Value& value = currentValue ()[ name ];
nodes_.push ( &value ); nodes_.push ( &value );
bool ok = readValue (); bool ok = readValue(depth+1);
nodes_.pop (); nodes_.pop ();
if ( !ok ) // error already set if ( !ok ) // error already set
@@ -504,7 +507,7 @@ Reader::readObject ( Token& tokenStart )
bool bool
Reader::readArray ( Token& tokenStart ) Reader::readArray(Token& tokenStart, unsigned depth)
{ {
currentValue () = Value ( arrayValue ); currentValue () = Value ( arrayValue );
skipSpaces (); skipSpaces ();
@@ -522,7 +525,7 @@ Reader::readArray ( Token& tokenStart )
{ {
Value& value = currentValue ()[ index++ ]; Value& value = currentValue ()[ index++ ];
nodes_.push ( &value ); nodes_.push ( &value );
bool ok = readValue (); bool ok = readValue(depth+1);
nodes_.pop (); nodes_.pop ();
if ( !ok ) // error already set if ( !ok ) // error already set

View File

@@ -81,6 +81,8 @@ public:
*/ */
std::string getFormatedErrorMessages () const; std::string getFormatedErrorMessages () const;
static constexpr unsigned nest_limit {1000};
private: private:
enum TokenType enum TokenType
{ {
@@ -129,9 +131,9 @@ private:
bool readCppStyleComment (); bool readCppStyleComment ();
bool readString (); bool readString ();
Reader::TokenType readNumber (); Reader::TokenType readNumber ();
bool readValue (); bool readValue(unsigned depth);
bool readObject ( Token& token ); bool readObject(Token& token, unsigned depth);
bool readArray ( Token& token ); bool readArray (Token& token, unsigned depth);
bool decodeNumber ( Token& token ); bool decodeNumber ( Token& token );
bool decodeString ( Token& token ); bool decodeString ( Token& token );
bool decodeString ( Token& token, std::string& decoded ); bool decodeString ( Token& token, std::string& decoded );

View File

@@ -24,6 +24,8 @@
#include <ripple/beast/unit_test.h> #include <ripple/beast/unit_test.h>
#include <ripple/beast/type_name.h> #include <ripple/beast/type_name.h>
#include <algorithm>
namespace ripple { namespace ripple {
struct json_value_test : beast::unit_test::suite struct json_value_test : beast::unit_test::suite
@@ -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 () void run ()
{ {
test_bool (); test_bool ();
@@ -258,6 +306,7 @@ struct json_value_test : beast::unit_test::suite
test_move (); test_move ();
test_comparisons (); test_comparisons ();
test_compact (); test_compact ();
test_nest_limits ();
} }
}; };