carlos / platform Public

services test: C46 — the deliver-line guard trusted unverified -h output

TestServicesDeliverLineIsARealCommand (added for C44) closed only one axis
of the class it was meant to close. It ignored the subprocess exit code
and matched flag names by strings.Contains, so:

  - a typo'd verb (e.g. `carlos secret set` instead of `secrets set`)
    exits nonzero and falls through to a DIFFERENT usage page — main.go's
    top-level usage() for a bad first word, a family's own bare usage
    constant for a bad sub-verb — which the test never rejected: it read
    whatever came back regardless of exit code.
  - the top-level usage page happens to contain "-app" as a substring
    inside an unrelated verb's description ("carlos version target -app
    <a> 0.5.0"), so a deliver line naming a nonexistent verb passed the
    flag search anyway.
  - a prefix-preserving flag rename (-app -> -application) would also
    silently pass: strings.Contains(helpOut, "-app") matches "-application"
    as a substring.

Both are the identical operator experience the original C43 bug produced:
a refusal while holding a secret that can never be shown again — so both
have to fail the guard, not just the flag-name axis.

Fix: two exact-match helpers, helpOutputIsForVerb (checks for the literal
"Usage of <verb>:" header flag.FlagSet's default Usage() prints — a typo'd
verb's fall-through page never contains it) and helpOutputHasFlag (matches
a flag as a whole line-leading token, never a substring).
TestServicesDeliverLineIsARealCommand now asserts the subprocess exited 0
AND that its output is the verb's own usage page BEFORE trusting anything
in it, then matches each flag by name. Two new permanent fixture-based
tests (TestHelpOutputIsForVerbRejectsFallThrough,
TestHelpOutputHasFlagRejectsPrefixPreservingRename) pin both axes without
needing a subprocess.

Both failure modes were also reproduced live against the real binary and
reverted (not part of this commit): a temporary typo in services.go's
deliverLine ("carlos secret set...") made the fixed test FAIL on the
nonzero-exit/fall-through-page check; a temporary rename of secrets set's
-app flag to -application (appconfig_cli.go) made it FAIL on the
exact-flag-name check, where the prior substring check would have passed.

Full gate clean: go build/vet ./..., gofmt -l (touched files), go test
./... (all 21 packages), GOOS=darwin go build ./.... No smart quotes
(U+201C/201D) introduced. Test-only change — no production code touched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016UimU7fAfeAoBMm6HKCPwQ
Paul Campbell pushed by paul@keymail.dev c55643021e675bf809d53336a71ad05d08ec3ab2 parent 235c71e
1 file changed, +108 −3
  • services_cli_test.go +108 −3
