Improve recipe advisory coverage and report non-CVE fixes

This commit is contained in:
notplants
2026-11-02 19:40:46 +00:00
committed by notplants
parent 508d6bb0de
commit bd7196bf9e
7 changed files with 238 additions and 42 deletions
+63 -2
View File
@@ -52,7 +52,7 @@ def vendor(url, cves=(), status="ok"):
def run_scan(gh_entries=(), vendor_entries=(), tag_dates=None, *, v_from=None, v_to=None,
images=None, recipe="fixture", urls=None, releases=None):
images=None, recipe="fixture", urls=None, releases=None, image_sources=None):
"""scan() with every network call replaced by fixtures.
`releases` maps CVE id -> tags whose release notes name it (the third, release-note method)."""
@@ -62,6 +62,7 @@ def run_scan(gh_entries=(), vendor_entries=(), tag_dates=None, *, v_from=None, v
with unittest.mock.patch.object(A, "registry_urls", lambda r, d: (list(urls), "/fake/reg.md")), \
unittest.mock.patch.object(A, "github_advisories", lambda u: list(gh_entries)), \
unittest.mock.patch.object(A, "vendor_pages", lambda u: list(vendor_entries)), \
unittest.mock.patch.object(A, "registry_image_sources", lambda r, d: image_sources or {}), \
unittest.mock.patch.object(A, "osv", lambda r, v: None), \
unittest.mock.patch.object(A, "_tag_date", lambda o, r, v: tag_dates.get(v)), \
unittest.mock.patch.object(A, "release_fix_versions", lambda src, cve: list(releases.get(cve, []))):
@@ -157,9 +158,23 @@ class TestRegistryUrls(unittest.TestCase):
def test_prose_ellipsis_is_not_a_security_source(self):
with tempfile.TemporaryDirectory() as directory:
urls, _ = self._write(directory,
"https://github.com/cryptpad/cryptpad and example `https://…` URLs")
"https://github.com/cryptpad/cryptpad and example `https://…` URLs "
"http://docspec:3000/conversion http://127.0.0.1:3000/health")
self.assertEqual(urls, ["https://github.com/cryptpad/cryptpad"])
def test_image_source_table_maps_monorepo_images(self):
mapping = A.registry_image_sources("lasuite-drive", str(HERE / "upstream"))
self.assertIn("github-advisories:suitenumerique/drive", mapping["drive-backend"])
self.assertIn("github-advisories:CollaboraOnline/online", mapping["code"])
def test_common_database_cpes_follow_registry_images(self):
self.assertIn(("postgres", A.COMMON_IMAGE_CPES["postgres"]),
A.registry_cpes("immich", str(HERE / "upstream")))
self.assertIn(("mariadb", A.COMMON_IMAGE_CPES["mariadb"]),
A.registry_cpes("gitea", str(HERE / "upstream")))
self.assertIn(("rustdesk-server", "cpe:2.3:a:rustdesk:rustdesk_server:*:*:*:*:oss:*:*:*"),
A.registry_cpes("rustdesk-server", str(HERE / "upstream")))
def test_strips_trailing_markdown_punctuation(self):
# Production defect: a captured backtick 404'd the fetch and rendered n8n/immich as '?'.
import tempfile
@@ -181,6 +196,25 @@ class TestRegistryUrls(unittest.TestCase):
self.assertEqual((urls, path), ([], None))
class TestNvdCompleteness(unittest.TestCase):
def test_truncated_results_are_not_a_clean_zero(self):
payload = json.dumps({"totalResults": 2, "vulnerabilities": [{"cve": {"id": "CVE-2026-0001"}}]})
with unittest.mock.patch.object(A, "_fetch", return_value=payload):
row = A.nvd_advisories("cpe:2.3:a:example:server:*:*:*:*:*:*:*:*", "server")
self.assertEqual(row["status"], "error: NVD result set truncated")
self.assertEqual(row["advisories"], [])
def test_database_backport_is_not_credited_again_on_major_upgrade(self):
ranges = [
{"version": "*", "versionStartIncluding": "15.0", "versionEndExcluding": "15.14"},
{"version": "*", "versionStartIncluding": "16.0", "versionEndExcluding": "16.5"},
]
self.assertIs(A._nvd_affects("15.13", ranges), True)
self.assertIs(A._nvd_affects("15.19", ranges), False)
self.assertIs(A._nvd_affects("18.6", ranges), False)
self.assertIsNone(A._nvd_affects("15-alpine", ranges))
# ── C. --image argument parsing ───────────────────────────────────────────────────────────────────
class TestImageArgParsing(unittest.TestCase):
@@ -306,6 +340,24 @@ class TestPerImageWindows(unittest.TestCase):
self.assertEqual(rep["unmatched_image_windows"], ["nosuchimage"])
self.assertIsNone(rep["cve_count_fixed"])
def test_registry_maps_image_to_differently_named_source(self):
rep = run_scan([self.APP, gh("suitenumerique/drive", [adv("CVE-DRIVE-1", patched="0.25.3")])],
v_from="3.5.3", v_to="3.5.4", images=[("drive-backend", "0.22.0", "0.25.3")],
urls=self.URLS,
image_sources={"drive-backend": {"github-advisories:suitenumerique/drive"}})
self.assertEqual(rep.get("unmatched_image_windows"), None)
self.assertIn("CVE-DRIVE-1", rep["fixed_by_this_upgrade"])
def test_monorepo_images_share_identical_window(self):
source = "github-advisories:suitenumerique/drive"
rep = run_scan([gh("suitenumerique/drive", [adv("CVE-DRIVE-1", patched="0.25.3")])],
v_from="0.22.0", v_to="0.25.3",
images=[("drive-backend", "0.22.0", "0.25.3")],
image_sources={"drive-backend": {source}})
self.assertTrue(rep["count_known"])
self.assertEqual(rep.get("unmatched_image_windows"), None)
self.assertEqual(rep["cve_count_fixed"], 1)
class TestSchemeChangeDateFallback(unittest.TestCase):
DATES = {"3.5.3": "2025-12-30T00:00:00Z", "2026.7.1": "2026-07-31T00:00:00Z"}
@@ -367,6 +419,15 @@ class TestCountGuarantees(unittest.TestCase):
self.assertTrue(rep["count_known"])
self.assertIn("0 identified", A.markdown(rep))
def test_ghsa_without_cve_is_preserved_as_security_advisory(self):
entry = {"cve": None, "ghsa": "GHSA-aaaa-bbbb-cccc", "severity": "low",
"patched": "1.1", "summary": "security fix", "published_at": None}
rep = run_scan([gh("app/app", [entry])], v_from="1.0", v_to="1.1")
self.assertTrue(rep["count_known"])
self.assertEqual(rep["cve_count_fixed"], 0)
self.assertEqual(rep["fixed_non_cve_advisories"], ["GHSA-aaaa-bbbb-cccc"])
self.assertIn("use YES", A.markdown(rep))
def test_404_advisory_feed_is_benign_not_a_failure(self):
rep = run_scan([gh("app/app", [adv("CVE-1", patched="1.1")]),
gh("side/car", [], status="no-advisories-published")],