package privacy import ( "strings" "testing" ) // Fixtures use "acme.ru" as a stand-in customer domain. The RFC 2606 // "example.*" names are on the allowlist (they are the sanitization target), // so they cannot double as the customer placeholder in detection tests. func TestScan_DetectsAndClassifies(t *testing.T) { files := []File{ {Path: "onekeylog/configuration/conf/resolv.conf", Content: []byte( "domain corp.acme.ru\nsearch corp.acme.ru\nnameserver 10.10.0.1\n")}, {Path: "onekeylog/configuration/conf/activedir.conf", Content: []byte( "racdomain=corp.acme.ru\nadfilterdc1=10.10.0.5\nrolegroup1name=cab-gr-CI00363277-x86bmc\n")}, {Path: "onekeylog/configuration/conf/BMC1/misccfg.ini", Content: []byte( "TimeZone=Europe/Moscow\nSELTimeUTCOffset=180\n")}, {Path: "onekeylog/log/audit.log", Content: []byte( "2026-08-24T11:47:34+03:00 login admin from ntp01.acme.ru ok\n" + "contact ops@acme.ru for access\n" + "outbound 45.32.10.7 established\n" + "internal 192.168.31.4 and 10.1.2.3 and doc 203.0.113.9\n")}, {Path: "onekeylog/runningdata/rundatainfo.log", Content: []byte( "Mon Aug 24 11:58:48 MSK 2026\n")}, // Vendor factory template - must be skipped entirely. {Path: "onekeylog/configuration/conf/ntp_auto.conf", Content: []byte( "server ntp.should-not-match.ru\n")}, {Path: "onekeylog/bin/blob", Content: []byte{0x00, 0x01, 0x02, 0xff}}, } rep := Scan(files) if rep == nil { t.Fatal("nil report") } if rep.FilesScanned != 5 { t.Fatalf("FilesScanned = %d, want 5 (binary + template skipped)", rep.FilesScanned) } has := func(cat, match string) bool { for _, f := range rep.Findings { if f.Category == cat && f.Match == match { return true } } return false } mustHave := []struct{ cat, match string }{ {catResolv, "corp.acme.ru"}, {catADLDAP, "corp.acme.ru"}, {catADLDAP, "cab-gr-CI00363277-x86bmc"}, {catTimezone, "Europe/Moscow"}, {catTimezone, "180"}, {catDomain, "ntp01.acme.ru"}, {catEmail, "ops@acme.ru"}, {catPublicIP, "45.32.10.7"}, {catTimezone, "MSK"}, } for _, w := range mustHave { if !has(w.cat, w.match) { t.Errorf("missing finding %s / %q", w.cat, w.match) } } mustNotHave := []string{"192.168.31.4", "10.1.2.3", "203.0.113.9", "ntp.should-not-match.ru"} for _, f := range rep.Findings { for _, bad := range mustNotHave { if f.Match == bad { t.Errorf("unexpected finding for %q (%s)", bad, f.Category) } } } if len(rep.Customers) == 0 || rep.Customers[0].Domain != "acme.ru" { t.Fatalf("customer guess = %+v, want acme.ru first", rep.Customers) } if rep.Customers[0].Confidence != "high" { t.Errorf("confidence = %s, want high", rep.Customers[0].Confidence) } } func TestScan_NothingToScan(t *testing.T) { if Scan(nil) != nil { t.Fatal("want nil for no files") } if Scan([]File{{Path: "x", Content: []byte{0}}}) != nil { t.Fatal("want nil when only binary files") } } func TestScan_Dedupe(t *testing.T) { rep := Scan([]File{{Path: "a.log", Content: []byte( "host is srv-prod.acme.net\nhost is srv-prod.acme.net again\n")}}) if rep == nil { t.Fatal("nil report") } n := 0 for _, f := range rep.Findings { if f.Category == catDomain && f.Match == "srv-prod.acme.net" { n++ } } if n != 1 { t.Fatalf("domain finding counted %d times, want 1", n) } } func TestScan_AllowlistedDomainNotFlagged(t *testing.T) { rep := Scan([]File{{Path: "ntp.conf", Content: []byte( "server 0.pool.ntp.org\nserver time.nist.gov\ncontact admin@example.com\n")}}) if rep != nil && len(rep.Findings) > 0 { t.Fatalf("allowlisted infra flagged: %+v", rep.Findings) } } func TestScan_NvidiaBugReportNoise(t *testing.T) { // dmidecode + dmesg boilerplate that must not be flagged. rep := Scan([]File{{Path: "nvidia-bug-report.log", Content: []byte( "driver bug via the NVIDIA Linux forum (see forums.developer.nvidia.com)\n" + "or by sending email to 'linux-bugs@nvidia.com'.\n" + " Asset Tag: Base Board Asset Tag\n" + " P1-DIMMA1_AssetTag (Date:24/31)\n" + " ME FW Version\n 18:6.1.4.5\n" + "[ 8.078174] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti \n")}}) if rep != nil && len(rep.Findings) > 0 { t.Fatalf("nvidia boilerplate flagged: %+v", rep.Findings) } if rep != nil && len(rep.Customers) > 0 { t.Fatalf("nvidia boilerplate produced a customer guess: %+v", rep.Customers) } } func TestScan_RealResolvStillCaughtInNoisyFile(t *testing.T) { rep := Scan([]File{{Path: "nvidia-bug-report.log", Content: []byte( "[ 8.078174] pps_core: \n" + "--- /etc/resolv.conf ---\ndomain corp.acme.ru\nnameserver 10.0.0.1\n")}}) if rep == nil { t.Fatal("nil report") } found := false for _, f := range rep.Findings { if f.Category == catResolv && f.Match == "corp.acme.ru" { found = true } } if !found { t.Fatalf("real resolv.conf leak missed: %+v", rep.Findings) } if len(rep.Customers) == 0 || rep.Customers[0].Domain != "acme.ru" { t.Fatalf("customer guess = %+v, want acme.ru", rep.Customers) } } func TestScan_OSConfigNoise(t *testing.T) { // Everything here is stock OS / BEE-SP / Redfish text - zero customer data. noise := strings.Join([]string{ `Process: 3950 ExecStartPre=/usr/bin/nvidia-fabricmanager-start.sh --mode`, `echo "Usage: mountall.sh [start|stop]" >&2`, `. /lib/init/vars.sh`, `auth.backend.gssapi.store-creds = "disable"`, `server.network-backend = "writev"`, `ssl.ca-file = "/conf/server.pem"`, `# ALL EXCEPT in.fingerd: other.host.name, .other.domain`, `/lib/arm-linux-gnueabihf/libc.so.6`, `"@odata.id": "/redfish/v1/Systems/1"`, `X.Org X Server 1.21.1.7`, `ME FW Version 6.1.4.75`, `96.00.CF.00.03 VBIOS`, `#option dns 129.219.13.81`, `toolchain@v0.0.1-go1.25.0.linux-amd64/src/runtime/sema.go:9`, `serial-getty@ttyAMA0.service`, `Copyright (C) 2002-22, www.smartmontools.org`, `OpenIB.org BSD license (FreeBSD Variant)`, }, "\n") rep := Scan([]File{{Path: "onekeylog/log/sollog/SOLHostCapture.log", Content: []byte(noise)}}) if rep != nil && len(rep.Findings) > 0 { t.Fatalf("OS config noise flagged: %+v", rep.Findings) } } func TestScan_ResolvNeedsDottedValue(t *testing.T) { rep := Scan([]File{{Path: "resolv.conf", Content: []byte( "domain 53\nsearch nameserver\ndomain corp.acme.ru\n")}}) if rep == nil { t.Fatal("nil") } var got []string for _, f := range rep.Findings { if f.Category == catResolv { got = append(got, f.Match) } } if len(got) != 1 || got[0] != "corp.acme.ru" { t.Fatalf("resolv matches = %v, want [corp.acme.ru]", got) } } func TestSummary(t *testing.T) { rep := Scan([]File{{Path: "resolv.conf", Content: []byte("domain acme.ru\n")}}) if rep.Summary.Total != len(rep.Findings) || rep.Summary.Total == 0 { t.Fatalf("summary total mismatch: %+v", rep.Summary) } if rep.Summary.High == 0 { t.Fatalf("expected a high finding, got %+v", rep.Summary) } }