package capdiag import ( "strings" "testing" ) func TestSanitizeErrTextRedactsSecretsAndPaths(t *testing.T) { home := t.TempDir() ws := t.TempDir() in := "stdio plugin \"x\": command \"npx\" not found on PATH; PATH=\"" + home + "/bin:/usr/bin\" Bearer sk-secret-token " + ws + "/secret.env stderr: Authorization=Bearer abc.def" out := sanitizeErrTextWithPaths(in, ws, home) if strings.Contains(out, home) { t.Fatalf("home path leaked: %q", out) } if strings.Contains(out, "sk-secret") || strings.Contains(out, "abc.def") { t.Fatalf("token leaked: %q", out) } if strings.Contains(out, "PATH=\""+home) { t.Fatalf("PATH value leaked: %q", out) } if !strings.Contains(out, "") && !strings.Contains(out, "Bearer ") { t.Fatalf("expected redaction markers in %q", out) } } func TestSanitizeErrTextRedactsHTTPBodyCredentialShapes(t *testing.T) { // An HTTP transport error carries up to 4KB of raw response body; none of // its credential shapes may survive into the shareable report. in := `http 401: {"access_token":"sk-live-secret","x-api-key":"header-secret","password":"pw-secret"} Cookie: session=cookie-secret` out := sanitizeErrText(in) for _, leaked := range []string{"sk-live-secret", "header-secret", "pw-secret", "cookie-secret"} { if strings.Contains(out, leaked) { t.Fatalf("credential leaked %q in %q", leaked, out) } } if !strings.Contains(out, "http 401") { t.Fatalf("status context lost: %q", out) } } func TestSanitizeErrTextRedactsVendorTokenShapes(t *testing.T) { cases := []struct{ name, in, leaked string }{ {"jwt", "stderr: jwt eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.sflKxwRJSMeKKF2QT4fwpMeJf36POk6yJVadQssw5c", "eyJhbGciOiJIUzI1NiJ9"}, {"github", "stderr: fatal: ghp_abcdefghijklmnopqrstuvwxyz123456", "ghp_abcdefghijklmnopqrstuvwxyz123456"}, {"openai", "stderr: invalid key sk-proj-abcdefghijklmnop1234", "sk-proj-abcdefghijklmnop1234"}, {"colon-header", "http 403: x-api-key: header-secret-value", "header-secret-value"}, {"set-cookie", "http 401: Set-Cookie: sid=abc123def456ghi; Path=/", "abc123def456ghi"}, } for _, tc := range cases { out := sanitizeErrText(tc.in) if strings.Contains(out, tc.leaked) { t.Fatalf("%s: credential leaked in %q", tc.name, out) } } }