ObjFW  Check-in [d60c3ae1ec]

Overview
Comment:JSON: Add configurable depth limit.

The default is 32.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: d60c3ae1ec14f5f9a548eadde30fff42db1edc906be2248b013f3577d59ab7a9
User & Date: js on 2012-12-03 01:16:39
Other Links: manifest | tags
Context
2012-12-03
01:17
OFXMLParser: Add configurable depth limit. check-in: ed4e64fd32 user: js tags: trunk
01:16
JSON: Add configurable depth limit. check-in: d60c3ae1ec user: js tags: trunk
2012-12-02
16:43
OFMethod: Add one more NULL check. check-in: ab13f1d324 user: js tags: trunk
Changes

Modified src/OFString+JSONValue.h from [5e2364c7d7] to [edcb750a8d].

39
40
41
42
43
44
45






















46
 *          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:.
 *
 * @return An object
 */
- (id)JSONValue;






















@end







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 *          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:.
 *
 * @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

Modified src/OFString+JSONValue.m from [12270ead53] to [9c5a92bb7c].

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#import "OFInvalidJSONException.h"

#import "macros.h"

int _OFString_JSONValue_reference;

static id nextObject(const char *restrict *, const char*,
    size_t *restrict line);

static void
skipWhitespaces(const char *restrict *pointer, const char *stop,
    size_t *restrict line)
{
	while (*pointer < stop && (**pointer == ' ' || **pointer == '\t' ||
	    **pointer == '\r' || **pointer == '\n')) {







|







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#import "OFInvalidJSONException.h"

#import "macros.h"

int _OFString_JSONValue_reference;

static id nextObject(const char *restrict *, const char*,
    size_t *restrict line, size_t depth, size_t depthLimit);

static void
skipWhitespaces(const char *restrict *pointer, const char *stop,
    size_t *restrict line)
{
	while (*pointer < stop && (**pointer == ' ' || **pointer == '\t' ||
	    **pointer == '\r' || **pointer == '\n')) {
388
389
390
391
392
393
394
395
396
397
398
399
400



401
402
403
404
405
406
407
	 * reach stop.
	 */
	return nil;
}

static inline OFMutableArray*
parseArray(const char *restrict *pointer, const char *stop,
    size_t *restrict line)
{
	OFMutableArray *array = [OFMutableArray array];

	if (++(*pointer) >= stop)
		return nil;




	while (**pointer != ']') {
		id object;

		skipWhitespacesAndComments(pointer, stop, line);
		if (*pointer >= stop)
			return nil;







|





>
>
>







388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
	 * reach stop.
	 */
	return nil;
}

static inline OFMutableArray*
parseArray(const char *restrict *pointer, const char *stop,
    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);
		if (*pointer >= stop)
			return nil;
415
416
417
418
419
420
421
422

423
424
425
426
427
428
429

			if (*pointer >= stop || **pointer != ']')
				return nil;

			break;
		}

		if ((object = nextObject(pointer, stop, line)) == nil)

			return nil;

		[array addObject: object];

		skipWhitespacesAndComments(pointer, stop, line);
		if (*pointer >= stop)
			return nil;







|
>







418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433

			if (*pointer >= stop || **pointer != ']')
				return nil;

			break;
		}

		object = nextObject(pointer, stop, line, depth, depthLimit);
		if (object == nil)
			return nil;

		[array addObject: object];

		skipWhitespacesAndComments(pointer, stop, line);
		if (*pointer >= stop)
			return nil;
441
442
443
444
445
446
447
448
449
450
451
452
453



454
455
456
457
458
459
460
	(*pointer)++;

	return array;
}

static inline OFMutableDictionary*
parseDictionary(const char *restrict *pointer, const char *stop,
    size_t *restrict line)
{
	OFMutableDictionary *dictionary = [OFMutableDictionary dictionary];

	if (++(*pointer) >= stop)
		return nil;




	while (**pointer != '}') {
		id key, object;

		skipWhitespacesAndComments(pointer, stop, line);
		if (*pointer >= stop)
			return nil;







|





>
>
>







445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
	(*pointer)++;

	return array;
}

static inline OFMutableDictionary*
parseDictionary(const char *restrict *pointer, const char *stop,
    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);
		if (*pointer >= stop)
			return nil;
477
478
479
480
481
482
483
484

485
486
487
488
489
490
491
492
493
494
495

496
497
498
499
500
501
502
			return nil;

		if ((**pointer >= 'a' && **pointer <= 'z') ||
		    (**pointer >= 'A' && **pointer <= 'Z') ||
		    **pointer == '_' || **pointer == '$' || **pointer == '\\')
			key = parseIdentifier(pointer, stop);
		else
			key = nextObject(pointer, stop, line);


		if (key == nil)
			return nil;

		skipWhitespacesAndComments(pointer, stop, line);
		if (*pointer + 1 >= stop || **pointer != ':')
			return nil;

		(*pointer)++;

		if ((object = nextObject(pointer, stop, line)) == nil)

			return nil;

		[dictionary setObject: object
			       forKey: key];

		skipWhitespacesAndComments(pointer, stop, line);
		if (*pointer >= stop)







|
>










|
>







484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
			return nil;

		if ((**pointer >= 'a' && **pointer <= 'z') ||
		    (**pointer >= 'A' && **pointer <= 'Z') ||
		    **pointer == '_' || **pointer == '$' || **pointer == '\\')
			key = parseIdentifier(pointer, stop);
		else
			key = nextObject(pointer, stop, line,
			    depth, depthLimit);

		if (key == nil)
			return nil;

		skipWhitespacesAndComments(pointer, stop, line);
		if (*pointer + 1 >= stop || **pointer != ':')
			return nil;

		(*pointer)++;

		object = nextObject(pointer, stop, line, depth, depthLimit);
		if (object == nil)
			return nil;

		[dictionary setObject: object
			       forKey: key];

		skipWhitespacesAndComments(pointer, stop, line);
		if (*pointer >= stop)
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
	}

	return number;
}

