feat(vuln): scanner service and fixture-driven L1 tests
Stdlib-only scanner per docs/17: Kubernetes API inventory, Trivy runner, persisted state, schema-validated report.json, strict acceptances parser, server-rendered page, aggregate metrics, internal API, and a scan loop that stays live while scanning. L1 tests under conformance/tests/l1-rendering/vuln-scanner-* run the actual modules against recorded fixtures with a fake Trivy runner. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
4c3a284cad
commit
50ec2297ee
32 files changed
+3951
No files matched your search
@@ -0,0 +1,44 @@
|
||||
"""Prometheus text exposition: aggregates only (R-VULN-06). The label allowlist is state, severity, fixable."""
|
||||
from datetime import timedelta
|
||||
|
||||
import report as report_mod
|
||||
from state import parse_ts
|
||||
|
||||
_CRITICAL_GRACE = timedelta(hours=72)
|
||||
_SEVERITIES = ("CRITICAL", "HIGH", "MEDIUM", "LOW", "UNKNOWN")
|
||||
|
||||
|
||||
def _sample(lines, name, value, labels=""):
|
||||
lines.append(name + labels + " " + format(value, ".0f" if float(value).is_integer() else "g"))
|
||||
|
||||
|
||||
def render(report, state, now):
|
||||
lines = []
|
||||
if report is not None:
|
||||
_sample(lines, "vuln_scanner_report_generated_timestamp_seconds", parse_ts(report["generatedAt"]).timestamp())
|
||||
_sample(lines, "vuln_scanner_database_updated_timestamp_seconds", parse_ts(report["database"]["updatedAt"]).timestamp())
|
||||
counts = {name: 0 for name in report_mod.STATES}
|
||||
for target in report["targets"]:
|
||||
counts[target["state"]] += 1
|
||||
for name in report_mod.STATES:
|
||||
_sample(lines, "vuln_scanner_targets", counts[name], '{state="' + name + '"}')
|
||||
findings = {(sev, fix): 0 for sev in _SEVERITIES for fix in ("true", "false")}
|
||||
overdue = 0
|
||||
for finding in report["findings"]:
|
||||
if finding["status"] == "accepted":
|
||||
continue
|
||||
findings[(finding["severity"], "true" if finding["fixedVersion"] else "false")] += 1
|
||||
if finding["severity"] == "CRITICAL" and finding["fixedVersion"]:
|
||||
first_seen = parse_ts(finding["firstSeenAt"])
|
||||
if first_seen and now - first_seen > _CRITICAL_GRACE:
|
||||
overdue += 1
|
||||
for (severity, fixable), value in sorted(findings.items()):
|
||||
_sample(lines, "vuln_scanner_findings", value, '{severity="' + severity + '",fixable="' + fixable + '"}')
|
||||
_sample(lines, "vuln_scanner_critical_fix_overdue", overdue)
|
||||
_sample(lines, "vuln_scanner_failed_targets_over_24h", report_mod.failed_over_24h(state, now))
|
||||
for name, key in (("vuln_scanner_last_complete_run_timestamp_seconds", "lastCompleteRunAt"),
|
||||
("vuln_scanner_triage_heartbeat_timestamp_seconds", "triageHeartbeatAt")):
|
||||
value = parse_ts(state.get(key))
|
||||
if value is not None:
|
||||
_sample(lines, name, value.timestamp())
|
||||
return "\n".join(lines) + "\n"
|
||||
Reference in new issue
Block a user