diff --git a/services_cli_test.go b/services_cli_test.go
index c27bf4d17..3a51719c4 100644
--- a/services_cli_test.go
+++ b/services_cli_test.go
@@ -540,6 +540,86 @@ func parseDeliverCommand(t *testing.T, line string) (verb, flags []string) {
return verb, flags
}
+// helpOutputIsForVerb reports whether helpOut is the `-h` output of EXACTLY
+// the verb it was asked about, not a fall-through to a different usage page
+// (C46, review). Every sub-verb's flag.NewFlagSet in this binary is named
+// after the verb itself (e.g. flag.NewFlagSet("secrets set", ...)), and
+// Go's flag package always prints "Usage of <name>:" as that FlagSet's own
+// -h header — so this line is a reliable anchor for "this really is that
+// verb's own usage", which a typo'd verb cannot produce: a typo'd TOP-LEVEL
+// word falls through to main.go's usage() (real English text that
+// coincidentally contains "-app" in "carlos version target -app <a> 0.5.0"
+// — the review's own example), and a typo'd SUB-verb falls through to that
+// family's own bare usage constant (secretsUsage etc.), neither of which
+// ever prints "Usage of <name>:" for the verb that was actually typed.
+func helpOutputIsForVerb(helpOut string, verb []string) bool {
+ want := "Usage of " + strings.Join(verb, " ") + ":"
+ for _, line := range strings.Split(helpOut, "\n") {
+ if strings.TrimSpace(line) == want {
+ return true
+ }
+ }
+ return false
+}
+
+// helpOutputHasFlag reports whether helpOut's flag.PrintDefaults output
+// lists EXACTLY flagName as a registered flag — a NAME match, never a
+// substring match (C46, review). Each flag prints as " -name" or " -name
+// type" at the START of its own line, so this checks that shape rather than
+// searching for flagName anywhere in the text: a strings.Contains check
+// would let a search for -app pass against a renamed -application flag,
+// since "-application" begins with the plain substring "-app" — the exact
+// false positive the review demonstrated.
+func helpOutputHasFlag(helpOut, flagName string) bool {
+ for _, line := range strings.Split(helpOut, "\n") {
+ trimmed := strings.TrimSpace(line)
+ if trimmed == flagName || strings.HasPrefix(trimmed, flagName+" ") {
+ return true
+ }
+ }
+ return false
+}
+
+// TestHelpOutputIsForVerbRejectsFallThrough — C46 (review), a permanent
+// fixture-based proof (no subprocess needed) that helpOutputIsForVerb
+// rejects the exact fall-through the review demonstrated against the real
+// binary: `carlos secret set -h` (typo'd verb) exits nonzero and prints the
+// TOP-LEVEL usage page, which itself contains "-app" as a substring inside
+// an unrelated verb's description — a page a naive Contains-based flag
+// search would have wrongly accepted as if it were secrets set's own -h.
+func TestHelpOutputIsForVerbRejectsFallThrough(t *testing.T) {
+ topLevelFallThrough := "usage: carlos <command> [flags]\n\n" +
+ " version target the semver ship counts iterations under: print it, or set\n" +
+ " it -- \"carlos version target -app <a> 0.5.0\"\n" +
+ " secrets sealed per-app secrets: set | unset | list | genkey\n"
+ if helpOutputIsForVerb(topLevelFallThrough, []string{"secrets", "set"}) {
+ t.Fatal("helpOutputIsForVerb matched a fall-through top-level usage page, " +
+ "which happens to contain \"-app\" as a substring in an unrelated verb's description")
+ }
+ real := "Usage of secrets set:\n -app string\n \tapp name\n"
+ if !helpOutputIsForVerb(real, []string{"secrets", "set"}) {
+ t.Fatal("helpOutputIsForVerb did not match the real usage page for the verb it names")
+ }
+}
+
+// TestHelpOutputHasFlagRejectsPrefixPreservingRename — C46 (review), a
+// permanent fixture-based proof that helpOutputHasFlag matches flag NAMES,
+// not substrings: the prior strings.Contains-based check would have let a
+// search for -app pass against a hypothetically renamed -application flag,
+// since "-application" begins with the plain substring "-app".
+func TestHelpOutputHasFlagRejectsPrefixPreservingRename(t *testing.T) {
+ renamed := "Usage of secrets set:\n -application string\n \tapp name\n"
+ if helpOutputHasFlag(renamed, "-app") {
+ t.Fatal("helpOutputHasFlag(-app) matched -application -- a substring match, not an exact one")
+ }
+ if !helpOutputHasFlag(renamed, "-application") {
+ t.Fatal("helpOutputHasFlag(-application) should match its own exact name")
+ }
+ if !helpOutputHasFlag("Usage of secrets set:\n -app string\n \tapp name\n", "-app") {
+ t.Fatal("helpOutputHasFlag(-app) should match its own exact name")
+ }
+}
+
// TestServicesDeliverLineIsARealCommand — C44 (review), the systemic fix
// for C43: a printed "deliver it" command whose flag did not exist
// (`carlos env set ... -secret`, env set has no such flag) sent an operator
@@ -553,6 +633,17 @@ func parseDeliverCommand(t *testing.T, line string) (verb, flags []string) {
// COMMAND, so the check that runs it is itself a subprocess, built through
// carlosTestEnv and run from a scratch cmd.Dir — the constraint this whole
// suite is written around.
+//
+// C46 (review): the first cut of this test trusted -h output it never
+// verified belonged to the verb it asked about, and matched flags by
+// substring -- both closed only ONE axis of the class C44 exists to close
+// (wrong flag), not the other (wrong verb: a typo'd verb falls through to a
+// DIFFERENT usage page, which is real English text that can coincidentally
+// contain a flag name -- see helpOutputIsForVerb's doc comment for the
+// review's own example). This now asserts the subprocess exited 0, asserts
+// the output is that verb's OWN usage page (helpOutputIsForVerb) BEFORE
+// trusting anything in it, and matches each flag by name
+// (helpOutputHasFlag), not by substring.
func TestServicesDeliverLineIsARealCommand(t *testing.T) {
bin := buildCarlos(t)
scratch := t.TempDir()
@@ -565,10 +656,24 @@ func TestServicesDeliverLineIsARealCommand(t *testing.T) {
cmd := exec.Command(bin, append(append([]string{}, verb...), "-h")...)
cmd.Env = env
cmd.Dir = scratch
- helpOut, _ := cmd.CombinedOutput() // -h exits 0 for a real verb; output is what matters either way
+ raw, err := cmd.CombinedOutput()
+ helpOut := string(raw)
+ // A real verb's own -h exits 0 (flag.ExitOnError treats -h as help
+ // requested, not an error). A typo'd verb -- top-level or sub-verb --
+ // exits nonzero and prints a DIFFERENT page; trusting that page's
+ // text at all, even before searching it, is the bug this guards.
+ if err != nil {
+ t.Fatalf("deliver line %q names `carlos %s -h`, which exited nonzero (%v) -- not a real verb:\n%s",
+ line, strings.Join(verb, " "), err, helpOut)
+ }
+ if !helpOutputIsForVerb(helpOut, verb) {
+ t.Fatalf("deliver line %q names `carlos %s`, but its -h output is not that verb's own "+
+ "usage page (no \"Usage of %s:\" line) -- a fall-through to a different page must fail here, "+
+ "not be searched:\n%s", line, strings.Join(verb, " "), strings.Join(verb, " "), helpOut)
+ }
for _, f := range flags {
- if !strings.Contains(string(helpOut), f) {
- t.Fatalf("deliver line %q names flag %q, which `carlos %s -h` does not list:\n%s",
+ if !helpOutputHasFlag(helpOut, f) {
+ t.Fatalf("deliver line %q names flag %q, which `carlos %s -h` does not list as a real flag:\n%s",
line, f, strings.Join(verb, " "), helpOut)
}
}