Check the -o path before looking for the mnemonic (closes #87)
check / check (push) Waiting to run

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
This commit is contained in:
2026-10-14 20:34:02 +00:00
committed by sneak
parent 5582c02edf
commit 2457a344d8
3 changed files with 106 additions and 9 deletions
+38
View File
@@ -108,6 +108,44 @@ func TestAnInputFileThatCannotBeOpenedIsRefused(t *testing.T) {
}
}
// The mnemonic command fails in these runs, so the refusal of the -o
// path shows that it was looked at before the mnemonic was looked for.
func TestAnOutputPathThatCannotBeWrittenIsRefused(t *testing.T) {
t.Setenv(mnemonic.CommandVariable, failingMnemonicCommand)
plain := written(t, "notes.txt", "the secret\n")
directory := t.TempDir()
missing := filepath.Join(directory, "missing")
dangling := filepath.Join(directory, "dangling")
require.NoError(t, os.Symlink(missing, dangling))
toDirectory := filepath.Join(directory, "to-directory")
require.NoError(t, os.Symlink(t.TempDir(), toDirectory))
refusals := map[string]error{
filepath.Join(missing, "notes.age"): fs.ErrNotExist,
filepath.Join(plain, "notes.age"): syscall.ENOTDIR,
dangling: fs.ErrNotExist,
directory: syscall.EISDIR,
toDirectory: syscall.EISDIR,
}
for name, refusal := range refusals {
out, err := execute(t, "age", "encrypt", "-o", name, plain)
require.ErrorIs(t, err, refusal, name)
require.Empty(t, out, name)
out, err = execute(t, "age", "decrypt", "-o", name, plain)
require.ErrorIs(t, err, refusal, name)
require.Empty(t, out, name)
}
// Nothing was made beside the two symlinks.
left, err := os.ReadDir(directory)
require.NoError(t, err)
require.Len(t, left, 2)
}
func TestAFileForAnotherKeyIsRefused(t *testing.T) {
t.Setenv(mnemonic.Variable, example())