static id
nextObject(const char *restrict *pointer, const char *stop,
    size_t *restrict line)
{
	skipWhitespacesAndComments(pointer, stop, line);

	if (*pointer >= stop)
		return nil;

	switch (**pointer) {
	case '"':
	case '\'':
		return parseString(pointer, stop, line);
	case '[':
		return parseArray(pointer, stop, line);
	case '{':
		return parseDictionary(pointer, stop, line);
	case 't':
		if (*pointer + 3 >= stop)
			return nil;

		if (memcmp(*pointer, "true", 4))
			return nil;








|











|

|







570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
	}

	return number;
}

static id
nextObject(const char *restrict *pointer, const char *stop,
    size_t *restrict line, size_t depth, size_t depthLimit)
{
	skipWhitespacesAndComments(pointer, stop, line);

	if (*pointer >= stop)
		return nil;

	switch (**pointer) {
	case '"':
	case '\'':
		return parseString(pointer, stop, line);
	case '[':
		return parseArray(pointer, stop, line, depth, depthLimit);
	case '{':
		return parseDictionary(pointer, stop, line, depth, depthLimit);
	case 't':
		if (*pointer + 3 >= stop)
			return nil;

		if (memcmp(*pointer, "true", 4))
			return nil;

627
628
629
630
631
632
633





634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
		return nil;
	}
}

@implementation OFString (JSONValue)
- (id)JSONValue
{





	const char *pointer = [self UTF8String];
	const char *stop = pointer + [self UTF8StringLength];
	id object;
	size_t line = 1;

	object = nextObject(&pointer, stop, &line);
	skipWhitespacesAndComments(&pointer, stop, &line);

	if (pointer < stop || object == nil)
		@throw [OFInvalidJSONException exceptionWithClass: [self class]
							     line: line];

	return object;
}
@end







>
>
>
>
>





|









636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
		return nil;
	}
}

@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, 0, depthLimit);
	skipWhitespacesAndComments(&pointer, stop, &line);

	if (pointer < stop || object == nil)
		@throw [OFInvalidJSONException exceptionWithClass: [self class]
							     line: line];

	return object;
}
@end