Skip to content

Commit

Permalink
Redact breadcrumb metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
nickdowell committed Oct 8, 2021
1 parent d211650 commit 86e82eb
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 20 deletions.
1 change: 0 additions & 1 deletion Bugsnag/Helpers/BugsnagKeys.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ extern NSString *const BSGKeyPersistUser;
extern NSString *const BSGKeyProduction;
extern NSString *const BSGKeyReason;
extern NSString *const BSGKeyRedactedKeys;
extern NSString *const BSGKeyRedaction;
extern NSString *const BSGKeyReleaseStage;
extern NSString *const BSGKeySendThreads;
extern NSString *const BSGKeySession;
Expand Down
1 change: 0 additions & 1 deletion Bugsnag/Helpers/BugsnagKeys.m
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@
NSString *const BSGKeyProduction = @"production";
NSString *const BSGKeyReason = @"reason";
NSString *const BSGKeyRedactedKeys = @"redactedKeys";
NSString *const BSGKeyRedaction = @"[REDACTED]";
NSString *const BSGKeyReleaseStage = @"releaseStage";
NSString *const BSGKeySendThreads = @"sendThreads";
NSString *const BSGKeySession = @"session";
Expand Down
38 changes: 22 additions & 16 deletions Bugsnag/Payload/BugsnagEvent.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#import "BugsnagThread+Private.h"
#import "BugsnagUser+Private.h"

static NSString * const RedactedMetadataValue = @"[REDACTED]";

