fix(privacy): drop nvidia-bug-report / dmidecode false positives

- Allowlist nvidia.com, mellanox.com, gnu.org, debian.org, ubuntu.com; the
  common "not specified / not available" FRU placeholders.
- Reject matches with no alphanumeric or <2 chars (stray ":" from a dumped
  resolv line), fru_location values that echo the field name ("Base Board
  Asset Tag", "P1-DIMMA1_AssetTag"), IPv4 embedded in a version string
  ("18:6.1.4.5"), and e-mail on kernel ring-buffer lines (driver copyright).
- Customer guess: don't report a single-hit low-confidence domain at all -
  "unidentified" beats guessing nvidia.com from a driver comment.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-09-02 16:27:17 +03:00
co-authored by Claude Sonnet 5
parent c9748f3830
commit 551d8e450d
5 changed files with 105 additions and 12 deletions
+47 -10
View File
@@ -50,6 +50,9 @@ var (
reSyslogTarget = regexp.MustCompile(`@((?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}:[0-9]{1,5})`)
reTZName = regexp.MustCompile(`\b((?:Africa|America|Antarctica|Asia|Atlantic|Australia|Europe|Indian|Pacific)/[A-Za-z_]+(?:/[A-Za-z_]+)?)\b`)
reTZAbbr = regexp.MustCompile(`\b(MSK|MSD|EEST|EET|MDST|CEST|CET|WEST|WET)\b\s+20\d\d`)
// Kernel ring-buffer line: "[ 12.345678] ...". These carry driver/copyright
// strings (emails, versions), never customer identity.
reKernelTimestamp = regexp.MustCompile(`^\[\s*\d+\.\d+\]\s`)
tableRules = []tableRule{
{catResolv, severityHigh, regexp.MustCompile(`(?i)^\s*(?:domain|search)\s+(\S+)`), 1,
@@ -84,15 +87,21 @@ var (
func scanLine(path, line string, ln int, certFile bool, emit func(models.PrivacyFinding)) {
var local []models.PrivacyFinding
specific := map[string]struct{}{} // matches from categories more precise than a bare FQDN
kernelLine := reKernelTimestamp.MatchString(line)
add := func(cat, sev, match, hint string) {
match = strings.Trim(strings.TrimSpace(match), `"',;`)
if match == "" || isAllowlistedValue(match) {
match = strings.Trim(strings.TrimSpace(match), `"',;:`)
if len(match) < 2 || !strings.ContainsFunc(match, isAlnum) || isAllowlistedValue(match) {
return
}
if ip := net.ParseIP(match); ip != nil && !isSensitiveIP(match) {
return
}
// BIOS/SMBIOS echoes the field name as the value ("Base Board Asset Tag",
// "P1-DIMMA1_AssetTag") - not a customer site tag.
if cat == catFRULocation && strings.Contains(strings.ToLower(strings.ReplaceAll(match, " ", "")), "assettag") {
return
}
if cat != catDomain && cat != catEmail && cat != catPublicIP {
specific[strings.ToLower(match)] = struct{}{}
}
@@ -129,21 +138,27 @@ func scanLine(path, line string, ln int, certFile bool, emit func(models.Privacy
}
add(catDomain, severityHigh, m[1], "domain / FQDN reveals the customer")
}
for _, m := range reEmail.FindAllStringSubmatch(line, -1) {
host := m[1][strings.IndexByte(m[1], '@')+1:]
if isAllowlistedDomain(host) || !looksLikeMailHost(host) {
continue
if !kernelLine {
for _, m := range reEmail.FindAllStringSubmatch(line, -1) {
host := m[1][strings.IndexByte(m[1], '@')+1:]
if isAllowlistedDomain(host) || !looksLikeMailHost(host) {
continue
}
add(catEmail, severityMedium, m[1], "e-mail address")
}
add(catEmail, severityMedium, m[1], "e-mail address")
}
for _, m := range reSyslogTarget.FindAllStringSubmatch(line, -1) {
add(catCollector, severityMedium, m[1], "remote syslog target")
}
for _, m := range reIPv4.FindAllStringSubmatch(line, -1) {
if !isSensitiveIP(m[1]) {
for _, loc := range reIPv4.FindAllStringIndex(line, -1) {
if inDottedNumberRun(line, loc[0], loc[1]) {
continue // part of a longer version string like "18:6.1.4.5"
}
ip := line[loc[0]:loc[1]]
if !isSensitiveIP(ip) {
continue
}
add(catPublicIP, severityMedium, m[1], "public IP reveals provider / site")
add(catPublicIP, severityMedium, ip, "public IP reveals provider / site")
}
if strings.Contains(strings.ToLower(line), "timezone") || strings.Contains(line, "/") {
for _, m := range reTZName.FindAllStringSubmatch(line, -1) {
@@ -165,6 +180,28 @@ func scanLine(path, line string, ln int, certFile bool, emit func(models.Privacy
}
}
func isAlnum(r rune) bool {
return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9')
}
// inDottedNumberRun reports whether line[start:end] (a dotted quad) is embedded
// in a longer numeric run - a version string like "18:6.1.4.5" or "1.2.3.4.5" -
// rather than a standalone address.
func inDottedNumberRun(line string, start, end int) bool {
if start > 0 {
switch line[start-1] {
case '.', ':':
if start >= 2 && line[start-2] >= '0' && line[start-2] <= '9' {
return true
}
}
}
if end < len(line) && line[end] == '.' {
return true
}
return false
}
// looksLikeMailHost rejects the many "local@identifier.token" strings that are
// not e-mail: OData/Redfish JSON annotations (Members@odata.count), SSH
// cipher/kex names (aes256-gcm@openssh.com is handled by the domain allowlist,