diff --git a/Signal/src/call/CallService.swift b/Signal/src/call/CallService.swift index 584e374e5..f5ae2086c 100644 --- a/Signal/src/call/CallService.swift +++ b/Signal/src/call/CallService.swift @@ -407,7 +407,7 @@ private class SignalCallData: NSObject { throw CallError.obsoleteCall(description: "Missing peerConnectionClient") } - Logger.info("session description for outgoing call: \(call.identifiersForLogs), sdp: \(sessionDescription.logSafeDescription).") + Logger.info("session description for outgoing call: \(call.identifiersForLogs), sdp: \(sessionDescription.sdp).") return peerConnectionClient.setLocalSessionDescription(sessionDescription).then { do { @@ -714,7 +714,7 @@ private class SignalCallData: NSObject { throw CallError.obsoleteCall(description: "negotiateSessionDescription() response for obsolete call") } - Logger.info("session description for incoming call: \(newCall.identifiersForLogs), sdp: \(negotiatedSessionDescription.logSafeDescription).") + Logger.info("session description for incoming call: \(newCall.identifiersForLogs), sdp: \(negotiatedSessionDescription.sdp).") do { let answerBuilder = SSKProtoCallMessageAnswer.SSKProtoCallMessageAnswerBuilder(id: newCall.signalingId, diff --git a/Signal/src/call/PeerConnectionClient.swift b/Signal/src/call/PeerConnectionClient.swift index bc8ad66a0..3c13e9cfc 100644 --- a/Signal/src/call/PeerConnectionClient.swift +++ b/Signal/src/call/PeerConnectionClient.swift @@ -1109,17 +1109,6 @@ class HardenedRTCSessionDescription { return RTCSessionDescription.init(type: rtcSessionDescription.type, sdp: description) } - - var logSafeDescription: String { - do { - let pattern = "\\d+\\.\\d+\\.\\d+\\.\\d+" - let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpression.Options.caseInsensitive) - let range = NSRange(location: 0, length: sdp.count) - return regex.stringByReplacingMatches(in: sdp, options: [], range: range, withTemplate: "") - } catch { - return "" - } - } } protocol VideoCaptureSettingsDelegate: class { diff --git a/Signal/test/call/PeerConnectionClientTest.swift b/Signal/test/call/PeerConnectionClientTest.swift index a092df808..09fee6e17 100644 --- a/Signal/test/call/PeerConnectionClientTest.swift +++ b/Signal/test/call/PeerConnectionClientTest.swift @@ -130,7 +130,7 @@ class PeerConnectionClientTest: XCTestCase { XCTAssertEqual(1, clientDelegate.dataChannelMessages.count) let dataChannelMessageProto = clientDelegate.dataChannelMessages[0] - XCTAssert(dataChannelMessageProto.hasHangup) + XCTAssertNotNil(dataChannelMessageProto.hangup) let hangupProto = dataChannelMessageProto.hangup! XCTAssertEqual(123, hangupProto.id) diff --git a/Signal/test/util/OWSScrubbingLogFormatterTest.m b/Signal/test/util/OWSScrubbingLogFormatterTest.m index 0921a2b6b..27964ebeb 100644 --- a/Signal/test/util/OWSScrubbingLogFormatterTest.m +++ b/Signal/test/util/OWSScrubbingLogFormatterTest.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSScrubbingLogFormatter.h" @@ -9,22 +9,36 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSScrubbingLogFormatterTest : XCTestCase +@property (nonatomic) NSDate *testDate; + @end @implementation OWSScrubbingLogFormatterTest +- (void)setUp +{ + [super setUp]; + + self.testDate = [NSDate new]; +} + +- (void)tearDown +{ + [super tearDown]; +} + - (DDLogMessage *)messageWithString:(NSString *)string { return [[DDLogMessage alloc] initWithMessage:string level:DDLogLevelInfo flag:0 context:0 - file:nil - function:nil + file:@"mock file name" + function:@"mock function name" line:0 tag:nil options:0 - timestamp:[NSDate new]]; + timestamp:self.testDate]; } - (void)testDataScrubbed @@ -75,7 +89,7 @@ NS_ASSUME_NONNULL_BEGIN } } -- (void)testNonPhonenumberNotScrubbed +- (void)testNonPhoneNumberNotScrubbed { OWSScrubbingLogFormatter *formatter = [OWSScrubbingLogFormatter new]; NSString *actual = @@ -85,6 +99,46 @@ NS_ASSUME_NONNULL_BEGIN XCTAssertNotEqual(NSNotFound, redactedRange.location, "Shouldn't touch non phone string."); } +- (void)testIPAddressesScrubbed +{ + id scrubbingFormatter = [OWSScrubbingLogFormatter new]; + id defaultFormatter = [DDLogFileFormatterDefault new]; + + NSDictionary *valueMap = @{ + @"0.0.0.0" : @"[ REDACTED_IP_ADDRESS:...0 ]", + @"127.0.0.1" : @"[ REDACTED_IP_ADDRESS:...1 ]", + @"255.255.255.255" : @"[ REDACTED_IP_ADDRESS:...255 ]", + @"1.2.3.4" : @"[ REDACTED_IP_ADDRESS:...4 ]", + @"0.0.0.0.0.0" : @"[ REDACTED_IP_ADDRESS:...0 ]", + @"255.255.255.255.255.255" : @"[ REDACTED_IP_ADDRESS:...255 ]", + }; + NSArray *messageFormats = @[ + @"a%@b", + @"http://%@", + @"%@ and %@ and %@", + @"%@", + ]; + + for (NSString *ipAddress in valueMap) { + NSString *redactedIPAddress = valueMap[ipAddress]; + + for (NSString *messageFormat in messageFormats) { + NSString *message = [messageFormat stringByReplacingOccurrencesOfString:@"%@" withString:ipAddress]; + + NSString *expectedRedactedMessage = [defaultFormatter + formatLogMessage:[self messageWithString:[messageFormat + stringByReplacingOccurrencesOfString:@"%@" + withString:redactedIPAddress]]]; + NSString *redactedMessage = [scrubbingFormatter formatLogMessage:[self messageWithString:message]]; + + XCTAssertEqualObjects(expectedRedactedMessage, redactedMessage); + + NSRange ipAddressRange = [redactedMessage rangeOfString:ipAddress]; + XCTAssertEqual(NSNotFound, ipAddressRange.location, "Failed to redact IP address: %@", redactedMessage); + } + } +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/utils/OWSScrubbingLogFormatter.m b/SignalMessaging/utils/OWSScrubbingLogFormatter.m index b46e46ff4..b2c812072 100644 --- a/SignalMessaging/utils/OWSScrubbingLogFormatter.m +++ b/SignalMessaging/utils/OWSScrubbingLogFormatter.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSScrubbingLogFormatter.h" @@ -8,14 +8,65 @@ NS_ASSUME_NONNULL_BEGIN @implementation OWSScrubbingLogFormatter +- (NSRegularExpression *)phoneRegex +{ + NSString *key = @"OWSScrubbingLogFormatter.phoneRegex"; + NSRegularExpression *_Nullable regex = NSThread.currentThread.threadDictionary[key]; + if (!regex) { + NSError *error; + regex = [NSRegularExpression regularExpressionWithPattern:@"\\+\\d{7,12}(\\d{3})" + options:NSRegularExpressionCaseInsensitive + error:&error]; + if (error || !regex) { + OWSFail(@"%@ could not compile regular expression: %@", self.logTag, error); + } + NSThread.currentThread.threadDictionary[key] = regex; + } + return regex; +} + +- (NSRegularExpression *)dataRegex +{ + NSString *key = @"OWSScrubbingLogFormatter.dataRegex"; + NSRegularExpression *_Nullable regex = NSThread.currentThread.threadDictionary[key]; + if (!regex) { + NSError *error; + regex = [NSRegularExpression regularExpressionWithPattern:@"<([\\da-f]{2})[\\da-f]{6}( [\\da-f]{8})*>" + options:NSRegularExpressionCaseInsensitive + error:&error]; + if (error || !regex) { + OWSFail(@"%@ could not compile regular expression: %@", self.logTag, error); + } + NSThread.currentThread.threadDictionary[key] = regex; + } + return regex; +} + +- (NSRegularExpression *)ipAddressRegex +{ + NSString *key = @"OWSScrubbingLogFormatter.ipAddressRegex"; + NSRegularExpression *_Nullable regex = NSThread.currentThread.threadDictionary[key]; + if (!regex) { + // Match IPv4 and IPv6 addresses. + // + // NOTE: the second group matches the last "quad/hex?" of the IPv4/IPv6 address. + NSError *error; + regex = [NSRegularExpression regularExpressionWithPattern:@"(\\d+\\.\\d+\\.)?\\d+\\.\\d+\\.\\d+\\.(\\d+)" + options:NSRegularExpressionCaseInsensitive + error:&error]; + if (error || !regex) { + OWSFail(@"%@ could not compile regular expression: %@", self.logTag, error); + } + NSThread.currentThread.threadDictionary[key] = regex; + } + return regex; +} + - (NSString *__nullable)formatLogMessage:(DDLogMessage *)logMessage { NSString *logString = [super formatLogMessage:logMessage]; - NSRegularExpression *phoneRegex = - [NSRegularExpression regularExpressionWithPattern:@"\\+\\d{7,12}(\\d{3})" - options:NSRegularExpressionCaseInsensitive - error:nil]; + NSRegularExpression *phoneRegex = self.phoneRegex; logString = [phoneRegex stringByReplacingMatchesInString:logString options:0 range:NSMakeRange(0, [logString length]) @@ -25,16 +76,18 @@ NS_ASSUME_NONNULL_BEGIN // We capture only the first two characters of the hex string for logging. // example log line: "Called someFunction with nsData: <01234567 89abcdef>" // scrubbed output: "Called someFunction with nsData: [ REDACTED_DATA:01 ]" - NSRegularExpression *dataRegex = - [NSRegularExpression regularExpressionWithPattern:@"<([\\da-f]{2})[\\da-f]{6}( [\\da-f]{8})*>" - options:NSRegularExpressionCaseInsensitive - error:nil]; - + NSRegularExpression *dataRegex = self.dataRegex; logString = [dataRegex stringByReplacingMatchesInString:logString options:0 range:NSMakeRange(0, [logString length]) withTemplate:@"[ REDACTED_DATA:$1... ]"]; + NSRegularExpression *ipAddressRegex = self.ipAddressRegex; + logString = [ipAddressRegex stringByReplacingMatchesInString:logString + options:0 + range:NSMakeRange(0, [logString length]) + withTemplate:@"[ REDACTED_IP_ADDRESS:...$2 ]"]; + return logString; } diff --git a/SignalServiceKit/src/Contacts/PhoneNumberUtil.m b/SignalServiceKit/src/Contacts/PhoneNumberUtil.m index e91cd8eff..5be24ae7d 100644 --- a/SignalServiceKit/src/Contacts/PhoneNumberUtil.m +++ b/SignalServiceKit/src/Contacts/PhoneNumberUtil.m @@ -18,28 +18,15 @@ @implementation PhoneNumberUtil -+ (NSObject *)sharedLock -{ - static dispatch_once_t onceToken; - static NSObject *lock = nil; - dispatch_once(&onceToken, ^{ - lock = [NSObject new]; - }); - return lock; -} - + (PhoneNumberUtil *)sharedThreadLocal { - @synchronized(self.sharedLock) - { - NSString *key = PhoneNumberUtil.logTag; - PhoneNumberUtil *_Nullable threadLocal = NSThread.currentThread.threadDictionary[key]; - if (!threadLocal) { - threadLocal = [PhoneNumberUtil new]; - NSThread.currentThread.threadDictionary[key] = threadLocal; - } - return threadLocal; + NSString *key = PhoneNumberUtil.logTag; + PhoneNumberUtil *_Nullable threadLocal = NSThread.currentThread.threadDictionary[key]; + if (!threadLocal) { + threadLocal = [PhoneNumberUtil new]; + NSThread.currentThread.threadDictionary[key] = threadLocal; } + return threadLocal; } - (instancetype)init {