secret decrypt and secret encrypt truncate the --output file before they can fail, and --input equal to --output destroys the input #143

Open
opened 2026-10-13 00:30:25 +02:00 by clawbot · 0 comments
Collaborator

Severity: major. A failing command empties a file the user named, and giving the same file as input and output silently replaces a document with the encryption of nothing. Nothing stored in the vault is affected.

What happens. Both commands create (truncate) the output file before reading any input or checking the key.

printf precious | tee out.txt
secret decrypt mykey -i wrongkey.age -o out.txt     # Error: ... no identity matched any of the recipients
wc -c out.txt                                       # 0
cp doc.txt same.txt
secret encrypt mykey -i same.txt -o same.txt        # exit 0; same.txt is an age file with an empty payload
secret decrypt mykey -i same.age -o same.age        # Error: ... failed to read intro: EOF; same.age is 0 bytes

Where.

file, err := cli.fs.Create(outputFile)
(encrypt),
file, err := cli.fs.Create(outputFile)
and
decryptor, err := age.Decrypt(input, identity)
(decrypt creates the output, then age.Decrypt fails on the header).

Correct behaviour. The README documents neither case. Recommended: write to a temporary file next to the output and rename it into place only on success, as secret.WriteFileAtomic does inside the state directory; that also makes input and output the same file safe. Refusing -i f -o f outright (os.SameFile) is the simpler alternative.

Definition of done

  • A failed encrypt or decrypt leaves an existing output file unchanged and creates no empty one.
  • -i f -o f either fails before touching f or produces the right result.
  • Tests for both cases fail before the fix.

Model: fable-5-1

Severity: major. A failing command empties a file the user named, and giving the same file as input and output silently replaces a document with the encryption of nothing. Nothing stored in the vault is affected. **What happens.** Both commands create (truncate) the output file before reading any input or checking the key. ``` printf precious | tee out.txt secret decrypt mykey -i wrongkey.age -o out.txt # Error: ... no identity matched any of the recipients wc -c out.txt # 0 cp doc.txt same.txt secret encrypt mykey -i same.txt -o same.txt # exit 0; same.txt is an age file with an empty payload secret decrypt mykey -i same.age -o same.age # Error: ... failed to read intro: EOF; same.age is 0 bytes ``` **Where.** https://git.eeqj.de/sneak/secret/src/commit/ef0ae90768c8b18a829033b3ebd4cf98fcaeb3d1/internal/cli/crypto.go#L195 (encrypt), https://git.eeqj.de/sneak/secret/src/commit/ef0ae90768c8b18a829033b3ebd4cf98fcaeb3d1/internal/cli/crypto.go#L285 and https://git.eeqj.de/sneak/secret/src/commit/ef0ae90768c8b18a829033b3ebd4cf98fcaeb3d1/internal/cli/crypto.go#L295 (decrypt creates the output, then `age.Decrypt` fails on the header). **Correct behaviour.** The README documents neither case. Recommended: write to a temporary file next to the output and rename it into place only on success, as `secret.WriteFileAtomic` does inside the state directory; that also makes input and output the same file safe. Refusing `-i f -o f` outright (`os.SameFile`) is the simpler alternative. **Definition of done** - A failed encrypt or decrypt leaves an existing output file unchanged and creates no empty one. - `-i f -o f` either fails before touching `f` or produces the right result. - Tests for both cases fail before the fix. Model: fable-5-1
sneak was assigned by clawbot 2026-10-13 00:30:25 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/secret#143