fix(settings): preserve provider API keys until Keychain write succeeds#433
fix(settings): preserve provider API keys until Keychain write succeeds#433postoso wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1c5c0851b9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| decoded[index] = SavedProvider( | ||
| id: provider.id, | ||
| name: provider.name, | ||
| baseURL: provider.baseURL, | ||
| apiKey: "", |
There was a problem hiding this comment.
Don't keep plaintext keys when only legacy cleanup fails
When a legacy Keychain item cannot be deleted, KeychainService.saveStoredKeys() throws from removeLegacyEntries() after SecItemAdd/SecItemUpdate has already persisted the consolidated key. Because this new blanking now only runs if storeKey returns normally, that cleanup-only failure path leaves the provider API key in SavedProviders plaintext on every launch even though the replacement Keychain write succeeded; the migration should distinguish write failure from post-write cleanup failure or avoid surfacing cleanup failure as a failed store.
Useful? React with 👍 / 👎.
|
@codex review |
|
Codex Review: Didn't find any major issues. 🚀 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
What
A destroy-before-confirm data-loss in provider API-key handling (
SettingsStore).scrubSavedProviderAPIKeys()blanked the in-memory plaintext key regardless of whether the KeychainstoreKeycall threw, and if another provider's write succeeded the whole array (with the failed key already blanked) was persisted, permanently losing that key.migrateProviderAPIKeysIfNeeded()similarly removed the legacyProviderAPIKeysdefaults entry and legacy Keychain entries before the new write was confirmed, so a failed migration write lost the keys.How
Only blank or remove a legacy source after its replacement write is verified: the plaintext-blanking now happens inside the do-block after a successful
storeKey, and migration removes legacy sources only after the merged write succeeds (otherwise the legacy data survives and is retried on the next launch). To make this unit-testable without the real Keychain, access is abstracted behind a smallProviderKeychainprotocol that the productionKeychainServiceconforms to and that can be injected in tests.Tests
New
ProviderAPIKeyMigrationTestsusing a throwing/recording keychain mock: keys are preserved when a write fails (both the scrub and migrate paths), and normal migration still happens on success.xcodebuild testis green andswiftlint --strictis clean.