Make account store edits transactional - #289
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
ceiling | eae7642 | Commit Preview URL Branch Preview URL |
Aug 14 2026, 02:50 AM |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughAccount persistence now uses locked transactional updates. Directory and token account commands mutate the latest on-disk state, save only changed data, preserve storage errors, and test concurrent updates and no-op behavior. ChangesAccount persistence
Estimated code review effort: 4 (Complex) | ~45 minutes Mergeability Score: ⚪ Minimal · up to The account-edit changes are merge-ready after normal checks and review; no actionable merge-blocking risk remains. Sequence Diagram(s)sequenceDiagram
participant AccountCommand
participant AccountStore
participant StoreLock
participant DiskState
AccountCommand->>AccountStore: request transactional account update
AccountStore->>StoreLock: acquire write lock
StoreLock->>DiskState: load latest state
AccountStore->>AccountStore: apply account mutation
AccountStore->>DiskState: save changed state
AccountStore-->>AccountCommand: return updated snapshot and result
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
@macroscope-app review |
|
Manual reviews triggered for commit All prior checks · these links stay valid even if you push more commits. |
|
Review started. Results will be posted as check runs when complete. |
ApprovabilityVerdict: Would Approve This PR fixes a race condition by wrapping account store operations in a transactional lock. The changes are mechanical refactoring of existing logic, tests are included, and the author owns all modified files. Macroscope would have approved this PR. Enable approvability here. |
Automated reviewNew in this pass: 1 issue.
Still open from earlier passes:
Resolved since the previous pass: 2. For coding agents: fix BLOCK and FIX IF QUICK findings now; everything else is tracked or informational; never exceed one CodeRev fix round per PR. Advisory. Findings generated by |
330a24b to
a6d37e2
Compare
98a7237 to
e9c4c9c
Compare
Every account edit read the store, changed it in memory, and wrote it back, with nothing holding those three steps together. Two edits that overlapped could each write a copy of the file that predated the other, so one of them disappeared with no error. A Copilot device login finishing while the user edited accounts in Settings could do the same. Add try_update and try_update_provider, which hold the shared state write lock across the load, the mutation, and the atomic save, and route every command and CLI path through them. The closures stay pure local mutations. Provider and network I/O happens before the transaction is entered, so the lock is never held across a request. The lock is not reentrant, so a closure must not reach another store. DirectoryAccountStore::save now takes the lock too, matching what save_provider already did on the token store.
try_update only saves when the loaded snapshot actually changed, so a no-op cannot create an empty accounts file or drop unknown fields. save() keeps Json errors as Json instead of wrapping them through io::Error::other.
adbde49 to
eae7642
Compare
## Summary Leftover Medium security work from the Aug 12 audit, after the High batch in 1.5.30 / PRs #276–#289. 1. **SBS-735 / SBS-734** — Cookie and token paste fields are masked by default (`SecretField`). Cookie, API-key, token-account, and provider-wide revoke go through an in-app confirm dialog that names the provider/account and credential type, then refresh provider state so detail is not left on a stale cached snapshot. 2. **SBS-729** — Windows-owned `powershell` / `where.exe` launch from `%SystemRoot%\System32` instead of PATH. Claude, Codex, and `gh` still use PATH. 3. **SBS-728** — `codexbar serve` requires a per-user bearer token unless `--allow-unauthenticated`. Identity and raw provider errors are omitted by default; `--include-identity` opts back in. `/health` stays open. GitHub #273 (`--refresh-interval`) is out of scope. Linear-only issues; do not mirror to GitHub. ## Related issue Fixes SBS-735, SBS-734, SBS-729, SBS-728. ## Affected areas - [x] Settings UI - [x] CLI - [x] Provider-specific behavior - [x] Documentation - [ ] Tray panel - [ ] Config file / settings persistence - [ ] Installer / release packaging - [ ] Startup / background behavior ## Validation - [x] Other: `pnpm exec vitest run` on settings/credential tests (105 passed); `pnpm exec tsc --noEmit`; locale drift 673 keys OK - [x] Other: `cargo test --manifest-path rust/Cargo.toml --lib -- host::windows_system cli::serve locale::tests` (17 passed) - [x] Other: `cargo fmt --all` on both manifests - [ ] `powershell.exe ... local-check.ps1` — not run (Linux workspace) - Tauri `clippy` not run here (`glib-2.0` missing). Shared-crate `clippy -D warnings` still hits two pre-existing Linux-only unused items in `secure_file.rs` and `updater.rs`. ## UI / tray proof - [x] Visual proof was not practical; manual validation and explanation attached SecretField and ConfirmDialog are covered by component tests (default mask, reveal/hide, accessible name, cancel/confirm/failure). No running desktop shell on this Linux host. ## Notes for reviewers Three commits, one per recommended slice. The serve token lives in the user config dir (`serve.token`, 0600 / current-user ACL on Windows) and is printed on start. Existing local scripts need `--allow-unauthenticated` or the printed bearer header. <!-- Macroscope's pull request summary starts here --> <!-- Macroscope will only edit the content between these invisible markers, and the markers themselves will not be visible in the GitHub rendered markdown. --> <!-- If you delete either of the start / end markers from your PR's description, Macroscope will append its summary at the bottom of the description. --> > [!NOTE] > ### Add confirmation dialogs for credential removal and harden Windows binary resolution > - Credential removal actions (API key, cookie, token, revoke) now open a `ConfirmDialog` before calling the backend; canceling leaves credentials unchanged and success shows a localized status message with proper ARIA roles. > - Adds reusable `ConfirmDialog` and `SecretField` components with focus trapping, Escape/backdrop cancel, and masked input with reveal/hide toggle. > - The `serve` CLI command now enforces bearer-token authentication by default, persisting a token under the OS config dir; `/health` is public while `/usage` and `/cost` require auth. `--allow-unauthenticated` and `--include-identity` flags control these behaviors and response redaction. > - On Windows, all invocations of `powershell.exe`, `where.exe`, `rundll32.exe`, and `explorer.exe` now resolve through trusted `%SystemRoot%\System32` paths instead of relying on PATH lookup; functions fail explicitly if the binary is not found. > - Risk: the `serve` command is now authenticated by default, breaking existing unauthenticated clients unless `--allow-unauthenticated` is passed. > > <!-- Macroscope's review summary starts here --> > > <sup><a href="https://app.macroscope.com">Macroscope</a> summarized 026ca25.</sup> > <!-- Macroscope's review summary ends here --> > <!-- Macroscope's pull request summary ends here --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added confirmation dialogs for removing cookies, API keys, token accounts, and stored credentials. * Added masked secret fields with reveal/hide controls and localized status messages. * Added optional authentication for the local server, with secure token storage and identity details in usage responses. * **Bug Fixes** * Improved Windows discovery of system tools and PowerShell executables. * **Documentation** * Expanded `serve` command guidance for authentication and usage requests. * **Localization** * Added English and Simplified Chinese credential-management translations. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Why
Every account edit read the store, changed it in memory, and wrote it back, with nothing holding those three steps together. Two overlapping edits could each write a copy of the file that predated the other, so one of them disappeared with no error and no sign anything had gone wrong. A Copilot device login finishing while the user edited accounts in Settings could do the same.
What
DirectoryAccountStore::try_updateandTokenAccountStore::try_update_providerhold the shared state write lock across the load, the mutation, and the atomic save.ensure_ambient_registered, and the Copilot device login.DirectoryAccountStore::savenow takes the lock too, matching whatsave_provideralready did on the token store.Notes for review
try_update*callssave_unlockedrather than the lockingsave, so there's no nesting.run_copilot_device_logincompletes the device flow first and only enters the closure to record the result, so the lock is never held across a request. Documented on both methods since it's the easy thing to get wrong later.(data, T)lets callers keep the post-update snapshot without a second read.Testing
cargo test -- account_dirs token_accounts cli::account— 21 pass, including a test per store where two threads add concurrently and both survive.cargo checkon the tauri app is clean.cargo clippy --all-targets -- -D warningsclean.Note
Make account store edits transactional to prevent races between concurrent changes
try_updatetoDirectoryAccountStoreandtry_update_providertoTokenAccountStore, each performing a read-modify-write under a cross-process state lock and skipping writes when data is unchanged.saveandsave_providernow also acquire the state lock internally, so direct saves are also serialized.PartialEqis derived on account data structs to enable no-op write detection.Macroscope summarized eae7642.
Summary by CodeRabbit