fix(vuln): survive OOM kills, isolate bad upstream strings, label findings per line
Review findings on the integrated scanner: - An OOM kill no longer wedges the loop: inFlight is persisted before each scan, and on restart that image is marked failed and sits out full runs for 20h. - One bad upstream string can no longer freeze the report: safe_url requires an exact lowercase scheme, acceptance reasons are bounded, /triage urls use the same rule, and report.build repairs or drops any entry the schema would refuse. lastCompleteRunAt advances only after a report is actually published. - The private registry reports credential-required, not failed: VULN_PRIVATE_REGISTRIES names it, and Trivy is not invoked. - loadError clears after a full run against a good inventory. - Every image under platform/vuln-scanner stays out of Renovate auto-merge. - The page labels status and target state per line, and mixed acceptance reads as mixed. The Keycloak client is still an operator action: its key must go in an existing encrypted file that an agent must not modify. platform/vuln-scanner/README.md has the exact steps. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
4c82c64b2f
commit
d8b25a67fe
15 files changed
+557
-43
No files matched your search
@@ -180,14 +180,39 @@ def _sev(severity):
|
||||
return '<span class="sev-' + e(severity) + '">' + e(severity) + "</span>"
|
||||
|
||||
|
||||
def _workload_lines(target):
|
||||
def _state_label(target):
|
||||
return "target " + e(target["state"]) + (" (" + e(target["reason"]) + ")" if target.get("reason") else "")
|
||||
|
||||
|
||||
def _status_label(finding):
|
||||
"""One finding's own status, with its own acceptance: never another image's."""
|
||||
label = "status " + e(finding["status"])
|
||||
acceptance = finding.get("acceptance")
|
||||
if acceptance:
|
||||
label += " (" + e(acceptance["reason"]) + ", until " + e(acceptance["expires"]) + ")"
|
||||
return label
|
||||
|
||||
|
||||
def _workload_lines(target, finding=None):
|
||||
"""One line per workload, each carrying the target state and, in a CVE expansion, the status."""
|
||||
suffix = " — " + _state_label(target) + (" — " + _status_label(finding) if finding else "")
|
||||
items = []
|
||||
for workload in target["workloads"]:
|
||||
items.append("<li>" + e(workload["namespace"]) + " / " + e(workload["kind"]) + " / " + e(workload["name"])
|
||||
+ " — containers: " + e(", ".join(workload["containers"]) or "none") + "</li>")
|
||||
+ " — containers: " + e(", ".join(workload["containers"]) or "none") + suffix + "</li>")
|
||||
return "<ul>" + "".join(items) + "</ul>"
|
||||
|
||||
|
||||
def _status_summary(statuses):
|
||||
"""The row-level status. A CVE accepted on one image and open on another reads as mixed."""
|
||||
counts = {}
|
||||
for status in statuses:
|
||||
counts[status] = counts.get(status, 0) + 1
|
||||
if len(counts) == 1:
|
||||
return e(next(iter(counts)))
|
||||
return "mixed: " + ", ".join(e(counts[s]) + " " + e(s) for s in sorted(counts, key=STATUS_RANK.get))
|
||||
|
||||
|
||||
def _remediation_cell(images, remediation):
|
||||
if not images:
|
||||
return ""
|
||||
@@ -250,13 +275,10 @@ def _cves(report, targets, params, remediation):
|
||||
for finding, target in row["items"]:
|
||||
if finding["status"] == "fix-available":
|
||||
fix_images.add(target["image"])
|
||||
lines.append("<li>" + e(target["id"]) + " [" + e(target["state"]) + "] — " + e(finding["package"]) + " "
|
||||
+ e(finding["installedVersion"]) + " → " + e(finding["fixedVersion"] or "no fix")
|
||||
+ _workload_lines(target) + "</li>")
|
||||
acceptance = next((f.get("acceptance") for f, _ in row["items"] if f.get("acceptance")), None)
|
||||
status_text = e(row["status"])
|
||||
if acceptance:
|
||||
status_text += " (" + e(acceptance["reason"]) + ", until " + e(acceptance["expires"]) + ")"
|
||||
lines.append("<li>" + e(target["id"]) + " — " + _state_label(target) + " — " + _status_label(finding)
|
||||
+ " — " + e(finding["package"]) + " " + e(finding["installedVersion"]) + " → "
|
||||
+ e(finding["fixedVersion"] or "no fix") + _workload_lines(target, finding) + "</li>")
|
||||
status_text = _status_summary(f["status"] for f, _ in row["items"])
|
||||
html_rows.append(
|
||||
"<tr><td>" + _link(row["url"], row["id"]) + "</td><td>" + _sev(row["severity"]) + "</td><td>"
|
||||
+ e("" if row["cvss"] is None else row["cvss"]) + "</td><td>" + e(row["package"]) + "</td><td>" + e(row["fix"])
|
||||
@@ -317,12 +339,13 @@ def _workloads(report, targets, params):
|
||||
html_rows = []
|
||||
for row in page_rows:
|
||||
lines = "".join(
|
||||
"<li>" + _link(f["url"], f["id"]) + " " + _sev(f["severity"]) + " " + e(f["package"]) + " — " + e(f["status"]) + "</li>"
|
||||
for f in sorted(row["findings"].values(), key=lambda f: (SEV_RANK[f["severity"]], f["id"])))
|
||||
"<li>" + _link(f["url"], f["id"]) + " " + _sev(f["severity"]) + " " + e(f["package"]) + " — " + _status_label(f)
|
||||
+ " — " + e(f["targetId"]) + " — " + _state_label(targets[f["targetId"]]) + "</li>"
|
||||
for f in sorted(row["findings"].values(), key=lambda f: (SEV_RANK[f["severity"]], f["id"], f["targetId"])))
|
||||
counts = "".join("<td>" + e(row["counts"][s]) + "</td>" for s in SEVERITIES)
|
||||
html_rows.append(
|
||||
"<tr><td>" + e(row["key"][0]) + "</td><td>" + e(row["key"][1]) + "</td><td>" + e(row["key"][2]) + "</td><td>"
|
||||
+ e(", ".join(sorted(row["containers"])) or "none") + "</td><td>" + "<br>".join(e(i) for i in sorted(row["images"])) + "</td>"
|
||||
+ e(", ".join(sorted(row["containers"])) or "none") + "</td><td>" + "<br>".join(e(i) + " — " + _state_label(targets[i]) for i in sorted(row["images"])) + "</td>"
|
||||
+ counts + "<td><details><summary>" + e(len(row["findings"])) + " findings</summary><ul>" + lines + "</ul></details></td></tr>")
|
||||
head = [_th(params, "Namespace", "namespace"), _th(params, "Kind", "kind"), _th(params, "Workload", "name"), "<th>Containers</th>",
|
||||
_th(params, "Images", "images")] + [_th(params, s, s.lower()) for s in SEVERITIES] + [_th(params, "Findings", "total")]
|
||||
|
||||
Reference in new issue
Block a user