Rewrite debug log scrubbing logic in Swift
Also introduces additional defense-in-depth improvements.
This commit is contained in:
parent
a24309fe29
commit
41ff3c142b
@ -268,8 +268,6 @@
|
||||
34480B361FD0929200BC14EF /* ShareAppExtensionContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 34480B351FD0929200BC14EF /* ShareAppExtensionContext.m */; };
|
||||
34480B551FD0A7A400BC14EF /* DebugLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 34480B4D1FD0A7A300BC14EF /* DebugLogger.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
34480B561FD0A7A400BC14EF /* DebugLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 34480B4E1FD0A7A300BC14EF /* DebugLogger.m */; };
|
||||
34480B571FD0A7A400BC14EF /* OWSScrubbingLogFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 34480B4F1FD0A7A300BC14EF /* OWSScrubbingLogFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
34480B591FD0A7A400BC14EF /* OWSScrubbingLogFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 34480B511FD0A7A400BC14EF /* OWSScrubbingLogFormatter.m */; };
|
||||
34480B5B1FD0A7E300BC14EF /* SignalMessaging-Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 34480B5A1FD0A7E300BC14EF /* SignalMessaging-Prefix.pch */; };
|
||||
344A761124B366F4009D69A5 /* FlagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 344A761024B366F4009D69A5 /* FlagsViewController.swift */; };
|
||||
344A761324B36C8C009D69A5 /* TestingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 344A761224B36C8C009D69A5 /* TestingViewController.swift */; };
|
||||
@ -1394,6 +1392,7 @@
|
||||
F9613CDE2981F15700894B55 /* SqliteUtilTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9613CDD2981F15700894B55 /* SqliteUtilTest.swift */; };
|
||||
F962B38A293F9F1F00765BD8 /* CRC32.swift in Sources */ = {isa = PBXBuildFile; fileRef = F962B389293F9F1F00765BD8 /* CRC32.swift */; };
|
||||
F962B38C293F9F9F00765BD8 /* CRC32Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = F962B38B293F9F9F00765BD8 /* CRC32Test.swift */; };
|
||||
F962FF4929AD0C7C00AFA397 /* OWSScrubbingLogFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F962FF4829AD0C7C00AFA397 /* OWSScrubbingLogFormatter.swift */; };
|
||||
F963164B291AE06C00218FB7 /* OWSScrubbingLogFormatterTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F963164A291AE06C00218FB7 /* OWSScrubbingLogFormatterTest.swift */; };
|
||||
F963F816292D1B5B007DBBBD /* UIButton+SignalUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F963F815292D1B5B007DBBBD /* UIButton+SignalUI.swift */; };
|
||||
F963F818292D7E53007DBBBD /* FormattedNumberField.swift in Sources */ = {isa = PBXBuildFile; fileRef = F963F817292D7E53007DBBBD /* FormattedNumberField.swift */; };
|
||||
@ -2525,8 +2524,6 @@
|
||||
34480B381FD092E300BC14EF /* SignalShareExtension-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SignalShareExtension-Prefix.pch"; sourceTree = "<group>"; };
|
||||
34480B4D1FD0A7A300BC14EF /* DebugLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugLogger.h; sourceTree = "<group>"; };
|
||||
34480B4E1FD0A7A300BC14EF /* DebugLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugLogger.m; sourceTree = "<group>"; };
|
||||
34480B4F1FD0A7A300BC14EF /* OWSScrubbingLogFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSScrubbingLogFormatter.h; sourceTree = "<group>"; };
|
||||
34480B511FD0A7A400BC14EF /* OWSScrubbingLogFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSScrubbingLogFormatter.m; sourceTree = "<group>"; };
|
||||
34480B5A1FD0A7E300BC14EF /* SignalMessaging-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SignalMessaging-Prefix.pch"; sourceTree = "<group>"; };
|
||||
344A761024B366F4009D69A5 /* FlagsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlagsViewController.swift; sourceTree = "<group>"; };
|
||||
344A761224B36C8C009D69A5 /* TestingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestingViewController.swift; sourceTree = "<group>"; };
|
||||
@ -3910,6 +3907,7 @@
|
||||
F9613CDD2981F15700894B55 /* SqliteUtilTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SqliteUtilTest.swift; sourceTree = "<group>"; };
|
||||
F962B389293F9F1F00765BD8 /* CRC32.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CRC32.swift; sourceTree = "<group>"; };
|
||||
F962B38B293F9F9F00765BD8 /* CRC32Test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CRC32Test.swift; sourceTree = "<group>"; };
|
||||
F962FF4829AD0C7C00AFA397 /* OWSScrubbingLogFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSScrubbingLogFormatter.swift; sourceTree = "<group>"; };
|
||||
F963164A291AE06C00218FB7 /* OWSScrubbingLogFormatterTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSScrubbingLogFormatterTest.swift; sourceTree = "<group>"; };
|
||||
F963F815292D1B5B007DBBBD /* UIButton+SignalUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+SignalUI.swift"; sourceTree = "<group>"; };
|
||||
F963F817292D7E53007DBBBD /* FormattedNumberField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormattedNumberField.swift; sourceTree = "<group>"; };
|
||||
@ -5138,8 +5136,7 @@
|
||||
346129371FD1B47200532771 /* OWSPreferences.h */,
|
||||
346129381FD1B47200532771 /* OWSPreferences.m */,
|
||||
34641E172088D7E900E2EDE5 /* OWSScreenLock.swift */,
|
||||
34480B4F1FD0A7A300BC14EF /* OWSScrubbingLogFormatter.h */,
|
||||
34480B511FD0A7A400BC14EF /* OWSScrubbingLogFormatter.m */,
|
||||
F962FF4829AD0C7C00AFA397 /* OWSScrubbingLogFormatter.swift */,
|
||||
4CB93DC12180FF07004B9764 /* ProximityMonitoringManager.swift */,
|
||||
3406D31D25DBF70400885B14 /* RefreshEvent.swift */,
|
||||
45360B8C1F9521F800FA666C /* Searcher.swift */,
|
||||
@ -9143,7 +9140,6 @@
|
||||
3464450E22B7F93600A957B1 /* OWSOrphanDataCleaner.h in Headers */,
|
||||
346129391FD1B47300532771 /* OWSPreferences.h in Headers */,
|
||||
346129B41FD1F7E800532771 /* OWSProfileManager.h in Headers */,
|
||||
34480B571FD0A7A400BC14EF /* OWSScrubbingLogFormatter.h in Headers */,
|
||||
34074F62203D0CBE004596AE /* OWSSounds.h in Headers */,
|
||||
34612A061FD7238600532771 /* OWSSyncManager.h in Headers */,
|
||||
34480B5B1FD0A7E300BC14EF /* SignalMessaging-Prefix.pch in Headers */,
|
||||
@ -10683,7 +10679,7 @@
|
||||
346129B51FD1F7E800532771 /* OWSProfileManager.m in Sources */,
|
||||
3470249E2385B6360078D72C /* OWSProfileManager.swift in Sources */,
|
||||
34641E182088D7E900E2EDE5 /* OWSScreenLock.swift in Sources */,
|
||||
34480B591FD0A7A400BC14EF /* OWSScrubbingLogFormatter.m in Sources */,
|
||||
F962FF4929AD0C7C00AFA397 /* OWSScrubbingLogFormatter.swift in Sources */,
|
||||
34074F61203D0CBE004596AE /* OWSSounds.m in Sources */,
|
||||
34612A071FD7238600532771 /* OWSSyncManager.m in Sources */,
|
||||
885C35502370DFD50004BA35 /* OWSSyncManager.swift in Sources */,
|
||||
|
||||
@ -5,7 +5,9 @@
|
||||
|
||||
import XCTest
|
||||
import CocoaLumberjack
|
||||
import Signal
|
||||
import SignalCoreKit
|
||||
import SignalServiceKit
|
||||
@testable import SignalMessaging
|
||||
|
||||
final class OWSScrubbingLogFormatterTest: XCTestCase {
|
||||
private var formatter: OWSScrubbingLogFormatter { OWSScrubbingLogFormatter() }
|
||||
@ -38,6 +40,18 @@ final class OWSScrubbingLogFormatterTest: XCTestCase {
|
||||
rawMessage.substring(from: datePrefixLength)
|
||||
}
|
||||
|
||||
func testAttachmentPathScrubbed() {
|
||||
let testCases: [String] = [
|
||||
"/Attachments/",
|
||||
"/foo/bar/Attachments/abc123.txt",
|
||||
"Something /foo/bar/Attachments/abc123.txt Something"
|
||||
]
|
||||
|
||||
for testCase in testCases {
|
||||
XCTAssertEqual(format(testCase), "[ REDACTED_CONTAINS_USER_PATH ]")
|
||||
}
|
||||
}
|
||||
|
||||
func testDataScrubbed_preformatted() {
|
||||
let testCases: [String: String] = [
|
||||
"<01>": "[ REDACTED_DATA:01... ]",
|
||||
@ -156,6 +170,51 @@ final class OWSScrubbingLogFormatterTest: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
func testGroupIdScrubbed() {
|
||||
for _ in 1...100 {
|
||||
let groupIdCount = Bool.random() ? kGroupIdLengthV1 : kGroupIdLengthV2
|
||||
let groupId = Randomness.generateRandomBytes(groupIdCount)
|
||||
let groupIdString = TSGroupThread.defaultThreadId(forGroupId: groupId)
|
||||
|
||||
let expectedOutput = "Hello [ REDACTED_GROUP_ID:...\(groupIdString.suffix(2)) ]!"
|
||||
|
||||
let result = format("Hello \(groupIdString)!")
|
||||
|
||||
XCTAssertTrue(
|
||||
result.contains(expectedOutput),
|
||||
"Failed to redact group ID: \(groupIdString). Result was \(result)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testThingsThatLookLikeGroupIdNotScrubbed() {
|
||||
let forbiddenBase64Lengths = Set([
|
||||
kGroupIdLengthV1.base64Length,
|
||||
kGroupIdLengthV2.base64Length
|
||||
])
|
||||
|
||||
for _ in 1...100 {
|
||||
let fakeGroupIdCount: Int32 = {
|
||||
while true {
|
||||
let result = Int32.random(in: 1...(kGroupIdLengthV2 * 2))
|
||||
if !forbiddenBase64Lengths.contains(result.base64Length) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}()
|
||||
let fakeGroupId = Randomness.generateRandomBytes(fakeGroupIdCount)
|
||||
let fakeGroupIdString = TSGroupThread.defaultThreadId(forGroupId: fakeGroupId)
|
||||
let input = "Hello \(fakeGroupIdString)!"
|
||||
|
||||
let result = format(input)
|
||||
XCTAssertEqual(
|
||||
stripDate(fromRawMessage: result),
|
||||
input,
|
||||
"Should not be affected"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testNotScrubbed() {
|
||||
let input = "Some unfiltered string"
|
||||
let result = format(input)
|
||||
@ -265,3 +324,7 @@ final class OWSScrubbingLogFormatterTest: XCTestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension Int32 {
|
||||
var base64Length: Int32 { Int32(4 * ceil(Double(self) / 3)) }
|
||||
}
|
||||
|
||||
@ -21,7 +21,6 @@ FOUNDATION_EXPORT const unsigned char SignalMessagingVersionString[];
|
||||
#import <SignalMessaging/OWSOrphanDataCleaner.h>
|
||||
#import <SignalMessaging/OWSPreferences.h>
|
||||
#import <SignalMessaging/OWSProfileManager.h>
|
||||
#import <SignalMessaging/OWSScrubbingLogFormatter.h>
|
||||
#import <SignalMessaging/OWSSounds.h>
|
||||
#import <SignalMessaging/OWSSyncManager.h>
|
||||
#import <SignalMessaging/ThreadUtil.h>
|
||||
|
||||
@ -5,10 +5,10 @@
|
||||
|
||||
#import "DebugLogger.h"
|
||||
#import "OWSPreferences.h"
|
||||
#import "OWSScrubbingLogFormatter.h"
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
#import <CocoaLumberjack/DDTTYLogger.h>
|
||||
#import <SignalCoreKit/NSDate+OWS.h>
|
||||
#import <SignalMessaging/SignalMessaging-Swift.h>
|
||||
#import <SignalServiceKit/AppContext.h>
|
||||
#import <SignalServiceKit/AppVersion.h>
|
||||
#import <SignalServiceKit/OWSFileSystem.h>
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
//
|
||||
// Copyright 2014 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSScrubbingLogFormatter : DDLogFileFormatterDefault
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,167 +0,0 @@
|
||||
//
|
||||
// Copyright 2016 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
#import "OWSScrubbingLogFormatter.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation OWSScrubbingLogFormatter
|
||||
|
||||
- (NSRegularExpression *)phoneRegex
|
||||
{
|
||||
static NSRegularExpression *regex = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSError *error;
|
||||
regex = [NSRegularExpression regularExpressionWithPattern:@"\\+\\d{7,12}(\\d{3})"
|
||||
options:0
|
||||
error:&error];
|
||||
if (error || !regex) {
|
||||
OWSFail(@"could not compile regular expression: %@", error);
|
||||
}
|
||||
});
|
||||
return regex;
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)uuidRegex
|
||||
{
|
||||
static NSRegularExpression *regex = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
// Example: AF112388-9F3D-4EBA-A321-CCE01BA2C85D
|
||||
NSError *error;
|
||||
regex =
|
||||
[NSRegularExpression regularExpressionWithPattern:
|
||||
@"[\\da-f]{8}\\-[\\da-f]{4}\\-[\\da-f]{4}\\-[\\da-f]{4}\\-[\\da-f]{9}([\\da-f]{3})"
|
||||
options:NSRegularExpressionCaseInsensitive
|
||||
error:&error];
|
||||
if (error || !regex) {
|
||||
OWSFail(@"could not compile regular expression: %@", error);
|
||||
}
|
||||
});
|
||||
return regex;
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)dataRegex
|
||||
{
|
||||
static NSRegularExpression *regex = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSError *error;
|
||||
regex = [NSRegularExpression regularExpressionWithPattern:@"<([\\da-f]{2})[\\da-f]{0,6}( [\\da-f]{2,8})*>"
|
||||
options:NSRegularExpressionCaseInsensitive
|
||||
error:&error];
|
||||
if (error || !regex) {
|
||||
OWSFail(@"could not compile regular expression: %@", error);
|
||||
}
|
||||
});
|
||||
return regex;
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)ios13DataRegex
|
||||
{
|
||||
static NSRegularExpression *regex = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSError *error;
|
||||
regex = [NSRegularExpression
|
||||
regularExpressionWithPattern:@"\\{length = \\d+, bytes = 0x([\\da-f]{2})[\\.\\da-f ]*\\}"
|
||||
options:NSRegularExpressionCaseInsensitive
|
||||
error:&error];
|
||||
if (error || !regex) {
|
||||
OWSFail(@"could not compile regular expression: %@", error);
|
||||
}
|
||||
});
|
||||
return regex;
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)ipV4AddressRegex
|
||||
{
|
||||
static NSRegularExpression *regex = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
// NOTE: The group matches the last quad of the IPv4 address.
|
||||
NSError *error;
|
||||
regex = [NSRegularExpression regularExpressionWithPattern:@"\\d+\\.\\d+\\.\\d+\\.(\\d+)"
|
||||
options:NSRegularExpressionCaseInsensitive
|
||||
error:&error];
|
||||
if (error || !regex) {
|
||||
OWSFail(@"could not compile regular expression: %@", error);
|
||||
}
|
||||
});
|
||||
return regex;
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)longHexRegex
|
||||
{
|
||||
static NSRegularExpression *regex = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
// Any hex string of 14 chars (7 bytes) or more.
|
||||
// Example: A321CCE01BA2C85D
|
||||
NSError *error;
|
||||
regex = [NSRegularExpression regularExpressionWithPattern:@"[\\da-f]{11,}([\\da-f]{3})"
|
||||
options:NSRegularExpressionCaseInsensitive
|
||||
error:&error];
|
||||
if (error || !regex) {
|
||||
OWSFail(@"could not compile regular expression: %@", error);
|
||||
}
|
||||
});
|
||||
return regex;
|
||||
}
|
||||
|
||||
- (NSString *__nullable)formatLogMessage:(DDLogMessage *)logMessage
|
||||
{
|
||||
NSString *logString = [super formatLogMessage:logMessage];
|
||||
|
||||
NSRegularExpression *phoneRegex = self.phoneRegex;
|
||||
logString = [phoneRegex stringByReplacingMatchesInString:logString
|
||||
options:0
|
||||
range:NSMakeRange(0, [logString length])
|
||||
withTemplate:@"[ REDACTED_PHONE_NUMBER:xxx$1 ]"];
|
||||
|
||||
NSRegularExpression *uuidRegex = self.uuidRegex;
|
||||
logString = [uuidRegex stringByReplacingMatchesInString:logString
|
||||
options:0
|
||||
range:NSMakeRange(0, [logString length])
|
||||
withTemplate:@"[ REDACTED_UUID:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx$1 ]"];
|
||||
|
||||
// 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 = self.dataRegex;
|
||||
logString = [dataRegex stringByReplacingMatchesInString:logString
|
||||
options:0
|
||||
range:NSMakeRange(0, [logString length])
|
||||
withTemplate:@"[ REDACTED_DATA:$1... ]"];
|
||||
|
||||
// On iOS 13, when built with the 13 SDK, NSData's description has changed
|
||||
// and needs to be scrubbed specifically.
|
||||
// example log line: "Called someFunction with nsData: {length = 8, bytes = 0x0123456789abcdef}"
|
||||
// scrubbed output: "Called someFunction with nsData: [ REDACTED_DATA:96 ]"
|
||||
NSRegularExpression *ios13DataRegex = self.ios13DataRegex;
|
||||
logString = [ios13DataRegex stringByReplacingMatchesInString:logString
|
||||
options:0
|
||||
range:NSMakeRange(0, [logString length])
|
||||
withTemplate:@"[ REDACTED_DATA:$1... ]"];
|
||||
|
||||
NSRegularExpression *ipV4AddressRegex = self.ipV4AddressRegex;
|
||||
logString = [ipV4AddressRegex stringByReplacingMatchesInString:logString
|
||||
options:0
|
||||
range:NSMakeRange(0, [logString length])
|
||||
withTemplate:@"[ REDACTED_IPV4_ADDRESS:...$1 ]"];
|
||||
|
||||
NSRegularExpression *longHexRegex = self.longHexRegex;
|
||||
logString = [longHexRegex stringByReplacingMatchesInString:logString
|
||||
options:0
|
||||
range:NSMakeRange(0, [logString length])
|
||||
withTemplate:@"[ REDACTED_HEX:...$1 ]"];
|
||||
|
||||
return logString;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
116
SignalMessaging/utils/OWSScrubbingLogFormatter.swift
Normal file
116
SignalMessaging/utils/OWSScrubbingLogFormatter.swift
Normal file
@ -0,0 +1,116 @@
|
||||
//
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CocoaLumberjack
|
||||
import SignalServiceKit
|
||||
|
||||
@objc
|
||||
class OWSScrubbingLogFormatter: DDLogFileFormatterDefault {
|
||||
private struct Replacement {
|
||||
let regex: NSRegularExpression
|
||||
let replacementTemplate: String
|
||||
|
||||
init(
|
||||
pattern: String,
|
||||
options: NSRegularExpression.Options = [],
|
||||
replacementTemplate: String
|
||||
) {
|
||||
do {
|
||||
self.regex = try .init(pattern: pattern, options: options)
|
||||
} catch {
|
||||
owsFail("Could not compile regular expression: \(error)")
|
||||
}
|
||||
self.replacementTemplate = replacementTemplate
|
||||
}
|
||||
|
||||
init(groupIdLength: Int32) {
|
||||
let prefix = TSGroupThread.groupThreadUniqueIdPrefix
|
||||
let groupIdBase64StringLength = groupIdLength.base64Length
|
||||
|
||||
let unredactedSize = 2
|
||||
let redactedSize = groupIdBase64StringLength - unredactedSize
|
||||
|
||||
// This assertion exists to prevent someone from updating the values and forgetting to
|
||||
// update things here.
|
||||
owsAssert(
|
||||
prefix == "g" &&
|
||||
groupIdBase64StringLength >= (unredactedSize + 1) &&
|
||||
groupIdBase64StringLength <= 100
|
||||
)
|
||||
|
||||
let base64Pattern = "A-Za-z0-9+/="
|
||||
let base64Char = "[\(base64Pattern)]"
|
||||
let notBase64Char = "[^\(base64Pattern)]"
|
||||
self.init(
|
||||
pattern: "(^|\(notBase64Char))\(prefix)\(base64Char){\(redactedSize)}(\(base64Char){\(unredactedSize)})($|\(notBase64Char))",
|
||||
replacementTemplate: "$1[ REDACTED_GROUP_ID:...$2 ]$3"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private let replacements: [Replacement] = [
|
||||
.init(
|
||||
pattern: "\\+\\d{7,12}(\\d{3})",
|
||||
replacementTemplate: "[ REDACTED_PHONE_NUMBER:xxx$1 ]"
|
||||
),
|
||||
// It's important to redact GV2 IDs first because they're longer. If the shorter IDs were
|
||||
// first, we'd be left with a bunch of partially-redacted GV2 IDs.
|
||||
.init(groupIdLength: kGroupIdLengthV2),
|
||||
.init(groupIdLength: kGroupIdLengthV1),
|
||||
.init(
|
||||
pattern: "[\\da-f]{8}\\-[\\da-f]{4}\\-[\\da-f]{4}\\-[\\da-f]{4}\\-[\\da-f]{9}([\\da-f]{3})",
|
||||
options: .caseInsensitive,
|
||||
replacementTemplate: "[ REDACTED_UUID:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx$1 ]"
|
||||
),
|
||||
.init(
|
||||
pattern: "<([\\da-f]{2})[\\da-f]{0,6}( [\\da-f]{2,8})*>",
|
||||
options: .caseInsensitive,
|
||||
replacementTemplate: "[ REDACTED_DATA:$1... ]"
|
||||
),
|
||||
// On iOS 13, when built with the 13 SDK, NSData's description has changed and needs to be
|
||||
// scrubbed specifically.
|
||||
// example log line: "Called someFunction with nsData: {length = 8, bytes = 0x0123456789abcdef}"
|
||||
// scrubbed output: "Called someFunction with nsData: [ REDACTED_DATA:96 ]"
|
||||
.init(
|
||||
pattern: "\\{length = \\d+, bytes = 0x([\\da-f]{2})[\\.\\da-f ]*\\}",
|
||||
options: .caseInsensitive,
|
||||
replacementTemplate: "[ REDACTED_DATA:$1... ]"
|
||||
),
|
||||
.init(
|
||||
pattern: "\\d+\\.\\d+\\.\\d+\\.(\\d+)",
|
||||
replacementTemplate: "[ REDACTED_IPV4_ADDRESS:...$1 ]"
|
||||
),
|
||||
.init(
|
||||
pattern: "[\\da-f]{11,}([\\da-f]{3})",
|
||||
options: .caseInsensitive,
|
||||
replacementTemplate: "[ REDACTED_HEX:...$1 ]"
|
||||
)
|
||||
]
|
||||
|
||||
public override func format(message: DDLogMessage) -> String? {
|
||||
guard var logString = super.format(message: message) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if logString.contains("/Attachments/") {
|
||||
return "[ REDACTED_CONTAINS_USER_PATH ]"
|
||||
}
|
||||
|
||||
for replacement in replacements {
|
||||
logString = replacement.regex.stringByReplacingMatches(
|
||||
in: logString,
|
||||
range: logString.entireRange,
|
||||
withTemplate: replacement.replacementTemplate
|
||||
)
|
||||
}
|
||||
|
||||
return logString
|
||||
}
|
||||
}
|
||||
|
||||
private extension Int32 {
|
||||
var base64Length: Int { Int(4 * ceil(Double(self) / 3)) }
|
||||
}
|
||||
@ -38,7 +38,7 @@ public extension TSGroupThread {
|
||||
groupMembership.isLocalUserFullMemberAndAdministrator
|
||||
}
|
||||
|
||||
private static let groupThreadUniqueIdPrefix = "g"
|
||||
public static let groupThreadUniqueIdPrefix = "g"
|
||||
|
||||
private static let uniqueIdMappingStore = SDSKeyValueStore(collection: "TSGroupThread.uniqueIdMappingStore")
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user