diff --git a/CHANGELOG.md b/CHANGELOG.md index 536c1adfe..a0d28f8a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ Changelog ========= +## 5.X.X (TBD) + +### Bug Fixes + +* Handle potential nil content value in RegisterErrorData class [#289](https://github.com/bugsnag/bugsnag-cocoa/pull/289) + ## 5.15.6 (30 May 2018) ### Bug Fixes diff --git a/Source/BugsnagCrashReport.m b/Source/BugsnagCrashReport.m index 8d99b7aa1..273401e4a 100644 --- a/Source/BugsnagCrashReport.m +++ b/Source/BugsnagCrashReport.m @@ -630,9 +630,16 @@ + (instancetype)errorDataFromThreads:(NSArray *)threads { } NSString *contentValue = data[@"value"]; +#pragma clang diagnostic push +#pragma ide diagnostic ignored "OCDFAInspection" + if (contentValue == nil || ![contentValue isKindOfClass:[NSString class]]) { + continue; + } +#pragma clang diagnostic pop + if ([self isReservedWord:contentValue]) { reservedWord = contentValue; - } else if (!([[contentValue componentsSeparatedByString:@"/"] count] > 2)) { + } else if ([[contentValue componentsSeparatedByString:@"/"] count] <= 2) { // must be a string that isn't a reserved word and isn't a filepath [interestingValues addObject:contentValue]; } diff --git a/iOS/Bugsnag.xcodeproj/project.pbxproj b/iOS/Bugsnag.xcodeproj/project.pbxproj index b1dfa9ef6..52ff718e0 100644 --- a/iOS/Bugsnag.xcodeproj/project.pbxproj +++ b/iOS/Bugsnag.xcodeproj/project.pbxproj @@ -312,6 +312,7 @@ F4295C14DCDDF541188CDE66 /* BugsnagSessionFileStore.h in Headers */ = {isa = PBXBuildFile; fileRef = F42955025DBE1DCEFD928CAA /* BugsnagSessionFileStore.h */; }; F4295C52A30DC98515F2FF02 /* BugsnagSessionTrackingApiClient.m in Sources */ = {isa = PBXBuildFile; fileRef = F4295FBD23F478FC6216A006 /* BugsnagSessionTrackingApiClient.m */; }; F4295DB3B395327B82A47A78 /* BugsnagSessionFileStore.m in Sources */ = {isa = PBXBuildFile; fileRef = F42954ACC6FFDDE3C8471495 /* BugsnagSessionFileStore.m */; }; + F4295F017754324FD52CCE46 /* RegisterErrorDataTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F42954B7D892334E7551F0F3 /* RegisterErrorDataTest.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -589,6 +590,7 @@ F429517A5571A61A897E963D /* BugsnagSessionTrackingApiClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BugsnagSessionTrackingApiClient.h; path = ../Source/BugsnagSessionTrackingApiClient.h; sourceTree = SOURCE_ROOT; }; F42953E7E61199381E0405CC /* BugsnagFileStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BugsnagFileStore.h; path = ../Source/BugsnagFileStore.h; sourceTree = SOURCE_ROOT; }; F42954ACC6FFDDE3C8471495 /* BugsnagSessionFileStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagSessionFileStore.m; path = ../Source/BugsnagSessionFileStore.m; sourceTree = SOURCE_ROOT; }; + F42954B7D892334E7551F0F3 /* RegisterErrorDataTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegisterErrorDataTest.m; sourceTree = ""; }; F42955025DBE1DCEFD928CAA /* BugsnagSessionFileStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BugsnagSessionFileStore.h; path = ../Source/BugsnagSessionFileStore.h; sourceTree = SOURCE_ROOT; }; F429554A50F3ABE60537F70E /* BugsnagKSCrashSysInfoParserTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagKSCrashSysInfoParserTest.m; sourceTree = ""; }; F42958B2E67C338E3086EAC2 /* BugsnagFileStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagFileStore.m; path = ../Source/BugsnagFileStore.m; sourceTree = SOURCE_ROOT; }; @@ -726,6 +728,7 @@ 8A2C8F951C6BC08600846019 /* report.json */, 8A2C8F291C6BBD2300846019 /* TestsInfo.plist */, F429554A50F3ABE60537F70E /* BugsnagKSCrashSysInfoParserTest.m */, + F42954B7D892334E7551F0F3 /* RegisterErrorDataTest.m */, ); name = Tests; path = BugsnagTests; @@ -1237,6 +1240,7 @@ E78C1EF31FCC615400B976D3 /* BugsnagSessionTrackingPayloadTest.m in Sources */, E78C1EF11FCC2F1700B976D3 /* BugsnagSessionTrackerTest.m in Sources */, F4295995C3259BF7D9730BC4 /* BugsnagKSCrashSysInfoParserTest.m in Sources */, + F4295F017754324FD52CCE46 /* RegisterErrorDataTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/iOS/BugsnagTests/RegisterErrorDataTest.m b/iOS/BugsnagTests/RegisterErrorDataTest.m new file mode 100644 index 000000000..c302835a9 --- /dev/null +++ b/iOS/BugsnagTests/RegisterErrorDataTest.m @@ -0,0 +1,262 @@ +// +// Created by Jamie Lynch on 11/06/2018. +// Copyright (c) 2018 Bugsnag. All rights reserved. +// + + +#import +#import + +@interface RegisterErrorData ++ (instancetype)errorDataFromThreads:(NSArray *)threads; +@property (nonatomic, strong) NSString *errorClass; +@property (nonatomic, strong) NSString *errorMessage; +@end + +@interface RegisterErrorDataTest : XCTestCase +@end + +@implementation RegisterErrorDataTest + + +- (void)testNilAddresses { + XCTAssertNil([RegisterErrorData errorDataFromThreads:nil]); +} + +- (void)testEmptyAddresses { + XCTAssertNil([RegisterErrorData errorDataFromThreads:@[]]); +} + +- (void)testEmptyCrashedThreadDict { + NSDictionary *thread = @{ + @"crashed": @YES + }; + XCTAssertNil([RegisterErrorData errorDataFromThreads:@[thread]]); +} + +- (void)testEmptyNotableAddresses { + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{} + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertNil(data); +} + +- (void)testEmptyContentValue { + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{ + @"hello_world": @{} + } + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertNil(data); +} + +- (void)testNilValueImplicit { + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{ + @"hello_world": @{ + @"type": @"string" + } + } + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertNil(data); +} + +- (void)testNilValueExplicit { + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{ + @"hello_world": @{ + @"type": @"string", + @"value": [NSNull null] + } + } + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertNil(data); +} + +- (void)testHasTypeAndValue{ + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{ + @"hello_world": @{ + @"type": @"string", + @"value": @"Hello, World!" + } + } + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertNil(data); +} + +- (void)testFatalError { + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{ + @"hello_world": @{ + @"type": @"string", + @"value": @"fatal error" + } + } + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertNotNil(data); + XCTAssertEqualObjects(@"fatal error", data.errorClass); + XCTAssertEqualObjects(@"", data.errorMessage); +} + +- (void)testAssertionFailed { + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{ + @"hello_world": @{ + @"type": @"string", + @"value": @"assertion failed" + } + } + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertNotNil(data); + XCTAssertEqualObjects(@"assertion failed", data.errorClass); + XCTAssertEqualObjects(@"", data.errorMessage); +} + +- (void)testPreconditionFailed { + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{ + @"hello_world": @{ + @"type": @"string", + @"value": @"precondition failed" + } + } + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertNotNil(data); + XCTAssertEqualObjects(@"precondition failed", data.errorClass); + XCTAssertEqualObjects(@"", data.errorMessage); +} + +- (void)testSingleMessageValue { + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{ + @"hello_world": @{ + @"type": @"string", + @"value": @"fatal error" + }, + @"message": @{ + @"type": @"string", + @"value": @"Single Message" + } + } + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertEqualObjects(@"Single Message", data.errorMessage); +} + +- (void)testMultiMessageValue { + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{ + @"hello_world": @{ + @"type": @"string", + @"value": @"fatal error" + }, + @"message": @{ + @"type": @"string", + @"value": @"A is for aardvark" + }, + @"message2": @{ + @"type": @"string", + @"value": @"Z is for zebra" + }, + @"message3": @{ + @"type": @"string", + @"value": @"C is for crayfish" + } + } + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertEqualObjects(@"A is for aardvark | C is for crayfish | Z is for zebra", data.errorMessage); +} + +- (void)testStackExcluded { + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{ + @"hello_world": @{ + @"type": @"string", + @"value": @"fatal error" + }, + @"message": @{ + @"type": @"stack", + @"value": @"0xf0924501" + } + } + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertEqualObjects(@"", data.errorMessage); +} + +- (void)testOtherTypesExcluded { + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{ + @"hello_world": @{ + @"type": @"string", + @"value": @"fatal error" + }, + @"message": @{ + @"type": @"someOtherType", + @"value": @"do not serialise" + } + } + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertEqualObjects(@"", data.errorMessage); +} + +- (void)testFilepathExcluded { + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{ + @"hello_world": @{ + @"type": @"string", + @"value": @"fatal error" + }, + @"message": @{ + @"type": @"string", + @"value": @"/usr/share/locale" + } + } + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertEqualObjects(@"", data.errorMessage); +} + +- (void)testForwardSlashIncluded { + NSDictionary *thread = @{ + @"crashed": @YES, + @"notable_addresses": @{ + @"hello_world": @{ + @"type": @"string", + @"value": @"fatal error" + }, + @"message": @{ + @"type": @"string", + @"value": @"usr/share" + } + } + }; + RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:@[thread]]; + XCTAssertEqualObjects(@"usr/share", data.errorMessage); +} + +@end