Files
zero/internal/secrets/boundary_test.go
KRATOS a4279662b9 redaction: catch compound secret keys + non-bearer auth schemes; stop scanner over-redaction (#249)
* redaction: catch compound secret keys + non-bearer auth schemes; stop scanner over-redaction

Hardens the secret-redaction last line of defense after an audit of
internal/redaction + internal/secrets.

1. Compound secret keys are now redacted. IsSensitiveKey was exact-match only, so
   db_password, session_secret, stripe_secret_key, auth_token, csrf_token,
   ssh_private_key, backup_api_key, etc. passed through in cleartext. A new
   keyLooksSensitive applies conservative structural heuristics (secret /
   password / passphrase / credential / apikey segments; a singular trailing
   "_token"; api_key / private_key pairs). Critically, the agent's token-COUNT
   fields (max_tokens, prompt_tokens, token_count) and ordinary *_key fields
   (primary_key, public_key, cache_key) are deliberately NOT redacted.

2. Authorization headers with non-bearer/basic schemes were leaking. The header
   pattern now also covers token, apikey, digest, negotiate, oauth, and
   aws4-hmac-sha256.

3. The secrets scanner over-redacted ordinary text: its prefixed patterns (sk-,
   gh*, AIza, AKIA, xox, github_pat, jwt) lacked the word boundaries that
   internal/redaction already uses, so kebab-case words like
   "task-management-and-coordination" matched as a fake key. Added \b anchors.

Design note: a free-text "key: value" colon pattern was prototyped but dropped --
it mangled "file.go:line: message" output (a filename's "secret" segment looked
like a sensitive key), the same over-redaction class fix #3 addresses. The
structured paths (RedactValue for maps/structs, plus assign/JSON/query forms in
RedactString) are covered; free-text colon-form remains out of scope.

Tests: compound-key + token-count-safety matrix, auth-scheme redaction, the
file:line guard, and scanner boundary positives/negatives.

* redaction: redact the full auth-header value for multi-part schemes

The broadened header pattern captured only the first token after the scheme, so
parameterized schemes (Digest "…, response=…", AWS4-HMAC-SHA256 "…, Signature=…")
left the actual secret param visible. Redact the entire value to end of line; the
scheme name is kept. Adds tests with realistic Digest + AWS SigV4 headers.

Addresses review feedback.
2026-06-18 20:01:25 +05:30

39 lines
1.3 KiB
Go

package secrets
import "testing"
// Kebab-case words must NOT be redacted: "sk-" inside "task"/"ask"/"risk"/etc.
// is not an openai_key. This is the over-redaction the \b anchors fix.
func TestScan_NoOverRedactionOnKebabWords(t *testing.T) {
clean := []string{
"see task-management-and-coordination-plan for the roadmap",
"we should ask-the-user-about-this-before-proceeding now",
"the risk-assessment-and-mitigation-strategy-doc is ready",
"disk-usage-monitoring-and-alerting-subsystem looks good",
"desk-booking-and-reservation-management-flow updated",
}
for _, c := range clean {
red, f := Redact(c)
if len(f) != 0 || red != c {
t.Errorf("over-redacted %q -> %q (findings=%d)", c, red, len(f))
}
}
}
// Real secrets preceded by a delimiter (space, =, :, quote, start) still match —
// the \b is satisfied by all of those, so the fix loses no real coverage.
func TestScan_RealSecretsStillCaught(t *testing.T) {
cases := []string{
"export OPENAI_API_KEY=sk-proj-abcdefghijklmnopqrstuvwx",
`token: "sk-abcdefghijklmnopqrstuvwxyz"`,
"key is sk-svcacct-abcdefghijklmnopqrstuvwx at the end",
"github_pat_11ABCDEFG0abcdefghijklmnopqrst",
"creds AKIAIOSFODNN7EXAMPLE here",
}
for _, c := range cases {
if _, f := Redact(c); len(f) == 0 {
t.Errorf("real secret NOT caught in %q", c)
}
}
}