diff --git a/cc-ci-plan/advisory-scan.py b/cc-ci-plan/advisory-scan.py index 9e5d334..61994b0 100755 --- a/cc-ci-plan/advisory-scan.py +++ b/cc-ci-plan/advisory-scan.py @@ -138,7 +138,11 @@ def github_advisories(urls: list[str]) -> list[dict]: hdrs["Authorization"] = f"Bearer {tok}" entry = {"source": f"github-advisories:{owner}/{repo}", "status": "ok", "advisories": []} try: - for a in json.loads(_fetch(api, hdrs)): # noqa: PERF401 — explicit for readability + for a in json.loads(_fetch(api, hdrs)): + # An advisory carries ONE ENTRY PER PATCHED RELEASE LINE. n8n patches three + # (1.123.32, 2.17.4, 2.18.1); reading only vulnerabilities[0] silently dropped the + # line our deployment is actually on, so CVE-2026-42231/42232 classified as + # out-of-window. Keep them ALL and let the classifier match any of them. vulns = a.get("vulnerabilities") or [] entry["advisories"].append( { @@ -146,8 +150,12 @@ def github_advisories(urls: list[str]) -> list[dict]: "ghsa": a.get("ghsa_id"), "severity": a.get("severity"), "summary": (a.get("summary") or "")[:200], - "vulnerable_range": vulns[0].get("vulnerable_version_range") if vulns else None, - "patched": vulns[0].get("patched_versions") if vulns else None, + "vulnerable_range": "; ".join( + filter(None, (v.get("vulnerable_version_range") for v in vulns)) + ) or None, + "patched": "; ".join( + filter(None, (v.get("patched_versions") for v in vulns)) + ) or None, "url": a.get("html_url"), } ) @@ -266,7 +274,11 @@ def scan(recipe: str, v_from: str | None, v_to: str | None, registry_dir: str) - kf, kt = _vkey(v_from), _vkey(v_to) fixed, unknown = [], [] for cve, e in report["cves"].items(): - kp = _vkey((e.get("patched") or "").split(",")[0].strip() or None) + # `patched_versions` is a RANGE EXPRESSION (">= 2.18.1"), not a bare version, and there may + # be several (one per patched release line, joined with ";"). Pull every version-looking + # token and treat the advisory as fixed-by-this-upgrade if ANY of them lands in (from, to]. + cands = [_vkey(t) for t in re.findall(r"\d+(?:\.\d+)*", e.get("patched") or "")] + kp = next((c for c in cands if kf and kt and kf < c <= kt), None) or (cands[0] if cands else ()) if kf and kt and kp and kf < kp <= kt: e["classification"] = "fixed-by-this-upgrade" fixed.append(cve)