diff options
Diffstat (limited to 'report.go')
| -rw-r--r-- | report.go | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/report.go b/report.go new file mode 100644 index 0000000..e52c9d4 --- /dev/null +++ b/report.go @@ -0,0 +1,82 @@ +package app + +import ( + "fmt" + "os" + "sort" + "strings" +) + +// BrewVulnsReport is the JSON structure emitted on stdout. +type BrewVulnsReport struct { + Exploited []string `json:"exploited"` + NotExploited []string `json:"not_exploited"` + NonCVE []string `json:"non_cve"` + VulnerablePackages []string `json:"vulnerable_packages"` +} + +// BuildBrewVulnsReport checks each brew-reported CVE against fprox and +// classifies it as exploited or not, alongside the vulnerable packages. +func BuildBrewVulnsReport() (BrewVulnsReport, error) { + r, err := runBrewVulns() + if err != nil { + return BrewVulnsReport{}, err + } + + // Maintain a CVE -> homebrew package mapping while accumulating the + // vulnerable packages and non-CVE identifiers so the report references all. + cvePkg := make(map[string]string) + pkgSeen := make(map[string]struct{}) + nonCveSeen := make(map[string]struct{}) + for _, finding := range r.Findings { + for _, v := range finding.Vulnerabilities { + if v.ID == "" { + continue + } + if strings.HasPrefix(v.ID, "CVE-") { + if _, ok := cvePkg[v.ID]; !ok { + cvePkg[v.ID] = finding.Formula + } + pkgSeen[finding.Formula] = struct{}{} + } else { + nonCveSeen[v.ID] = struct{}{} + } + } + } + + var exploited, notExploited []string + for cve := range cvePkg { + has, err := CveHasExploits(cve) + if err != nil { + // Skip CVEs we could not resolve so they are never mislabeled. + fmt.Fprintf(os.Stderr, "warning: skipping %s: %v\n", cve, err) + continue + } + if has { + exploited = append(exploited, cve) + } else { + notExploited = append(notExploited, cve) + } + } + sort.Strings(exploited) + sort.Strings(notExploited) + + pkgs := make([]string, 0, len(pkgSeen)) + for pkg := range pkgSeen { + pkgs = append(pkgs, pkg) + } + sort.Strings(pkgs) + + nonCves := make([]string, 0, len(nonCveSeen)) + for id := range nonCveSeen { + nonCves = append(nonCves, id) + } + sort.Strings(nonCves) + + return BrewVulnsReport{ + Exploited: exploited, + NotExploited: notExploited, + NonCVE: nonCves, + VulnerablePackages: pkgs, + }, nil +} |
