package security import "testing" func TestParseBrewVulns(t *testing.T) { // Shape matches `brew vulns --json` (homebrew/brew-vulns output_json). out := []byte(`[ { "formula": "expat", "version": "2.5.0", "tag": "R_2_5_0", "repo_url": "https://github.com/libexpat/libexpat", "vulnerabilities": [ { "id": "GHSA-xxxx-yyyy-zzzz", "severity": "HIGH", "summary": "XML parsing vulnerability", "aliases": ["CVE-2024-12345"], "fixed_versions": ["2.6.0"] } ] }, { "formula": "somelib", "version": "1.0.0", "tag": "v1.0.0", "repo_url": "https://github.com/foo/somelib", "vulnerabilities": [ { "id": "CVE-2023-0001", "severity": "CRITICAL", "summary": "RCE", "aliases": [], "fixed_versions": ["1.0.1"] }, { "id": "OSV-2023-9", "severity": "UNKNOWN", "summary": "unspecified", "aliases": [], "fixed_versions": [] } ] } ]`) findings, n, err := parseBrewVulns(out) if err != nil { t.Fatalf("parseBrewVulns error: %v", err) } if n != 2 { t.Errorf("affected formulae = %d, want 2", n) } if len(findings) != 3 { t.Fatalf("findings = %d, want 3", len(findings)) } // First finding: GHSA id, CVE alias preferred, HIGH -> 7.0 -> HIGH. f := findings[0] if f.PackageName != "expat" || f.PackageVersion != "2.5.0" { t.Errorf("pkg = %s %s, want expat 2.5.0", f.PackageName, f.PackageVersion) } if f.CVE != "CVE-2024-12345" { t.Errorf("CVE = %q, want CVE-2024-12345 (alias preferred over GHSA id)", f.CVE) } if f.Severity != "HIGH" || f.CVSS != 7.0 { t.Errorf("severity = %q / %.1f, want HIGH / 7.0", f.Severity, f.CVSS) } if f.Link != "https://github.com/advisories/GHSA-xxxx-yyyy-zzzz" { t.Errorf("link = %q, want GHSA advisory URL (built from id)", f.Link) } // CRITICAL maps to 9.0 -> CRITICAL, link via NVD for a CVE id. if findings[1].Severity != "CRITICAL" || findings[1].CVSS != 9.0 { t.Errorf("finding[1] = %q / %.1f, want CRITICAL / 9.0", findings[1].Severity, findings[1].CVSS) } if findings[1].Link != "https://nvd.nist.gov/vuln/detail/CVE-2023-0001" { t.Errorf("finding[1] link = %q, want NVD URL", findings[1].Link) } // UNKNOWN -> 0 -> UNKNOWN, osv.dev link. if findings[2].Severity != "UNKNOWN" || findings[2].CVSS != 0 { t.Errorf("finding[2] = %q / %.1f, want UNKNOWN / 0", findings[2].Severity, findings[2].CVSS) } if findings[2].Link != "https://osv.dev/vulnerability/OSV-2023-9" { t.Errorf("finding[2] link = %q, want osv.dev URL", findings[2].Link) } } func TestParseBrewVulnsEmpty(t *testing.T) { // `brew vulns --json` prints "[]" when nothing is affected. findings, n, err := parseBrewVulns([]byte("[]\n")) if err != nil { t.Fatalf("unexpected error: %v", err) } if n != 0 || len(findings) != 0 { t.Errorf("got %d findings / %d formulae, want 0/0", len(findings), n) } // Non-JSON (e.g. an "Unknown command" error on stderr, empty stdout) errors. if _, _, err := parseBrewVulns([]byte("")); err == nil { t.Error("expected error for empty output") } if _, _, err := parseBrewVulns([]byte("Error: something")); err == nil { t.Error("expected error for non-JSON output") } }