-
Notifications
You must be signed in to change notification settings - Fork 1
fix: keychain access persistence #326
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
Open
jvsena42
wants to merge
17
commits into
master
Choose a base branch
from
fix/keychain-access
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
7e3c260
chore: create instalation marker utility
jvsena42 e747b79
fix: update keychain attributes to prevent from updating to the cloud
jvsena42 4812373
chore: add RN cleanup methods
jvsena42 e46e5d0
fix: cleanup RN files after success migration
jvsena42 333ff18
fix: implement orphaned keychain check
jvsena42 d3f8f88
fix: delete marker on app reset
jvsena42 780f10c
Merge branch 'master' into fix/keychain-access
jvsena42 850d03c
test: creation marker tests
jvsena42 82f22a1
test: orphaned keychain tests
jvsena42 9f1fb0a
test: update keychain tests
jvsena42 4027c12
test: RN migration cleanup tests
jvsena42 d503eb3
test: test isolation
jvsena42 e040853
chore: remove unwrap focing
jvsena42 f621dc4
test: remove redundtant check
jvsena42 2eed320
Merge branch 'master' into fix/keychain-access
jvsena42 40a72dc
Merge branch 'master' into fix/keychain-access
jvsena42 aba6816
fix: change accessibility level to notification extension
jvsena42 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import Foundation | ||
|
|
||
| /// Utility to manage an installation marker file that helps detect orphaned keychain entries. | ||
| /// | ||
| /// The marker is placed in the app sandbox Documents directory (NOT the app group container) | ||
| /// because this directory is deleted when the app is uninstalled, while the keychain persists. | ||
| /// | ||
| /// By checking if the marker exists at app startup, we can detect if: | ||
| /// - The marker exists: App was installed before, keychain is valid | ||
| /// - The marker doesn't exist but keychain has data: Orphaned keychain from previous install | ||
| /// | ||
| /// This helps prevent security issues where a reinstalled app might find old keychain data | ||
| /// without corresponding wallet data (LDK, CoreDB, UserDefaults). | ||
| enum InstallationMarker { | ||
| private static let markerFileName = ".bitkit_installed" | ||
|
|
||
| /// App sandbox Documents directory (NOT app group) - gets deleted on uninstall | ||
| private static var sandboxDocumentsUrl: URL { | ||
| FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] | ||
| } | ||
|
|
||
| static var markerPath: URL { | ||
| sandboxDocumentsUrl.appendingPathComponent(markerFileName) | ||
| } | ||
|
|
||
| /// Check if the installation marker exists | ||
| static func exists() -> Bool { | ||
| FileManager.default.fileExists(atPath: markerPath.path) | ||
| } | ||
|
|
||
| /// Create the installation marker file | ||
| /// Should be called after handling any orphaned keychain detection | ||
| static func create() throws { | ||
| let data = UUID().uuidString.data(using: .utf8)! | ||
| try data.write(to: markerPath) | ||
| Logger.info("Installation marker created", context: "InstallationMarker") | ||
| } | ||
|
|
||
| /// Delete the installation marker file | ||
| /// Should be called during app reset/wipe to ensure clean state on next install | ||
| static func delete() throws { | ||
| if exists() { | ||
| try FileManager.default.removeItem(at: markerPath) | ||
| Logger.info("Installation marker deleted", context: "InstallationMarker") | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| @testable import Bitkit | ||
| import XCTest | ||
|
|
||
| final class InstallationMarkerTests: XCTestCase { | ||
| override func setUp() { | ||
| super.setUp() | ||
| // Clean up before each test | ||
| try? InstallationMarker.delete() | ||
| } | ||
|
|
||
| override func tearDown() { | ||
| // Clean up after each test | ||
| try? InstallationMarker.delete() | ||
| super.tearDown() | ||
| } | ||
|
|
||
| func testMarkerDoesNotExistInitially() { | ||
| // After cleanup in setUp, marker should not exist | ||
| XCTAssertFalse(InstallationMarker.exists()) | ||
| } | ||
|
|
||
| func testCreateMarker() throws { | ||
| // Initially should not exist | ||
| XCTAssertFalse(InstallationMarker.exists()) | ||
|
|
||
| // Create the marker | ||
| try InstallationMarker.create() | ||
|
|
||
| // Now should exist | ||
| XCTAssertTrue(InstallationMarker.exists()) | ||
|
|
||
| // Verify file actually exists at the expected path | ||
| XCTAssertTrue(FileManager.default.fileExists(atPath: InstallationMarker.markerPath.path)) | ||
| } | ||
|
|
||
| func testDeleteMarker() throws { | ||
| // Create the marker first | ||
| try InstallationMarker.create() | ||
| XCTAssertTrue(InstallationMarker.exists()) | ||
|
|
||
| // Delete it | ||
| try InstallationMarker.delete() | ||
|
|
||
| // Should no longer exist | ||
| XCTAssertFalse(InstallationMarker.exists()) | ||
| XCTAssertFalse(FileManager.default.fileExists(atPath: InstallationMarker.markerPath.path)) | ||
| } | ||
|
|
||
| func testDeleteNonExistentMarkerDoesNotThrow() { | ||
| // Ensure marker doesn't exist | ||
| XCTAssertFalse(InstallationMarker.exists()) | ||
|
|
||
| // Deleting non-existent marker should not throw | ||
| XCTAssertNoThrow(try InstallationMarker.delete()) | ||
| } | ||
|
|
||
| func testMarkerPathUsesSandboxDocuments() { | ||
| // Get the expected sandbox Documents directory | ||
| let sandboxDocuments = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] | ||
|
|
||
| // Verify marker path is within sandbox Documents, not app group | ||
| XCTAssertTrue(InstallationMarker.markerPath.path.hasPrefix(sandboxDocuments.path)) | ||
|
|
||
| // Verify it's NOT using the app group container | ||
| // App group would contain "group.bitkit" in the path | ||
| XCTAssertFalse(InstallationMarker.markerPath.path.contains("group.bitkit")) | ||
| } | ||
|
|
||
| func testCreateMarkerIsIdempotent() throws { | ||
| // Create the marker | ||
| try InstallationMarker.create() | ||
| XCTAssertTrue(InstallationMarker.exists()) | ||
|
|
||
| // Creating again should overwrite without error | ||
| // (the file content is a new UUID each time, but that's fine) | ||
| try InstallationMarker.create() | ||
| XCTAssertTrue(InstallationMarker.exists()) | ||
| } | ||
|
|
||
| func testMarkerPersistsAcrossChecks() throws { | ||
| // Create the marker | ||
| try InstallationMarker.create() | ||
|
|
||
| // Multiple checks should all return true | ||
| XCTAssertTrue(InstallationMarker.exists()) | ||
| XCTAssertTrue(InstallationMarker.exists()) | ||
| XCTAssertTrue(InstallationMarker.exists()) | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.