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: Create key and seed files with owner-only permissions - #284
quill generate (PEM and seed files) and quill decrypt-pem wrote secret material with plain std::fs::write:
File modes depended on the umask, so with the common 022 the files ended up 0644.
Symbolic links at the output path were followed. The "exists and overwrite is not set" check used Path::exists(), which returns false for a dangling symlink.
This PR adds a write_secret_file helper in src/main.rs and routes all three write sites through it. The helper:
refuses to write if the output path is a symbolic link, whether or not its target exists;
without an overwrite flag, errors on any existing entry; with one, replaces only a regular file;
creates the file with create_new (O_EXCL) and, on Unix, mode 0o600.
The early checks in generate now use symlink_metadata, so a dangling link is rejected before the password prompt.
Behavior change: decrypt-pem still overwrites an existing regular file, but now refuses to write through a symlink.
Testing
New test generate_secret_files checks the 0600 mode (both on creation and on overwrite) and checks that a dangling symlink is rejected with and without --overwrite-pem-file.
`quill generate` and `quill decrypt-pem` wrote secret material with
`std::fs::write`, so file modes depended on the umask (typically 0644)
and symbolic links at the output path were followed. The existence check
used `Path::exists()`, which reports false for a dangling symlink.
Route all secret writes through a helper that refuses symlinks, only
replaces regular files when overwriting is requested, and creates the
file with `create_new` and mode 0600 on Unix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Critical overwrite paths can delete an existing secret if opening its replacement fails or replace a seed before rejecting a PEM symlink, causing data loss and seed/PEM inconsistency.
- `generate` checks both the seed and PEM destinations before writing
either, so a rejected PEM path can no longer clobber an existing seed.
- Overwriting writes to a 0600 temp file in the same directory and then
renames it over the destination, so the old secret survives any
failure before the rename.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Temporary filenames include the full destination basename, causing otherwise valid output paths to fail with ENAMETOOLONG during overwrite generation and decrypt-pem file output.
Test that overwrite repairs existing file permissions
tests/output/root.rs:150
The overwrite case starts with files that are already 0600, so it would also pass if overwriting regressed to std::fs::write and retained insecure permissions on an existing file. Set both files to 0644 before overwriting and assert 0600 for both afterward to cover the permission-repair behavior this PR introduces.
► src/main.rs Add check_secret_path and create_secret_file helpers for secure secret file handling ► src/main.rs Implement write_secret_file function to safely write secret material with overwrite handling ► src/main.rs Implement create_secret_file to create files with owner-only permissions and safe write/rollback
Enhancement
► tests/output/root.rs Add generate_secret_files test to verify secret file creation, permissions, overwrite behavior, and symlink handling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
quill generate(PEM and seed files) andquill decrypt-pemwrote secret material with plainstd::fs::write:022the files ended up0644.Path::exists(), which returnsfalsefor a dangling symlink.This PR adds a
write_secret_filehelper insrc/main.rsand routes all three write sites through it. The helper:create_new(O_EXCL) and, on Unix, mode0o600.The early checks in
generatenow usesymlink_metadata, so a dangling link is rejected before the password prompt.Behavior change:
decrypt-pemstill overwrites an existing regular file, but now refuses to write through a symlink.Testing
generate_secret_fileschecks the0600mode (both on creation and on overwrite) and checks that a dangling symlink is rejected with and without--overwrite-pem-file.generatetests pass.🤖 Generated with Claude Code