Index: src/OFString+JSONValue.h ================================================================== --- src/OFString+JSONValue.h +++ src/OFString+JSONValue.h @@ -41,6 +41,28 @@ * instead check the returned object using @ref isKindOfClass:. * * @return An object */ - (id)JSONValue; + +/*! + * @brief Creates an object from the JSON value of the string. + * + * @note This also allows parsing JSON5, an extension of JSON. See + * http://json5.org/ for more details. + * + * @warning Although not specified by the JSON specification, this can also + * return primitives like strings and numbers. The rationale behind + * this is that most JSON parsers allow JSON data just consisting of a + * single primitive, leading to realworld JSON files sometimes only + * consisting of a single primitive. Therefore, you should not make any + * assumptions about the object returned by this method if you don't + * want your program to terminate due to a message not understood, but + * instead check the returned object using @ref isKindOfClass:. + * + * @brief depthLimit The maximum depth the parser should accept (defaults to 32 + * if not specified, 0 means no limit (insecure!)) + * + * @return An object + */ +- (id)JSONValueWithDepthLimit: (size_t)depthLimit; @end Index: src/OFString+JSONValue.m ================================================================== --- src/OFString+JSONValue.m +++ src/OFString+JSONValue.m @@ -32,11 +32,11 @@ #import "macros.h" int _OFString_JSONValue_reference; static id nextObject(const char *restrict *, const char*, - size_t *restrict line); + size_t *restrict line, size_t depth, size_t depthLimit); static void skipWhitespaces(const char *restrict *pointer, const char *stop, size_t *restrict line) { @@ -390,16 +390,19 @@ return nil; } static inline OFMutableArray* parseArray(const char *restrict *pointer, const char *stop, - size_t *restrict line) + size_t *restrict line, size_t depth, size_t depthLimit) { OFMutableArray *array = [OFMutableArray array]; if (++(*pointer) >= stop) return nil; + + if (++depth > depthLimit) + return nil; while (**pointer != ']') { id object; skipWhitespacesAndComments(pointer, stop, line); @@ -417,11 +420,12 @@ return nil; break; } - if ((object = nextObject(pointer, stop, line)) == nil) + object = nextObject(pointer, stop, line, depth, depthLimit); + if (object == nil) return nil; [array addObject: object]; skipWhitespacesAndComments(pointer, stop, line); @@ -443,16 +447,19 @@ return array; } static inline OFMutableDictionary* parseDictionary(const char *restrict *pointer, const char *stop, - size_t *restrict line) + size_t *restrict line, size_t depth, size_t depthLimit) { OFMutableDictionary *dictionary = [OFMutableDictionary dictionary]; if (++(*pointer) >= stop) return nil; + + if (++depth > depthLimit) + return nil; while (**pointer != '}') { id key, object; skipWhitespacesAndComments(pointer, stop, line); @@ -479,11 +486,12 @@ if ((**pointer >= 'a' && **pointer <= 'z') || (**pointer >= 'A' && **pointer <= 'Z') || **pointer == '_' || **pointer == '$' || **pointer == '\\') key = parseIdentifier(pointer, stop); else - key = nextObject(pointer, stop, line); + key = nextObject(pointer, stop, line, + depth, depthLimit); if (key == nil) return nil; skipWhitespacesAndComments(pointer, stop, line); @@ -490,11 +498,12 @@ if (*pointer + 1 >= stop || **pointer != ':') return nil; (*pointer)++; - if ((object = nextObject(pointer, stop, line)) == nil) + object = nextObject(pointer, stop, line, depth, depthLimit); + if (object == nil) return nil; [dictionary setObject: object forKey: key]; @@ -563,11 +572,11 @@ return number; } static id nextObject(const char *restrict *pointer, const char *stop, - size_t *restrict line) + size_t *restrict line, size_t depth, size_t depthLimit) { skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; @@ -575,13 +584,13 @@ switch (**pointer) { case '"': case '\'': return parseString(pointer, stop, line); case '[': - return parseArray(pointer, stop, line); + return parseArray(pointer, stop, line, depth, depthLimit); case '{': - return parseDictionary(pointer, stop, line); + return parseDictionary(pointer, stop, line, depth, depthLimit); case 't': if (*pointer + 3 >= stop) return nil; if (memcmp(*pointer, "true", 4)) @@ -629,20 +638,25 @@ } @implementation OFString (JSONValue) - (id)JSONValue { + return [self JSONValueWithDepthLimit: 32]; +} + +- (id)JSONValueWithDepthLimit: (size_t)depthLimit +{ const char *pointer = [self UTF8String]; const char *stop = pointer + [self UTF8StringLength]; id object; size_t line = 1; - object = nextObject(&pointer, stop, &line); + object = nextObject(&pointer, stop, &line, 0, depthLimit); skipWhitespacesAndComments(&pointer, stop, &line); if (pointer < stop || object == nil) @throw [OFInvalidJSONException exceptionWithClass: [self class] line: line]; return object; } @end