Skip to content

[FirebaseCoreInternal] Sanitize app ID used in file system resource #9683

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ public final class HeartbeatController {
private let heartbeatsStorageCapacity: Int = 30
/// Current date provider. It is used for testability.
private let dateProvider: () -> Date
// TODO: Maybe share config with HeartbeatsPayload's DateFormatter?
/// Used for standardizing dates for calendar-day comparision.
static let dateStandardizer: (Date) -> (Date) = {
var calendar = Calendar(identifier: .iso8601)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ final class HeartbeatStorage: HeartbeatStorageProtocol {

// MARK: - HeartbeatStorageProtocol

// TODO: Document.
/// Synchronously reads from and writes to storage using the given transform block.
/// - Parameter transform: A block to transform the currently stored heartbeats bundle to a new
/// heartbeats bundle value.
func readAndWriteSync(using transform: (HeartbeatsBundle?) -> HeartbeatsBundle?) {
queue.sync {
let oldHeartbeatsBundle = try? load(from: storage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ extension FileStorage: StorageFactory {
static func makeStorage(id: String) -> Storage {
let rootDirectory = FileManager.default.applicationSupportDirectory
let heartbeatDirectoryPath = Constants.heartbeatFileStorageDirectoryPath
let heartbeatFilePath = "heartbeats-\(id)"

// Sanitize the `id` so the heartbeat file name does not include a ":".
let sanitizedID = id.replacingOccurrences(of: ":", with: "_")
let heartbeatFilePath = "heartbeats-\(sanitizedID)"

let storageURL = rootDirectory
.appendingPathComponent(heartbeatDirectoryPath, isDirectory: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,4 +364,30 @@ class HeartbeatLoggingIntegrationTests: XCTestCase {
XCTAssertNotNil(try Data(contentsOf: heartbeatsFileURL), "Data should not be nil.")
#endif
}

#if !os(tvOS)
// Do not run on tvOS because tvOS uses UserDefaults to store heartbeats.
func testControllerCreatesHeartbeatStorageWithSanitizedFileName() throws {
// Given
let appID = "1:123456789000:ios:abcdefghijklmnop"
let sanitizedAppID = appID.replacingOccurrences(of: ":", with: "_")
let controller = HeartbeatController(id: appID)
// When
// - Trigger the controller to write to the file system.
controller.log("dummy_agent")
_ = XCTWaiter.wait(for: [expectation(description: "Wait for async log.")], timeout: 0.1)
// Then
let heartbeatsDirectoryURL = FileManager.default
.applicationSupportDirectory
.appendingPathComponent(
HeartbeatLoggingTestUtils.Constants.heartbeatFileStorageDirectoryPath,
isDirectory: true
)

let directoryContents = try FileManager.default
.contentsOfDirectory(atPath: heartbeatsDirectoryURL.path)

XCTAssertEqual(directoryContents, ["heartbeats-\(sanitizedAppID)"])
}
#endif // !os(tvOS)
}