From 925e8b7239b1466d1c08a923caaf155eac5dc69c Mon Sep 17 00:00:00 2001 From: Jonathan Wight Date: Tue, 9 Jul 2013 11:51:37 -0700 Subject: [PATCH] Fixes IOS-4383 - null value in CFDictionarySetValue Due to testing for _nullObject parameter it is possible for the code to try and pass a null value to CFDictionarySetValue - this patch fixes that and cleans up the handling of _nullObject. JIRA: IOS-4383 --- Source/CJSONDeserializer.m | 94 +++++++++++-------- .../UnitTests/CJSONDeserializer_UnitTests.m | 4 +- 2 files changed, 59 insertions(+), 39 deletions(-) diff --git a/Source/CJSONDeserializer.m b/Source/CJSONDeserializer.m index e296a5a..13e3dcc 100644 --- a/Source/CJSONDeserializer.m +++ b/Source/CJSONDeserializer.m @@ -97,7 +97,13 @@ - (id)deserialize:(NSData *)inData error:(NSError **)outError } } } - + else + { + if (theObject == [NSNull null]) + { + theObject = _nullObject; + } + } } return (theObject); @@ -138,7 +144,7 @@ - (BOOL)_setData:(NSData *)inData error:(NSError **)outError; { if (outError) { - *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONDeserializerErrorCode_NothingToScan userInfo:NULL]; + *outError = [self _error:kJSONDeserializerErrorCode_NothingToScan underlyingError:NULL description:@"Have no data to scan."]; } return(NO); } @@ -217,7 +223,7 @@ - (BOOL)_setData:(NSData *)inData error:(NSError **)outError; - (BOOL)_scanJSONObject:(id *)outObject error:(NSError **)outError { - BOOL theResult = YES; + BOOL theResult; _current = _SkipWhiteSpace(_current, _end); @@ -236,26 +242,28 @@ - (BOOL)_scanJSONObject:(id *)outObject error:(NSError **)outError switch (C) { case 't': - if (_ScanUTF8String(self, "true", 4)) - { - theObject = (__bridge id) kCFBooleanTrue; - } + { + theResult = _ScanUTF8String(self, "true", 4); + theObject = (__bridge id) kCFBooleanTrue; break; + } case 'f': - if (_ScanUTF8String(self, "false", 5)) - { - theObject = (__bridge id) kCFBooleanFalse; - } + { + theResult = _ScanUTF8String(self, "false", 5); + theObject = (__bridge id) kCFBooleanFalse; + } break; case 'n': - if (_ScanUTF8String(self, "null", 4)) - { - theObject = _nullObject; - } + { + theResult = _ScanUTF8String(self, "null", 4); + theObject = _nullObject ?: [NSNull null]; + } break; case '\"': case '\'': + { theResult = [self _scanJSONStringConstant:&theObject key:NO error:outError]; + } break; case '0': case '1': @@ -268,24 +276,28 @@ - (BOOL)_scanJSONObject:(id *)outObject error:(NSError **)outError case '8': case '9': case '-': + { theResult = [self _scanJSONNumberConstant:&theObject error:outError]; + } break; case '{': + { theResult = [self _scanJSONDictionary:&theObject error:outError]; + } break; case '[': + { theResult = [self _scanJSONArray:&theObject error:outError]; + } break; default: - theResult = NO; + { if (outError) { - NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - @"Could not scan object. Character not a valid JSON character.", NSLocalizedDescriptionKey, - NULL]; - [theUserInfo addEntriesFromDictionary:self._userInfoForScanLocation]; - *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONDeserializerErrorCode_CouldNotScanObject userInfo:theUserInfo]; + *outError = [self _error:kJSONDeserializerErrorCode_CouldNotScanObject description:@"Could not scan object. Character not a valid JSON character."]; } + return(NO); + } break; } @@ -294,7 +306,7 @@ - (BOOL)_scanJSONObject:(id *)outObject error:(NSError **)outError *outObject = theObject; } - return (theResult); + return(theResult); } - (BOOL)_scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)outError @@ -358,19 +370,22 @@ - (BOOL)_scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)out return (NO); } - if (theValue == NULL && _nullObject == NULL) + if (_nullObject == NULL && theValue == [NSNull null]) { - // If the value is a null and nullObject is also null then we're skipping this key/value pair. + continue; } - else + + if (theKey == NULL) { - if (theKey == NULL) - { - *outError = [self _error:kJSONDeserializerErrorCode_DictionaryKeyScanFailed description:@"Could not scan dictionary. Failed to scan a key."]; - return(NO); - } - CFDictionarySetValue((__bridge CFMutableDictionaryRef) theDictionary, (__bridge void *) theKey, (__bridge void *) theValue); + *outError = [self _error:kJSONDeserializerErrorCode_DictionaryKeyScanFailed description:@"Could not scan dictionary. Failed to scan a key."]; + return(NO); + } + if (theValue == NULL) + { + *outError = [self _error:kJSONDeserializerErrorCode_DictionaryValueScanFailed description:@"Could not scan dictionary. Failed to scan a value."]; + return(NO); } + CFDictionarySetValue((__bridge CFMutableDictionaryRef)theDictionary, (__bridge void *)theKey, (__bridge void *)theValue); _current = _SkipWhiteSpace(_current, _end); @@ -450,11 +465,7 @@ - (BOOL)_scanJSONArray:(NSArray **)outArray error:(NSError **)outError _current = _start + theScanLocation; if (outError) { - NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - @"Could not scan array. Could not scan a value.", NSLocalizedDescriptionKey, - NULL]; - [theUserInfo addEntriesFromDictionary:self._userInfoForScanLocation]; - *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONDeserializerErrorCode_ArrayValueScanFailed userInfo:theUserInfo]; + *outError = [self _error:kJSONDeserializerErrorCode_ArrayValueScanFailed underlyingError:NULL description:@"Could not scan array. Could not scan a value."]; } return (NO); } @@ -798,16 +809,25 @@ - (NSDictionary *)_userInfoForScanLocation return (theUserInfo); } -- (NSError *)_error:(NSInteger)inCode description:(NSString *)inDescription +- (NSError *)_error:(NSInteger)inCode underlyingError:(NSError *)inUnderlyingError description:(NSString *)inDescription { NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: inDescription, NSLocalizedDescriptionKey, NULL]; [theUserInfo addEntriesFromDictionary:self._userInfoForScanLocation]; + if (inUnderlyingError) + { + theUserInfo[NSUnderlyingErrorKey] = inUnderlyingError; + } NSError *theError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:inCode userInfo:theUserInfo]; return (theError); } +- (NSError *)_error:(NSInteger)inCode description:(NSString *)inDescription + { + return ([self _error:inCode underlyingError:NULL description:inDescription]); + } + #pragma mark - inline static BOOL _PtrRangeContainsCharacter(PtrRange inPtrRange, char C) diff --git a/Support/UnitTests/CJSONDeserializer_UnitTests.m b/Support/UnitTests/CJSONDeserializer_UnitTests.m index cc4f65c..1550993 100644 --- a/Support/UnitTests/CJSONDeserializer_UnitTests.m +++ b/Support/UnitTests/CJSONDeserializer_UnitTests.m @@ -822,13 +822,13 @@ -(void)testCheckForErrorWithNilJSONAndIgnoringError_Deprecated STAssertNil(dictionary, @"Dictionary will be nil when there is an error deserializing", nil); } --(void)testSkipNullValueInArray +-(void)testNullValueInArray { CJSONDeserializer *theDeserializer = [CJSONDeserializer deserializer]; theDeserializer.nullObject = NULL; NSData *theData = [@"[null]" dataUsingEncoding:NSUTF8StringEncoding]; NSArray *theArray = [theDeserializer deserialize:theData error:nil]; - STAssertEqualObjects(theArray, [NSArray array], @"Skipping null did not produce empty array"); + STAssertEqualObjects(theArray, @[ [NSNull null] ], @"Array mismatch"); } -(void)testAlternativeNullValueInArray