Skip to content

[Hackathon] fix(credentials): restrict the atomic-write temp file's Windows ACL before rename, not after #366

Closed as not planned

Description

@OceanRanger

Verified against (commit SHA or released version)

commit 988c035 (v0.14.0, current main)

Discord Username / User ID

ocean_ranger — preferred contact: PR comments or GitHub (Discord available but not actively monitored)

What does this improvement do?

writeCredentialsAtomic (src/lib/credentials.ts:338-361) writes the temp file with { mode: 0o600 } — a no-op on Windows, where the file instead inherits the parent directory's ACLs — then renames it over the credentials file, and only afterwards calls ensureRestrictiveMode(path), which runs icacls to restrict the final file to OWNER RIGHTS.

Two consequences on Windows:

  1. Write window: between writeFileSync(tmp) and the post-rename icacls, the temp file holds the plaintext API key under the directory's inherited ACLs (on typical setups Authenticated Users / BUILTIN\Users have read/modify via inherited profile-dir ACEs — verified locally on Windows 10 10.0.19045).
  2. Crash residue (the stronger problem): if the process is killed (Ctrl-C, crash, power loss) between writeFileSync and renameSync, the unlinkSync cleanup never runs, so ~/.testsprite/credentials.tmp.<pid> — a complete plaintext copy of the API key with broad inherited ACLs — is left on disk indefinitely. The same kill between renameSync and ensureRestrictiveMode leaves the final credentials file itself under inherited ACLs until the next successful write repairs it.

The end state after a successful write is correct (verified: the final ACL is exactly OWNER RIGHTS:(F)), so this is about the window and the crash paths, not the steady state. #356 recently repaired ACL inheritance in this same file, so this file's Windows ACL behavior is already treated as a real concern.

Details / implementation notes

Reproduction

By code inspection of the ordering above. The crash residue is observable by killing a testsprite auth configure run between the write and the rename (a concurrent-writer loop widens the window), then inspecting icacls on the leftover .tmp.<pid> file. A unit test with an injectable fs can pin the required ordering deterministically.

Suggested approach

  • Restrict the temp file's ACL (the same icacls sequence ensureRestrictiveMode uses) before renameSync, so the moved-in file is born restricted; the existing post-rename pass can stay as a belt-and-suspenders check.
  • Best-effort extras: clean up stale .tmp.<pid> siblings for the current pid on the next write, and/or surface leftover temp files in doctor's credential check.

Happy to implement this with tests once the issue is confirmed and labeled; will claim with /assign. One concern per PR: the ordering of the ACL restriction only. No flag or output-format changes.

Confirmations

  • I have searched existing issues and this is not a duplicate.
  • I have provided my Discord identity above for reward coordination.

Activity

  1. OceanRanger commented on Oct 19, 2026

    @OceanRanger
    Author

    /assign

  2. testsprite-hob commented on Oct 19, 2026

    @testsprite-hob

    Thanks for the interest, @OceanRanger! This issue hasn't been accepted for implementation yet — see the triage comment (a maintainer will label it accepted once it is). Feel free to check back, or ping us on Discord if it's been quiet.

  3. OceanRanger commented on Oct 22, 2026

    @OceanRanger
    Author

    /assign

  4. testsprite-hob commented on Oct 22, 2026

    @testsprite-hob

    Thanks for the interest, @OceanRanger! This issue hasn't been accepted for implementation yet — see the triage comment (a maintainer will label it accepted once it is). Feel free to check back, or ping us on Discord if it's been quiet.

  5. fyZhang66 commented on Oct 22, 2026

    @fyZhang66
    Contributor

    Thanks @OceanRanger. The ordering is as you describe, but it takes a crash inside a millisecond window on a shared Windows machine, and no one has hit it in practice. Closing as not planned.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    hackathonCLI hackathon submissions

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions