From 0d5b598cd087ba8956dbb4df010bd1f7e146b17e Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Mon, 22 Dec 2025 22:00:49 +0100 Subject: [PATCH] cmd/age: warn about duplicate command-line arguments Fixes #284 --- cmd/age/age.go | 24 ++++++++++++++++ cmd/age/testdata/duplicates.txt | 49 +++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 cmd/age/testdata/duplicates.txt diff --git a/cmd/age/age.go b/cmd/age/age.go index 9b51f6b..5b1dfc8 100644 --- a/cmd/age/age.go +++ b/cmd/age/age.go @@ -10,10 +10,12 @@ import ( "flag" "fmt" "io" + "iter" "os" "path/filepath" "regexp" "runtime/debug" + "slices" "strings" "filippo.io/age" @@ -215,6 +217,16 @@ func main() { } } + warnDuplicates(slices.Values(recipientFlags), "recipient") + warnDuplicates(slices.Values(recipientsFileFlags), "recipients file") + warnDuplicates(func(yield func(string) bool) { + for _, f := range identityFlags { + if f.Type == "i" && !yield(f.Value) { + return + } + } + }, "identity file") + var inUseFiles []string for _, i := range identityFlags { if i.Type != "i" { @@ -550,3 +562,15 @@ func absPath(name string) string { } return name } + +func warnDuplicates(s iter.Seq[string], name string) { + seen := make(map[string]bool) + warned := make(map[string]bool) + for e := range s { + if seen[e] && !warned[e] { + warningf("duplicate %s %q", name, e) + warned[e] = true + } + seen[e] = true + } +} diff --git a/cmd/age/testdata/duplicates.txt b/cmd/age/testdata/duplicates.txt new file mode 100644 index 0000000..0612cbe --- /dev/null +++ b/cmd/age/testdata/duplicates.txt @@ -0,0 +1,49 @@ +# Test duplicate recipient detection +age -r age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85 -r age1yv2sxrjnyymx4qkn3ehdd5zdq4xnrea5ljm4hsmgxn5tpv985anqkrwqa7 -r age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85 -o test.age input +stderr 'warning: duplicate recipient "age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85"' + +# Test duplicates separated by different argument +age -r age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85 -a -r age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85 -o test2.age input +stderr 'warning: duplicate recipient "age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85"' + +# Test duplicate recipients file detection +age -R recipients1.txt -R recipients2.txt -R recipients1.txt -o test3.age input +stderr 'warning: duplicate recipients file "recipients1.txt"' + +# Test duplicates separated by output flag +age -R recipients1.txt -o test4.age -R recipients1.txt input +stderr 'warning: duplicate recipients file "recipients1.txt"' + +# First create an encrypted file for decrypt tests +age -r age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85 -o encrypted.age input + +# Test duplicate identity file detection (decrypt mode) +age -d -i key1.txt -i key2.txt -i key1.txt encrypted.age +stderr 'warning: duplicate identity file "key1.txt"' + +# Test duplicates separated by different argument in decrypt mode +age -d -i key1.txt -o test.out -i key1.txt encrypted.age +stderr 'warning: duplicate identity file "key1.txt"' + +# Test no warning when no duplicates +age -r age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85 -r age1yv2sxrjnyymx4qkn3ehdd5zdq4xnrea5ljm4hsmgxn5tpv985anqkrwqa7 -o test5.age input +! stderr 'warning: duplicate' + +# Test multiple duplicates (same value repeated 3+ times) +age -r age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85 -r age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85 -r age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85 -o test6.age input +stderr 'warning: duplicate recipient "age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85"' + +-- input -- +test data +-- recipients1.txt -- +age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85 +-- recipients2.txt -- +age1yv2sxrjnyymx4qkn3ehdd5zdq4xnrea5ljm4hsmgxn5tpv985anqkrwqa7 +-- key1.txt -- +# created: 2025-12-22T22:06:22+01:00 +# public key: age1jh0u9yam5jhql7vy4acz90p0rhqcr0rcyu4zm85s9sdcwrs7rfgshtdg85 +AGE-SECRET-KEY-1WRM2S8SP3XSKLLXAXS489EXZNKCKRZWYQLQ8D2NRNQWCVAPSMA9SC5JWZQ +-- key2.txt -- +# created: 2025-12-22T22:06:27+01:00 +# public key: age1yv2sxrjnyymx4qkn3ehdd5zdq4xnrea5ljm4hsmgxn5tpv985anqkrwqa7 +AGE-SECRET-KEY-1WZ3MRPAWEWR4DG474H460MXX7J2T0TEYNJ0SKQDMKP02JU7UJ9UQFGLZCE