You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(secrets): DPAPI legacy migration can clobber a concurrent rotation #486
Found while reviewing #464 at commit c46016c. Not a merge blocker; filing so it is tracked rather than lost in the review thread. Related to #461 (Windows support).
The keychain-windows legacy-plaintext migration runs on the read path and finishes with an unconditional mv -f over the key's file, using the value captured before the PowerShell encrypt subprocess ran. Nothing re-checks that the file is still the unmarked legacy content that was read. If an acq secret set or rotation lands inside that window (a real powershell.exe -Command startup takes hundreds of milliseconds to seconds), the freshly rotated value is silently replaced by the old one.
Reproduced with a stubbed powershell.exe whose encrypt step sleeps:
reader start (legacy plaintext on disk, encrypt takes ~2s)
writer -> stores NEW-rotated-token
immediately after writer: [NEW-rotated-token]
reader done -> returns [OLD-legacy-token]
FINAL store content: [OLD-legacy-token] <-- rotation silently reverted
Scope is narrow and self-extinguishing: it only affects the first read of a key that still holds a legacy plaintext value, it requires concurrency, and once the key is migrated the path never runs again for that key. The stale value is also well-formed, so the post-create ensure_valid_key gate catches it, but it presents as a confusing auth failure against a key the user just rotated.
Suggested fix: before the rename, re-check that the target file is still unmarked and still byte-identical to what was read; skip the migration otherwise.
Verified fixed by #464. The merged code re-checks that the target file is still the exact unmarked legacy plaintext before renaming the migrated DPAPI envelope, and skips migration if a concurrent writer changed it. The PR also added a regression test: keychain-windows: migration does not clobber a rewrite that lands mid-encryption.
Activity
fix(secrets): make DPAPI legacy migration race-safe and observable
fix(secrets): harden DPAPI envelope store per adversarial review
fix(secrets): make DPAPI legacy migration race-safe and observable
fix(secrets): harden DPAPI envelope store per adversarial review
fix(secrets): make DPAPI legacy migration race-safe and observable
fix(secrets): harden DPAPI envelope store per adversarial review
feat(secrets): add Windows-native DPAPI secret backend (#464)
mogul commentedon Oct 3, 2026
Verified fixed by #464. The merged code re-checks that the target file is still the exact unmarked legacy plaintext before renaming the migrated DPAPI envelope, and skips migration if a concurrent writer changed it. The PR also added a regression test:
keychain-windows: migration does not clobber a rewrite that lands mid-encryption.