Skip to content

fix(settings): preserve provider API keys until Keychain write succeeds#433

Open
postoso wants to merge 1 commit into
altic-dev:mainfrom
postoso:fix/keychain-migration-dataloss
Open

fix(settings): preserve provider API keys until Keychain write succeeds#433
postoso wants to merge 1 commit into
altic-dev:mainfrom
postoso:fix/keychain-migration-dataloss

Conversation

@postoso

@postoso postoso commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

What

A destroy-before-confirm data-loss in provider API-key handling (SettingsStore). scrubSavedProviderAPIKeys() blanked the in-memory plaintext key regardless of whether the Keychain storeKey call 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 legacy ProviderAPIKeys defaults 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 small ProviderKeychain protocol that the production KeychainService conforms to and that can be injected in tests.

Tests

New ProviderAPIKeyMigrationTests using 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 test is green and swiftlint --strict is clean.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +3193 to +3197
decoded[index] = SavedProvider(
id: provider.id,
name: provider.name,
baseURL: provider.baseURL,
apiKey: "",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@postoso

postoso commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

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".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant