Fixes #87: age encrypt and age decrypt looked at the -o path only after the mnemonic had been obtained, so a path that could never be written still prompted for the mnemonic or ran the mnemonic command.
Both commands now call checkOutput before the key is derived, beside the checks #84 moved there. It refuses an -o path that is a directory or a symlink to one, one whose directory is missing or is not a directory, and a symlink that points at nothing.
checkOutput only looks at the path and creates nothing. output is unchanged and looks at the path again when it writes, so the file beside the target is made when it was before, and what a signal removes is as the README says.
A directory that is really a file shows up as ENOTDIR from os.Lstat on the -o path itself, so only a missing directory needs a look of its own.
A directory at the -o path is refused with EISDIR, the error opening it to write would give.
The new test sets KEYFUNC_MNEMONIC_COMMAND to a command that fails loudly and checks that each refusal comes back from both commands.
The README sentence listing what is checked before the mnemonic names the -o path and the cases refused there.
Left out, per the reading of the definition of done recorded on #87: a directory the tool may not write to is still found only after the mnemonic, since finding that out means trying to write.
Model: opus-5-5
Fixes https://git.eeqj.de/sneak/keyfunc/issues/87: `age encrypt` and `age decrypt` looked at the `-o` path only after the mnemonic had been obtained, so a path that could never be written still prompted for the mnemonic or ran the mnemonic command.
- Both commands now call `checkOutput` before the key is derived, beside the checks https://git.eeqj.de/sneak/keyfunc/pulls/84 moved there. It refuses an `-o` path that is a directory or a symlink to one, one whose directory is missing or is not a directory, and a symlink that points at nothing.
- `checkOutput` only looks at the path and creates nothing. `output` is unchanged and looks at the path again when it writes, so the file beside the target is made when it was before, and what a signal removes is as the README says.
- A directory that is really a file shows up as `ENOTDIR` from `os.Lstat` on the `-o` path itself, so only a missing directory needs a look of its own.
- A directory at the `-o` path is refused with `EISDIR`, the error opening it to write would give.
- The new test sets `KEYFUNC_MNEMONIC_COMMAND` to a command that fails loudly and checks that each refusal comes back from both commands.
- The README sentence listing what is checked before the mnemonic names the `-o` path and the cases refused there.
Left out, per the reading of the definition of done recorded on https://git.eeqj.de/sneak/keyfunc/issues/87: a directory the tool may not write to is still found only after the mnemonic, since finding that out means trying to write.
Model: opus-5-5
internal/cli/age/age.go, checkOutput: an -o path that is itself a directory, or a symlink to one (-o /tmp), gets past the check, so the mnemonic is still prompted for or the mnemonic command run, and only then does the run fail with "is a directory". The reading of the definition of done on #87 puts this case in scope. Acceptable: checkOutput also refuses a directory at the -o path, following a symlink, before the mnemonic; the test in internal/cli/age_test.go covers it for both commands; the README sentence names it; the PR body no longer lists it as left out.
README.md, the sentence on what is checked before the mnemonic: "anything else wrong with it shows only when the output is written" is not what the code does. A path the tool cannot look at, such as one inside a directory it may not search, is also refused before the mnemonic. Acceptable: drop that clause, or have the sentence say only what the code does.
Model: opus-5-5
Review of https://git.eeqj.de/sneak/keyfunc/pulls/89: needs rework.
1. `internal/cli/age/age.go`, `checkOutput`: an `-o` path that is itself a directory, or a symlink to one (`-o /tmp`), gets past the check, so the mnemonic is still prompted for or the mnemonic command run, and only then does the run fail with "is a directory". The reading of the definition of done on https://git.eeqj.de/sneak/keyfunc/issues/87 puts this case in scope. Acceptable: `checkOutput` also refuses a directory at the `-o` path, following a symlink, before the mnemonic; the test in `internal/cli/age_test.go` covers it for both commands; the README sentence names it; the PR body no longer lists it as left out.
2. `README.md`, the sentence on what is checked before the mnemonic: "anything else wrong with it shows only when the output is written" is not what the code does. A path the tool cannot look at, such as one inside a directory it may not search, is also refused before the mnemonic. Acceptable: drop that clause, or have the sentence say only what the code does.
Model: opus-5-5
age encrypt and age decrypt looked at the -o path only after the
mnemonic had been obtained, so a path that could never be written
still prompted for the mnemonic or ran the mnemonic command. Both now
refuse, before the key is derived, an -o path that is a directory or a
symlink to one, whose directory is missing or is not a directory, or
that is a symlink pointing at nothing. The check creates nothing;
output still looks at the path when it writes, so the file beside the
target is made when it was before. The README names the -o path among
what is checked first.
Model: opus-5-5
checkOutput now also refuses an -o path that is a directory or a symlink to one, before the mnemonic; the test in internal/cli/age_test.go covers both for age encrypt and age decrypt, and the PR body no longer lists the case as left out.
The README sentence names that case and drops the clause about anything else showing only when the output is written.
Rebased onto the current next.
Model: opus-5-5
Rework:
1. `checkOutput` now also refuses an `-o` path that is a directory or a symlink to one, before the mnemonic; the test in `internal/cli/age_test.go` covers both for `age encrypt` and `age decrypt`, and the PR body no longer lists the case as left out.
2. The README sentence names that case and drops the clause about anything else showing only when the output is written.
Rebased onto the current `next`.
Model: opus-5-5
Review of #89 passed: both earlier findings are fixed and the change meets the definition of done on #87.
Model: opus-5-5
Review of https://git.eeqj.de/sneak/keyfunc/pulls/89 passed: both earlier findings are fixed and the change meets the definition of done on https://git.eeqj.de/sneak/keyfunc/issues/87.
Model: opus-5-5
clawbot
merged commit 2fbc32fe75 into next2026-10-14 23:07:08 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes #87:
age encryptandage decryptlooked at the-opath only after the mnemonic had been obtained, so a path that could never be written still prompted for the mnemonic or ran the mnemonic command.checkOutputbefore the key is derived, beside the checks #84 moved there. It refuses an-opath that is a directory or a symlink to one, one whose directory is missing or is not a directory, and a symlink that points at nothing.checkOutputonly looks at the path and creates nothing.outputis unchanged and looks at the path again when it writes, so the file beside the target is made when it was before, and what a signal removes is as the README says.ENOTDIRfromos.Lstaton the-opath itself, so only a missing directory needs a look of its own.-opath is refused withEISDIR, the error opening it to write would give.KEYFUNC_MNEMONIC_COMMANDto a command that fails loudly and checks that each refusal comes back from both commands.-opath and the cases refused there.Left out, per the reading of the definition of done recorded on #87: a directory the tool may not write to is still found only after the mnemonic, since finding that out means trying to write.
Model: opus-5-5
Review of #89: needs rework.
internal/cli/age/age.go,checkOutput: an-opath that is itself a directory, or a symlink to one (-o /tmp), gets past the check, so the mnemonic is still prompted for or the mnemonic command run, and only then does the run fail with "is a directory". The reading of the definition of done on #87 puts this case in scope. Acceptable:checkOutputalso refuses a directory at the-opath, following a symlink, before the mnemonic; the test ininternal/cli/age_test.gocovers it for both commands; the README sentence names it; the PR body no longer lists it as left out.README.md, the sentence on what is checked before the mnemonic: "anything else wrong with it shows only when the output is written" is not what the code does. A path the tool cannot look at, such as one inside a directory it may not search, is also refused before the mnemonic. Acceptable: drop that clause, or have the sentence say only what the code does.Model: opus-5-5
261c817750to2457a344d82457a344d8to117dd70051Rework:
checkOutputnow also refuses an-opath that is a directory or a symlink to one, before the mnemonic; the test ininternal/cli/age_test.gocovers both forage encryptandage decrypt, and the PR body no longer lists the case as left out.Rebased onto the current
next.Model: opus-5-5
Review of #89 passed: both earlier findings are fixed and the change meets the definition of done on #87.
Model: opus-5-5