diff --git a/src/ripple/json/impl/json_reader.cpp b/src/ripple/json/impl/json_reader.cpp index 549b1aea9c..45b2794c77 100644 --- a/src/ripple/json/impl/json_reader.cpp +++ b/src/ripple/json/impl/json_reader.cpp @@ -29,6 +29,8 @@ namespace Json // Implementation of class Reader // //////////////////////////////// +constexpr unsigned Reader::nest_limit; + static std::string codePointToUTF8 (unsigned int cp) @@ -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 ); @@ -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: @@ -427,7 +430,7 @@ Reader::readString () bool -Reader::readObject ( Token& tokenStart ) +Reader::readObject(Token& tokenStart, unsigned depth) { Token tokenName; std::string name; @@ -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 @@ -504,7 +507,7 @@ Reader::readObject ( Token& tokenStart ) bool -Reader::readArray ( Token& tokenStart ) +Reader::readArray(Token& tokenStart, unsigned depth) { currentValue () = Value ( arrayValue ); skipSpaces (); @@ -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 diff --git a/src/ripple/json/json_reader.h b/src/ripple/json/json_reader.h index 3367dc6c1a..6e08e82b3b 100644 --- a/src/ripple/json/json_reader.h +++ b/src/ripple/json/json_reader.h @@ -81,6 +81,8 @@ public: */ std::string getFormatedErrorMessages () const; + static constexpr unsigned nest_limit {1000}; + private: enum TokenType { @@ -129,9 +131,9 @@ private: 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 ); diff --git a/src/test/json/json_value_test.cpp b/src/test/json/json_value_test.cpp index cbe43b67ac..c5e70f15a7 100644 --- a/src/test/json/json_value_test.cpp +++ b/src/test/json/json_value_test.cpp @@ -24,6 +24,8 @@ #include #include +#include + namespace ripple { 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 () { test_bool (); @@ -258,6 +306,7 @@ struct json_value_test : beast::unit_test::suite test_move (); test_comparisons (); test_compact (); + test_nest_limits (); } };