Skip to content

fix: make credential file writes atomic and fail closed on corruption#244

Open
bukinoshita wants to merge 4 commits intomainfrom
fix/profile-config-race-4812
Open

fix: make credential file writes atomic and fail closed on corruption#244
bukinoshita wants to merge 4 commits intomainfrom
fix/profile-config-race-4812

Conversation

@bukinoshita
Copy link
Copy Markdown
Member

@bukinoshita bukinoshita commented Apr 9, 2026

Summary by cubic

Prevents silent loss of saved auth profiles by making credential writes atomic and locked, and by failing closed on corrupted JSON.

  • Bug Fixes
    • Atomic writes: temp file + fsync + rename to avoid partial/truncated credentials.json.
    • Advisory lock around read/modify/write to prevent concurrent races.
    • readCredentials() throws CorruptedCredentialsError on invalid JSON; file is preserved; mutators stop instead of rebuilding empty state.
    • Mutators (storeApiKey, removeApiKey, setActiveProfile, renameProfile, storeApiKeyAsync) use immutable updates under the lock; removeApiKey no longer deletes on parse failure.
    • Logout gracefully handles corrupted credentials and still proceeds to the removal path.

Written for commit 219f90b. Summary will update on new commits.

cursoragent and others added 2 commits April 9, 2026 17:50
…losed on corruption

- Add writeFileAtomic: writes to tmp file, fsyncs, renames over original
- Add withFileLock: advisory lock with stale detection for read/modify/write
- readCredentials now throws CorruptedCredentialsError on malformed JSON
  instead of silently returning null (which caused storeApiKey to rebuild
  an empty config and erase all existing profiles)
- removeApiKey no longer deletes the file when it can't parse JSON
- All mutators (storeApiKey, removeApiKey, setActiveProfile, renameProfile,
  storeApiKeyAsync) wrapped in file lock to prevent concurrent overwrites
- All mutations use immutable spread patterns instead of in-place mutation
- logout command gracefully handles corrupted credentials by catching the
  error and proceeding to the removal path

Fixes: BU-652

Co-authored-by: Bu Kinoshita <bukinoshita@users.noreply.github.com>
@bukinoshita
Copy link
Copy Markdown
Member Author

@cubic-dev-ai can you review?

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented Apr 9, 2026

@cubic-dev-ai can you review?

@bukinoshita I have started the AI code review. It will take a few minutes to complete.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/lib/file-lock.ts">

<violation number="1" location="src/lib/file-lock.ts:61">
P2: `sleepSync` burns 100% CPU while spinning. Use `Atomics.wait` for a zero-CPU synchronous sleep — it's available in all supported Node versions (≥20).</violation>
</file>

<file name="src/lib/config.ts">

<violation number="1" location="src/lib/config.ts:394">
P1: The auto-migration write inside `resolveApiKeyAsync` still mutates `creds` in-place and calls `writeCredentials` without `withFileLock`, leaving the same lost-update race this PR eliminates everywhere else. Wrap the write in `withFileLock` and use immutable updates to match the other mutators.</violation>
</file>

<file name="src/commands/auth/logout.ts">

<violation number="1" location="src/commands/auth/logout.ts:52">
P2: This change still allows an uncaught credentials-read throw via `resolveProfileName()`, so logout can crash on corrupted credentials instead of reaching the `remove_failed` handling path.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread src/lib/config.ts
Comment thread src/lib/file-lock.ts
Comment thread src/commands/auth/logout.ts
@bukinoshita bukinoshita marked this pull request as ready for review April 14, 2026 12:55
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.

2 participants