Files
Mikhail ChusavitinandClaude Sonnet 5 f38fb2de69 feat(privacy): detect an already-sanitized source
Scan now reports PrivacyScan.Sanitized {detected, markers, strong, files,
evidence}. SanitizationMarkers recognises a value slot filled with one
repeated placeholder + separators (xxxxx.xxxx.xx, x@xxxx.xxxx.xx,
000.00.00.0, a decoy timezone) - it matches the shape, not the literal "x",
so evolving the redaction mechanism still trips it.

detected requires corroboration: strong>=2, or strong>=1 && markers>=3, or
markers>=4. A single filler-looking token is reported (markers:1) but never
asserted as sanitized, so a partial future pass or a coincidence does not
read as "done". 0.0.0.0 / 000 / UTC / Etc/UTC are too plausibly intentional
and do not count.

UI: the Customer-data panel shows "файл уже обезличен" and hides the
sanitize button when detected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jDYM1nnoZZ3vFz23DDaV1
2026-09-03 10:02:02 +03:00

70 lines
2.2 KiB
Go

package privacy
import "testing"
func TestIsGenericFiller(t *testing.T) {
yes := []string{"xxxxx.xxxx.xx", "x@xxxx.xxxx.xx", "xxxxxx/xxxxxx", "000.00.00.0", "00.00.00.00"}
no := []string{"0.0.0.0", "000", "corp.acme.ru", "192.0.2.0", "10.20.30.40", "example.local", "x", "xx"}
for _, s := range yes {
if !isGenericFiller(s) {
t.Errorf("isGenericFiller(%q) = false, want true", s)
}
}
for _, s := range no {
if isGenericFiller(s) {
t.Errorf("isGenericFiller(%q) = true, want false", s)
}
}
}
func TestSanitizationMarkers_Line(t *testing.T) {
strong := 0
for _, m := range SanitizationMarkers("domain xxxxxxx.xxxxx") {
if m.Strong {
strong++
}
}
if strong == 0 {
t.Fatal("redacted resolv domain not detected as a strong marker")
}
if len(SanitizationMarkers("domain corp.acme.ru")) != 0 {
t.Fatal("real domain flagged as a sanitization marker")
}
if len(SanitizationMarkers("gateway 0.0.0.0 unset")) != 0 {
t.Fatal("0.0.0.0 flagged as a marker")
}
}
func TestScan_SanitizedDetectionNeedsCorroboration(t *testing.T) {
// One lone filler-looking token: reported, but not "detected".
rep := Scan([]File{{Path: "a.log", Content: []byte("host is xxxxx.xxxx.xx today\nnothing else\n")}})
if rep == nil || rep.Sanitized == nil {
t.Fatal("expected a sanitized report")
}
if rep.Sanitized.Detected {
t.Fatalf("a single marker must not read as sanitized: %+v", rep.Sanitized)
}
if rep.Sanitized.Markers != 1 {
t.Fatalf("markers = %d, want 1", rep.Sanitized.Markers)
}
// Two strong markers in two files -> detected.
rep2 := Scan([]File{
{Path: "resolv.conf", Content: []byte("domain xxxxxxx.xxxxx\n")},
{Path: "nsupdate_temp", Content: []byte("update add xxxxx.xxxxxxx.xx 0 A 10.0.0.1\n")},
})
if rep2 == nil || rep2.Sanitized == nil || !rep2.Sanitized.Detected {
t.Fatalf("two-file sanitized dump not detected: %+v", rep2.Sanitized)
}
}
func TestScan_UnsanitizedHasNoSanitizedReport(t *testing.T) {
rep := Scan([]File{{Path: "resolv.conf", Content: []byte("domain corp.acme.ru\nnameserver 10.0.0.1\n")}})
if rep == nil {
t.Fatal("nil report")
}
if rep.Sanitized != nil {
t.Fatalf("clean dump got a sanitized report: %+v", rep.Sanitized)
}
}