Check arguments before looking for the mnemonic (closes #79)
check / check (push) Waiting to run

The key index, --words, the --to recipients and the input file of
age encrypt and age decrypt were checked only after the mnemonic had
been obtained, so a run that could not succeed still prompted for it or
ran the mnemonic command. options.Index now refuses an index above
derive.MaxIndex, which covers every command group; the mnemonic command
calls the new childmnemonic.CheckWords first; age encrypt reads its
recipients with the new agekey.Recipients, and both age commands open
their input, before the key is derived. The README says which checks
come first.

Judgement call: the -o path is still looked at after the mnemonic.

Model: opus-5-5
This commit was merged in pull request #84.
This commit is contained in:
2026-10-14 21:05:48 +02:00
parent b02f502928
commit d1ed234bc9
9 changed files with 162 additions and 62 deletions
+28 -5
View File
@@ -34,6 +34,14 @@ const (
twentyFour = 24
)
// tooLarge is the smallest key index with no hardened child.
const tooLarge = "2147483648"
// failingMnemonicCommand fails loudly, so a run that looks for the
// mnemonic ends with that failure. A run that ends with the refusal of
// an argument instead shows that the argument was checked first.
const failingMnemonicCommand = "echo the mnemonic was looked for >&2; exit 1"
// example returns the mnemonic the README gives its test vectors for:
// eleven abandons and about.
func example() string {
@@ -99,12 +107,25 @@ func TestThePrivateKeyMatchesThePublicOne(t *testing.T) {
require.Equal(t, vectorOne, back)
}
// The mnemonic command fails in these runs, so the refusal of the index
// shows that it was checked before the mnemonic was looked for. The age
// commands have a test of their own.
func TestAnIndexWithNoHardenedChildIsRefused(t *testing.T) {
t.Setenv(mnemonic.Variable, example())
t.Setenv(mnemonic.CommandVariable, failingMnemonicCommand)
out, err := execute(t, "ssh", "pub", "-n", "2147483648")
require.ErrorIs(t, err, derive.ErrIndexTooLarge)
require.Empty(t, out)
for _, args := range [][]string{
{subcommand, "pub", "-n", tooLarge},
{subcommand, "priv", "-n", tooLarge},
{subcommand, installing, "-n", tooLarge, host},
{subcommand, "to", "-n", tooLarge, host},
{"mnemonic", "-n", tooLarge},
} {
name := strings.Join(args, " ")
out, err := execute(t, args...)
require.ErrorIs(t, err, derive.ErrIndexTooLarge, name)
require.Empty(t, out, name)
}
}
func TestAnIndexWithALeadingZeroIsDecimal(t *testing.T) {
@@ -181,8 +202,10 @@ func TestAChildMnemonicIsPrinted(t *testing.T) {
require.NotEqual(t, short, next)
}
// The mnemonic command fails in this run, so the refusal of the length
// shows that it was checked before the mnemonic was looked for.
func TestALengthTheToolDoesNotOfferIsRefused(t *testing.T) {
t.Setenv(mnemonic.Variable, example())
t.Setenv(mnemonic.CommandVariable, failingMnemonicCommand)
out, err := execute(t, "mnemonic", "--words", "15")
require.ErrorIs(t, err, childmnemonic.ErrWordCount)