Location: src/commands/secrets.ts:109-110 in secrets(): const out = opts.output ?? '.env'; await writeFile(out, serializeEnv(bundle)).
Kind: secrets written to disk with permissive mode; symlink follow on write.
Evidence: the bundle is every secret for the branch (GET /projects/:id/secrets via fetchSecretBundle). writeFile is called with no mode and no chmod, so a freshly created .env is 0644. The command then prints “credentials must never be committed” and gitignores the file, guarding git but not the filesystem. Contrast src/commands/storage.ts:127-129, which preserves a 0600 mode when replacing a download. writeFile also follows symlinks, so --output pointed at a link writes through it, while the repo already has writeFileAtomicSync/resolveThroughSymlink in src/util.ts for exactly this.
Impact: every project credential readable by any local user on creation.
A fix would touch: the write in secrets() (mode 0600 on create, or reuse writeFileAtomicSync), and test/run-secrets.test.ts or a new test asserting the mode.
Found by Yun Tianming: nightly sweep 2026-09-23-1000 at 3cac58008fcb. Report only; no code was changed for this finding.
🤖 Generated with Claude Code
Issue cleanup — 2026-09-29
Duplicate of cli#240. The underlying issue remains open there; this closure does not mean it has been fixed. The original report is preserved above.
Location:
src/commands/secrets.ts:109-110insecrets():const out = opts.output ?? '.env'; await writeFile(out, serializeEnv(bundle)).Kind: secrets written to disk with permissive mode; symlink follow on write.
Evidence: the bundle is every secret for the branch (
GET /projects/:id/secretsviafetchSecretBundle).writeFileis called with nomodeand nochmod, so a freshly created.envis 0644. The command then prints “credentials must never be committed” and gitignores the file, guarding git but not the filesystem. Contrastsrc/commands/storage.ts:127-129, which preserves a 0600 mode when replacing a download.writeFilealso follows symlinks, so--outputpointed at a link writes through it, while the repo already haswriteFileAtomicSync/resolveThroughSymlinkinsrc/util.tsfor exactly this.Impact: every project credential readable by any local user on creation.
A fix would touch: the write in
secrets()(mode 0600 on create, or reusewriteFileAtomicSync), andtest/run-secrets.test.tsor a new test asserting the mode.Found by Yun Tianming: nightly sweep
2026-09-23-1000at3cac58008fcb. Report only; no code was changed for this finding.🤖 Generated with Claude Code
Issue cleanup — 2026-09-29
Duplicate of cli#240. The underlying issue remains open there; this closure does not mean it has been fixed. The original report is preserved above.