id BSGLoadConfigValue(NSDictionary *report, NSString *valueName) {
NSString *keypath = [NSString stringWithFormat:@"user.config.%@", valueName];
Expand Down Expand Up @@ -458,8 +459,17 @@ - (BOOL)shouldBeSent {
(self.enabledReleaseStages.count == 0);
}

- (NSArray *)serializeBreadcrumbs {
return [[self breadcrumbs] valueForKeyPath:NSStringFromSelector(@selector(objectValue))];
- (NSArray<NSDictionary *> *)serializeBreadcrumbsWithRedactedKeys:(NSSet *)redactedKeys {
return BSGArrayMap(self.breadcrumbs, ^NSDictionary * (BugsnagBreadcrumb *breadcrumb) {
NSMutableDictionary *dictionary = [[breadcrumb objectValue] mutableCopy];
NSDictionary *metadata = dictionary[BSGKeyMetadata];
NSMutableDictionary *redactedMetadata = [NSMutableDictionary dictionary];
for (NSString *key in metadata) {
redactedMetadata[key] = [self redactedMetadataValue:metadata[key] forKey:key redactedKeys:redactedKeys];
}
dictionary[BSGKeyMetadata] = redactedMetadata;
return dictionary;
});
}

- (void)attachCustomStacktrace:(NSArray *)frames withType:(NSString *)type {
Expand Down Expand Up @@ -540,15 +550,13 @@ - (NSDictionary *)toJsonWithRedactedKeys:(NSSet *)redactedKeys {
});

event[BSGKeyThreads] = [BugsnagThread serializeThreads:self.threads];

// Build Event
event[BSGKeySeverity] = BSGFormatSeverity(self.severity);
event[BSGKeyBreadcrumbs] = [self serializeBreadcrumbs];
event[BSGKeyBreadcrumbs] = [self serializeBreadcrumbsWithRedactedKeys:redactedKeys];

// add metadata
NSMutableDictionary *metadata = [[[self metadata] toDictionary] mutableCopy];
@try {
event[BSGKeyMetadata] = [self sanitiseMetadata:metadata redactedKeys:redactedKeys];
[self redactKeys:redactedKeys inMetadata:metadata];
event[BSGKeyMetadata] = metadata;
} @catch (NSException *exception) {
bsg_log_err(@"An exception was thrown while sanitising metadata: %@", exception);
}
Expand Down Expand Up @@ -599,7 +607,7 @@ - (NSDictionary *)toJsonWithRedactedKeys:(NSSet *)redactedKeys {
return event;
}

- (NSMutableDictionary *)sanitiseMetadata:(NSMutableDictionary *)metadata redactedKeys:(NSSet *)redactedKeys {
- (void)redactKeys:(NSSet *)redactedKeys inMetadata:(NSMutableDictionary *)metadata {
for (NSString *sectionKey in [metadata allKeys]) {
if ([metadata[sectionKey] isKindOfClass:[NSDictionary class]]) {
metadata[sectionKey] = [metadata[sectionKey] mutableCopy];
Expand All @@ -614,29 +622,27 @@ - (NSMutableDictionary *)sanitiseMetadata:(NSMutableDictionary *)metadata redact

if (section != nil) { // redact sensitive metadata values
for (NSString *objKey in [section allKeys]) {
section[objKey] = [self sanitiseMetadataValue:section[objKey] key:objKey redactedKeys:redactedKeys];
section[objKey] = [self redactedMetadataValue:section[objKey] forKey:objKey redactedKeys:redactedKeys];
}
}
}
return metadata;
}

- (id)sanitiseMetadataValue:(id)value key:(NSString *)key redactedKeys:(NSSet *)redactedKeys {
if ([self isRedactedKey:key redactedKeys:redactedKeys]) {
return BSGKeyRedaction;
- (id)redactedMetadataValue:(id)value forKey:(NSString *)key redactedKeys:(NSSet *)redactedKeys {
if ([self redactedKeys:redactedKeys matches:key]) {
return RedactedMetadataValue;
} else if ([value isKindOfClass:[NSDictionary class]]) {
NSMutableDictionary *nestedDict = [(NSDictionary *)value mutableCopy];

for (NSString *nestedKey in [nestedDict allKeys]) {
nestedDict[nestedKey] = [self sanitiseMetadataValue:nestedDict[nestedKey] key:nestedKey redactedKeys:redactedKeys];
nestedDict[nestedKey] = [self redactedMetadataValue:nestedDict[nestedKey] forKey:nestedKey redactedKeys:redactedKeys];
}
return nestedDict;
} else {
return value;
}
}

- (BOOL)isRedactedKey:(NSString *)key redactedKeys:(NSSet *)redactedKeys {
- (BOOL)redactedKeys:(NSSet *)redactedKeys matches:(NSString *)key {
for (id obj in redactedKeys) {
if ([obj isKindOfClass:[NSString class]]) {
if ([[key lowercaseString] isEqualToString:[obj lowercaseString]]) {
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Changelog
=========

## TBD

### Bug fixes

* Apply `redactedKeys` to breadcrumb metadata.
[#1204](https://github.com/bugsnag/bugsnag-cocoa/pull/1204)

## 6.14.0 (2021-10-06)

### Enhancements
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ test-fixtures: ## Build the end-to-end test fixture
@./features/scripts/export_ios_app.sh
@./features/scripts/export_mac_app.sh

e2e_macos:
./features/scripts/export_mac_app.sh
bundle exec maze-runner --app=macOSTestApp --farm=local --os=macOS --os-version=11 $(FEATURES)

#--------------------------------------------------------------------------
# Release
#
Expand Down
24 changes: 24 additions & 0 deletions Tests/BugsnagMetadataRedactionTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#import <XCTest/XCTest.h>

#import "BugsnagBreadcrumb+Private.h"
#import "BugsnagEvent+Private.h"

@interface BugsnagMetadataRedactionTest : XCTestCase
Expand Down Expand Up @@ -130,4 +131,27 @@ - (void)testCaseInsensitiveKeys {
XCTAssertEqualObjects(@"ba09", section[@"somekey"]);
}

- (void)testBreadcrumbMetadataRedaction {
BugsnagBreadcrumb *breadcrumb = [[BugsnagBreadcrumb alloc] init];
breadcrumb.message = @"message cannot be empty";
breadcrumb.metadata = @{
@"foo" : @"not redacted",
@"password" : @"secret",
@"x" : @{
@"bar" : @"not redacted",
@"password" : @"123456"
}
};

BugsnagEvent *event = [self generateEventWithMetadata:@{}];
event.breadcrumbs = @[breadcrumb];

NSDictionary *eventPayload = [event toJsonWithRedactedKeys:[NSSet setWithArray:@[@"password"]]];
NSDictionary *metaData = eventPayload[@"breadcrumbs"][0][@"metaData"];
XCTAssertEqualObjects(metaData[@"foo"], @"not redacted");
XCTAssertEqualObjects(metaData[@"password"], @"[REDACTED]");
XCTAssertEqualObjects(metaData[@"x"][@"bar"], @"not redacted");
XCTAssertEqualObjects(metaData[@"x"][@"password"], @"[REDACTED]");
}

@end
1 change: 1 addition & 0 deletions features/breadcrumbs.feature
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Feature: Attaching a series of notable events leading up to errors
And the event "breadcrumbs.0.metaData.method" equals "GET"
And the event "breadcrumbs.0.metaData.url" equals "http://bs-local.com:9340/reflect/"
And the event "breadcrumbs.0.metaData.urlParams.status" equals "444"
And the event "breadcrumbs.0.metaData.urlParams.password" equals "[REDACTED]"
And the event "breadcrumbs.0.metaData.status" equals 444
And the event "breadcrumbs.0.metaData.duration" is greater than 0
And the event "breadcrumbs.0.metaData.requestContentLength" is null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ class NetworkBreadcrumbsScenario : Scenario {
}

override func run() {

// Make some network requests so that automatic network breadcrumbs are left
query(address: "http://bs-local.com:9340/reflect/?status=444")
query(address: "http://bs-local.com:9340/reflect/?status=444&password=T0p5ecr3t")
query(address: "http://bs-local.com:9340/reflect/?delay_ms=3000")

// Send a handled error
Expand Down

0 comments on commit 86e82eb

Please sign in to comment.