544c42a618
- metrics: LogIssue.Context on first-occurrence errors (≤11 lines, ≤400 chars/line, warns carry none); RedactLine sanitizer (password/token/api-key/bearer/64-hex) applied to everything shipped; FetchContainerLogTail - report: 16KB per-report context budget (lowest-count issues dropped first); log_tail_requests ACK flag → next report ships log_tails (200 lines, ≤64KB/app head-truncated, ordered, redacted); consume-once drain - tests: synthetic-window context capture, caps, redaction, budget order, consume-once, fetch-error skip Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
67 lines
2.1 KiB
Go
67 lines
2.1 KiB
Go
package metrics
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Part E — sanitization. Red-proof: gutting RedactLine (return s unchanged) must fail
|
|
// every case here with the secret visible in the failure message.
|
|
func TestRedactLine_SpecPatterns(t *testing.T) {
|
|
in := "password=hunter2 token: abc Bearer xyz"
|
|
got := RedactLine(in)
|
|
want := "password=[REDACTED] token: [REDACTED] Bearer [REDACTED]"
|
|
if got != want {
|
|
t.Fatalf("RedactLine(%q) = %q, want %q", in, got, want)
|
|
}
|
|
for _, secret := range []string{"hunter2", "abc", "xyz"} {
|
|
if strings.Contains(got, secret) {
|
|
t.Fatalf("secret %q shipped: %q", secret, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRedactLine_Hex64(t *testing.T) {
|
|
hex64 := strings.Repeat("ab12", 16) // 64 hex chars — restic repo password shape
|
|
in := "repo unlock with " + hex64 + " done"
|
|
got := RedactLine(in)
|
|
if strings.Contains(got, hex64) {
|
|
t.Fatalf("64-hex secret shipped: %q", got)
|
|
}
|
|
if !strings.Contains(got, "[REDACTED-HEX64]") {
|
|
t.Fatalf("expected [REDACTED-HEX64] marker, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestRedactLine_Variants(t *testing.T) {
|
|
cases := []struct{ in, want string }{
|
|
{"authorization: Bearer eyJhbGciOi.payload.sig", "authorization: [REDACTED]"},
|
|
{"api_key=sk-live-123", "api_key=[REDACTED]"},
|
|
{"API-KEY: verysecret", "API-KEY: [REDACTED]"},
|
|
{"apikey=whatever", "apikey=[REDACTED]"},
|
|
{"PASSWD: root123", "PASSWD: [REDACTED]"},
|
|
{"client secret=s3cr3t", "client secret=[REDACTED]"},
|
|
}
|
|
for _, c := range cases {
|
|
if got := RedactLine(c.in); got != c.want {
|
|
t.Errorf("RedactLine(%q) = %q, want %q", c.in, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Benign lines must pass through byte-identical — support usefulness over aggression.
|
|
func TestRedactLine_BenignUntouched(t *testing.T) {
|
|
cases := []string{
|
|
"connection refused to 10.0.0.5:5432",
|
|
"GET /api/keys 200 12ms", // "api/keys" is not "api_key"
|
|
"tokenizer initialized in 40ms", // "token" not followed by separator+value
|
|
"ERROR: NFS mount /mnt/media gone", // the CWA shape — must stay readable
|
|
"deadbeef", // short hex, not 64
|
|
}
|
|
for _, c := range cases {
|
|
if got := RedactLine(c); got != c {
|
|
t.Errorf("benign line mangled: %q -> %q", c, got)
|
|
}
|
|
}
|
|
}
|