package vuln import ( "strings" ) // Finding mirrors agent vuln_findings JSON for server-side enrichment. type Finding struct { CVEID string `json:"cve_id"` Severity string `json:"severity"` Component string `json:"component"` Patched bool `json:"patched"` ExploitableInFleetContext bool `json:"exploitable_in_fleet_context"` Detail string `json:"detail,omitempty"` } // CatalogEntry is a lightweight embedded CVE rule for fleet correlator. type CatalogEntry struct { ID string `json:"id"` Name string `json:"name"` Component string `json:"component"` Severity string `json:"severity"` FleetPorts []int `json:"fleet_ports,omitempty"` PatchKBs []string `json:"patch_kbs,omitempty"` } // EmbeddedCatalog is served by GET /api/v1/vuln/catalog (cached JSON). var EmbeddedCatalog = []CatalogEntry{ {ID: "CVE-2021-44228", Name: "Log4Shell", Component: "Apache Log4j", Severity: "critical"}, {ID: "CVE-2021-26855", Name: "ProxyLogon", Component: "Microsoft Exchange", Severity: "critical", FleetPorts: []int{443, 80}, PatchKBs: []string{"KB5000871"}}, {ID: "CVE-2020-1472", Name: "Zerologon", Component: "Microsoft Netlogon", Severity: "critical", FleetPorts: []int{445, 135}, PatchKBs: []string{"KB4577015"}}, {ID: "CVE-2019-19781", Name: "Citrix ADC", Component: "Citrix ADC/Gateway", Severity: "critical", FleetPorts: []int{443}}, {ID: "CVE-2019-11510", Name: "Pulse Secure", Component: "Ivanti Pulse Connect Secure", Severity: "critical", FleetPorts: []int{443}}, {ID: "CVE-2020-5902", Name: "F5 BIG-IP", Component: "F5 BIG-IP", Severity: "critical", FleetPorts: []int{443, 8443}}, {ID: "CVE-2022-1388", Name: "F5 iControl", Component: "F5 BIG-IP", Severity: "critical", FleetPorts: []int{443, 8443}}, {ID: "CVE-2021-26084", Name: "Confluence OGNL", Component: "Atlassian Confluence", Severity: "critical", FleetPorts: []int{8090, 8443}}, {ID: "CVE-2022-26134", Name: "Confluence RCE", Component: "Atlassian Confluence", Severity: "critical", FleetPorts: []int{8090, 8443}}, {ID: "CVE-2021-40539", Name: "ManageEngine", Component: "Zoho ManageEngine ADSelfService Plus", Severity: "critical", FleetPorts: []int{9251}}, {ID: "CVE-2018-13379", Name: "FortiOS path traversal", Component: "Fortinet FortiGate/FortiOS", Severity: "critical", FleetPorts: []int{443, 10443}}, {ID: "CVE-2021-34527", Name: "PrintNightmare", Component: "Windows Print Spooler", Severity: "high", FleetPorts: []int{445, 135}, PatchKBs: []string{"KB5004945"}}, {ID: "CVE-2020-0688", Name: "Exchange RCE", Component: "Microsoft Exchange", Severity: "high", FleetPorts: []int{443}}, {ID: "CVE-2021-21972", Name: "vCenter RCE", Component: "VMware vCenter", Severity: "critical", FleetPorts: []int{443}}, } // FleetContext carries server-known signals for correlator enrichment. type FleetContext struct { ListenPortCount int PathTracerPorts []int SSHAvailable bool OSVersion string } // EnrichFindings applies fleet-context rules (open ports from Path Tracer when available). func EnrichFindings(findings []Finding, ctx FleetContext) []Finding { if len(findings) == 0 { return findings } portSet := make(map[int]bool) for _, p := range ctx.PathTracerPorts { portSet[p] = true } rules := catalogByID() out := make([]Finding, len(findings)) copy(out, findings) for i := range out { if out[i].Patched || out[i].ExploitableInFleetContext { continue } rule, ok := rules[out[i].CVEID] if !ok { continue } for _, p := range rule.FleetPorts { if portSet[p] { out[i].ExploitableInFleetContext = true out[i].Detail = strings.TrimSpace(out[i].Detail + " · Path Tracer hop port " + itoa(p) + " open") break } } if !out[i].ExploitableInFleetContext && ctx.SSHAvailable && ctx.ListenPortCount > 0 { for _, p := range rule.FleetPorts { if p == 22 || p == 445 || p == 443 { out[i].ExploitableInFleetContext = true out[i].Detail = strings.TrimSpace(out[i].Detail + " · fleet SSH/listener context") break } } } } return out } func catalogByID() map[string]CatalogEntry { m := make(map[string]CatalogEntry, len(EmbeddedCatalog)) for _, e := range EmbeddedCatalog { m[e.ID] = e } return m } func itoa(n int) string { if n == 0 { return "0" } var b [12]byte i := len(b) for n > 0 { i-- b[i] = byte('0' + n%10) n /= 10 } return string(b[i:]) } // RiskScore computes a 0-100 score from enriched findings. func RiskScore(findings []Finding) int { score := 0 for _, f := range findings { if f.ExploitableInFleetContext { if f.Severity == "critical" { score += 25 } else { score += 12 } } else if !f.Patched { score += 8 } } if score > 100 { return 100 } return score }