mirror of
				https://github.com/eledio-devices/thirdparty-ArduinoJson.git
				synced 2025-11-01 08:48:30 +01:00 
			
		
		
		
	Added a nesting limit to the parser to prevent stack overflow that could be a security issue
This commit is contained in:
		| @@ -13,7 +13,8 @@ namespace Internals { | |||||||
|  |  | ||||||
| class JsonParser { | class JsonParser { | ||||||
|  public: |  public: | ||||||
|   JsonParser(JsonBuffer *buffer, char *json) : _buffer(buffer), _ptr(json) {} |   JsonParser(JsonBuffer *buffer, char *json, uint8_t nestingLimit) | ||||||
|  |       : _buffer(buffer), _ptr(json), _nestingLimit(nestingLimit) {} | ||||||
|  |  | ||||||
|   JsonArray &parseArray(); |   JsonArray &parseArray(); | ||||||
|   JsonObject &parseObject(); |   JsonObject &parseObject(); | ||||||
| @@ -33,6 +34,7 @@ class JsonParser { | |||||||
|  |  | ||||||
|   JsonBuffer *_buffer; |   JsonBuffer *_buffer; | ||||||
|   char *_ptr; |   char *_ptr; | ||||||
|  |   uint8_t _nestingLimit; | ||||||
| }; | }; | ||||||
| } | } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,9 +19,11 @@ class JsonBuffer { | |||||||
|   JsonArray &createArray(); |   JsonArray &createArray(); | ||||||
|   JsonObject &createObject(); |   JsonObject &createObject(); | ||||||
|  |  | ||||||
|   JsonArray &parseArray(char *json); |   JsonArray &parseArray(char *json, uint8_t nestingLimit = DEFAULT_LIMIT); | ||||||
|   JsonObject &parseObject(char *json); |   JsonObject &parseObject(char *json, uint8_t nestingLimit = DEFAULT_LIMIT); | ||||||
|  |  | ||||||
|   virtual void *alloc(size_t size) = 0; |   virtual void *alloc(size_t size) = 0; | ||||||
|  |  | ||||||
|  |   static const uint8_t DEFAULT_LIMIT = 10; | ||||||
| }; | }; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -39,6 +39,9 @@ bool JsonParser::skip(const char *wordToSkip) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void JsonParser::parseAnythingTo(JsonVariant &destination) { | void JsonParser::parseAnythingTo(JsonVariant &destination) { | ||||||
|  |   if (_nestingLimit == 0) return; | ||||||
|  |   _nestingLimit--; | ||||||
|  |  | ||||||
|   skipSpaces(); |   skipSpaces(); | ||||||
|  |  | ||||||
|   switch (*_ptr) { |   switch (*_ptr) { | ||||||
| @@ -79,6 +82,8 @@ void JsonParser::parseAnythingTo(JsonVariant &destination) { | |||||||
|       destination = parseString(); |       destination = parseString(); | ||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   _nestingLimit++; | ||||||
| } | } | ||||||
|  |  | ||||||
| JsonArray &JsonParser::parseArray() { | JsonArray &JsonParser::parseArray() { | ||||||
|   | |||||||
| @@ -26,12 +26,12 @@ JsonObject &JsonBuffer::createObject() { | |||||||
|   return JsonObject::invalid(); |   return JsonObject::invalid(); | ||||||
| } | } | ||||||
|  |  | ||||||
| JsonArray &JsonBuffer::parseArray(char *json) { | JsonArray &JsonBuffer::parseArray(char *json, uint8_t nestingLimit) { | ||||||
|   JsonParser parser(this, json); |   JsonParser parser(this, json, nestingLimit); | ||||||
|   return parser.parseArray(); |   return parser.parseArray(); | ||||||
| } | } | ||||||
|  |  | ||||||
| JsonObject &JsonBuffer::parseObject(char *json) { | JsonObject &JsonBuffer::parseObject(char *json, uint8_t nestingLimit) { | ||||||
|   JsonParser parser(this, json); |   JsonParser parser(this, json, nestingLimit); | ||||||
|   return parser.parseObject(); |   return parser.parseObject(); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										88
									
								
								test/JsonParser_NestingLimit_Tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								test/JsonParser_NestingLimit_Tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | |||||||
|  | // Copyright Benoit Blanchon 2014 | ||||||
|  | // MIT License | ||||||
|  | // | ||||||
|  | // Arduino JSON library | ||||||
|  | // https://github.com/bblanchon/ArduinoJson | ||||||
|  |  | ||||||
|  | #include <gtest/gtest.h> | ||||||
|  | #include <ArduinoJson/JsonArray.hpp> | ||||||
|  | #include <ArduinoJson/JsonObject.hpp> | ||||||
|  | #include <ArduinoJson/StaticJsonBuffer.hpp> | ||||||
|  |  | ||||||
|  | using namespace ArduinoJson; | ||||||
|  |  | ||||||
|  | class JsonParser_NestingLimit_Tests : public testing::Test { | ||||||
|  |  protected: | ||||||
|  |   void whenNestingLimitIs(uint8_t nestingLimit) { | ||||||
|  |     _nestingLimit = nestingLimit; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void parseArrayMustFail(const char *json) { | ||||||
|  |     ASSERT_FALSE(tryParseArray(json)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void parseArrayMustSucceed(const char *json) { | ||||||
|  |     ASSERT_TRUE(tryParseArray(json)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void parseObjectMustFail(const char *json) { | ||||||
|  |     ASSERT_FALSE(tryParseObject(json)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void parseObjectMustSucceed(const char *json) { | ||||||
|  |     ASSERT_TRUE(tryParseObject(json)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   bool tryParseArray(const char *json) { | ||||||
|  |     StaticJsonBuffer<256> buffer; | ||||||
|  |     char s[256]; | ||||||
|  |     strcpy(s, json); | ||||||
|  |     return buffer.parseArray(s, _nestingLimit).success(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   bool tryParseObject(const char *json) { | ||||||
|  |     StaticJsonBuffer<256> buffer; | ||||||
|  |     char s[256]; | ||||||
|  |     strcpy(s, json); | ||||||
|  |     return buffer.parseObject(s, _nestingLimit).success(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   uint8_t _nestingLimit; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | TEST_F(JsonParser_NestingLimit_Tests, ParseArrayWithNestingLimit0) { | ||||||
|  |   whenNestingLimitIs(0); | ||||||
|  |   parseArrayMustSucceed("[]"); | ||||||
|  |   parseArrayMustFail("[[]]"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_F(JsonParser_NestingLimit_Tests, ParseArrayWithNestingLimit1) { | ||||||
|  |   whenNestingLimitIs(1); | ||||||
|  |   parseArrayMustSucceed("[[]]"); | ||||||
|  |   parseArrayMustFail("[[[]]]"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_F(JsonParser_NestingLimit_Tests, ParseArrayWithNestingLimit2) { | ||||||
|  |   whenNestingLimitIs(2); | ||||||
|  |   parseArrayMustSucceed("[[[]]]"); | ||||||
|  |   parseArrayMustFail("[[[[]]]]"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_F(JsonParser_NestingLimit_Tests, ParseObjectWithNestingLimit0) { | ||||||
|  |   whenNestingLimitIs(0); | ||||||
|  |   parseObjectMustSucceed("{}"); | ||||||
|  |   parseObjectMustFail("{\"key\":{}}"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_F(JsonParser_NestingLimit_Tests, ParseObjectWithNestingLimit1) { | ||||||
|  |   whenNestingLimitIs(1); | ||||||
|  |   parseObjectMustSucceed("{\"key\":{}}"); | ||||||
|  |   parseObjectMustFail("{\"key\":{\"key\":{}}}"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_F(JsonParser_NestingLimit_Tests, ParseObjectWithNestingLimit2) { | ||||||
|  |   whenNestingLimitIs(2); | ||||||
|  |   parseObjectMustSucceed("{\"key\":{\"key\":{}}}"); | ||||||
|  |   parseObjectMustFail("{\"key\":{\"key\":{\"key\":{}}}}"); | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user