From 244aed433d740c2978005fcad62b09efb260ff0b Mon Sep 17 00:00:00 2001 From: Aric Camarata Date: Mon, 26 Oct 2026 15:19:32 -0400 Subject: [PATCH] fix(ci): redact dash-prefixed secret values (D-1133) --- free/ci/internal/adhoc/argv.go | 3 ++- free/ci/internal/adhoc/argv_test.go | 30 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/free/ci/internal/adhoc/argv.go b/free/ci/internal/adhoc/argv.go index a4667e199..e82d25288 100644 --- a/free/ci/internal/adhoc/argv.go +++ b/free/ci/internal/adhoc/argv.go @@ -34,7 +34,8 @@ func secretValues(argv []string) [][]byte { switch { case hasValue && value != "": out = append(out, []byte(value)) - case !hasValue && strings.HasPrefix(a, "-") && i+1 < len(argv) && !strings.HasPrefix(argv[i+1], "-"): + case !hasValue && strings.HasPrefix(a, "-") && i+1 < len(argv): + // A secret value may itself begin with a dash. out = append(out, []byte(argv[i+1])) } } diff --git a/free/ci/internal/adhoc/argv_test.go b/free/ci/internal/adhoc/argv_test.go index e5322bc11..a6681f08a 100644 --- a/free/ci/internal/adhoc/argv_test.go +++ b/free/ci/internal/adhoc/argv_test.go @@ -42,3 +42,33 @@ func TestRecordedArgvIsBounded(t *testing.T) { t.Fatalf("recorded %d bytes", len(got)) } } + +func TestDashPrefixedSecretValuesAreRedacted(t *testing.T) { + for _, secret := range []string{"-randomValue123", "--randomValue123", "-x"} { + t.Run(secret, func(t *testing.T) { + argv := []string{"deploy", "--password", secret, "--verbose"} + if got := Recorded(redact.New(), argv); got != "deploy --password [redacted] --verbose" { + t.Errorf("recorded argv before registration = %q", got) + } + r := redact.New() + tooShort := Register(r, argv) + if len(secret) < redact.MinValueLen { + if tooShort != 1 { + t.Errorf("short secret count = %d, want 1", tooShort) + } + return + } + if tooShort != 0 { + t.Fatalf("short secret count = %d, want 0", tooShort) + } + if got := Recorded(r, argv); got != "deploy --password [redacted] --verbose" { + t.Errorf("recorded argv after registration = %q", got) + } + f := r.NewFilter() + got := string(append(f.Redact([]byte("line "+secret+" end\n")), f.Flush()...)) + if strings.Contains(got, secret) || !strings.Contains(got, redact.Placeholder) { + t.Errorf("job output was not redacted: %q", got) + } + }) + } +}