Skip to content

Commit

Permalink
Merge pull request #54 from spotify/20200131-expose-lexer
Browse files Browse the repository at this point in the history
Expose redactor
  • Loading branch information
ecamacho committed Feb 3, 2020
2 parents 1efa7b1 + b23dbdc commit f43b546
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 28 deletions.
50 changes: 50 additions & 0 deletions Sources/XCLogParser/lexer/LexRedactor.swift
@@ -0,0 +1,50 @@
// Copyright (c) 2020 Spotify AB.
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import Foundation

public class LexRedactor: LogRedactor {
private static let redactedTemplate = "/Users/<redacted>/"
private lazy var userDirRegex: NSRegularExpression? = {
do {
return try NSRegularExpression(pattern: "\\/Users\\/(\\w*)\\/")
} catch {
return nil
}
}()
public var userDirToRedact: String?

public func redactUserDir(string: String) -> String {
guard let regex = userDirRegex else {
return string
}
if let userDirToRedact = userDirToRedact {
return string.replacingOccurrences(of: userDirToRedact, with: Self.redactedTemplate)
} else {
guard let firstMatch = regex.firstMatch(in: string,
options: [],
range: NSRange(location: 0, length: string.count)) else {
return string
}
let userDir = string.substring(firstMatch.range)
userDirToRedact = userDir
return string.replacingOccurrences(of: userDir, with: Self.redactedTemplate)
}
}
}
38 changes: 10 additions & 28 deletions Sources/XCLogParser/lexer/Lexer.swift
Expand Up @@ -22,24 +22,24 @@ import Foundation
public final class Lexer {

static let SLFHeader = "SLF"
static let redactedTemplate = "/Users/<redacted>/"

let typeDelimiters: CharacterSet
let filePath: String
var classNames = [String]()
var userDirToRedact: String?

lazy var userDirRegex: NSRegularExpression? = {
do {
return try NSRegularExpression(pattern: "\\/Users\\/(\\w*)\\/")
} catch {
return nil
var userDirToRedact: String? {
set {
redactor.userDirToRedact = newValue
}
get {
redactor.userDirToRedact
}
}()
}
private var redactor: LogRedactor

public init(filePath: String) {
self.filePath = filePath
self.typeDelimiters = CharacterSet(charactersIn: TokenType.all())
self.redactor = LexRedactor()
}

/// Tokenizes an xcactivitylog serialized in the `SLF` format
Expand Down Expand Up @@ -188,7 +188,7 @@ public final class Lexer {
#endif
scanner.scanLocation += value
if redacted {
return redactUserDir(string: String(scanner.string[start..<end]))
return redactor.redactUserDir(string: String(scanner.string[start..<end]))
}
return String(scanner.string[start..<end])
}
Expand All @@ -200,24 +200,6 @@ public final class Lexer {
let result = Double(bitPattern: beValue.byteSwapped)
return result
}

private func redactUserDir(string: String) -> String {
guard let regex = userDirRegex else {
return string
}
if let userDirToRedact = userDirToRedact {
return string.replacingOccurrences(of: userDirToRedact, with: Self.redactedTemplate)
} else {
guard let firstMatch = regex.firstMatch(in: string,
options: [],
range: NSRange(location: 0, length: string.count)) else {
return string
}
let userDir = string.substring(firstMatch.range)
userDirToRedact = userDir
return string.replacingOccurrences(of: userDir, with: Self.redactedTemplate)
}
}
}

extension Scanner {
Expand Down
29 changes: 29 additions & 0 deletions Sources/XCLogParser/lexer/LogRedactor.swift
@@ -0,0 +1,29 @@
// Copyright (c) 2020 Spotify AB.
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

public protocol LogRedactor {
/// Predefined (or inferrred during redation process) user home path.
/// Introduced for better performance.
var userDirToRedact: String? {get set}

/// Redacts a string by replacing sensitive username path with a template
/// - parameter string: The string to redact
/// - returns: Redacted text with
func redactUserDir(string: String) -> String
}
62 changes: 62 additions & 0 deletions Tests/XCLogParserTests/LexRedactorTests.swift
@@ -0,0 +1,62 @@
// Copyright (c) 2020 Spotify AB.
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import Foundation
import XCTest
@testable import XCLogParser

class LexRedactorTests: XCTestCase {
let redactor = LexRedactor()

func testRedacting() {

let redactedText = redactor.redactUserDir(string: "Some /Users/private/path")

XCTAssertEqual(redactedText, "Some /Users/<redacted>/path")
}

func testMultiplePathsRedacting() {

let redactedText = redactor.redactUserDir(string: "Some /Users/private/path and other /Users/private/path2")

XCTAssertEqual(redactedText, "Some /Users/<redacted>/path and other /Users/<redacted>/path2")
}

func testRedactingFillsUserDir() {
_ = redactor.redactUserDir(string: "Some /Users/private/path")

XCTAssertEqual(redactor.userDirToRedact, "/Users/private/")
}

func testPredefinedUserDirIsRedacted() {
redactor.userDirToRedact = "/Users/private/"

let redactedText = redactor.redactUserDir(string: "Some /Users/private/path")

XCTAssertEqual(redactedText, "Some /Users/<redacted>/path")
}

func testNotInPredefinedUserDirIsNotRedacted() {
redactor.userDirToRedact = "/Users/priv/"

let redactedText = redactor.redactUserDir(string: "Some /Users/private/path")

XCTAssertEqual(redactedText, "Some /Users/private/path")
}
}

0 comments on commit f43b546

Please sign in to comment.