diff --git a/README.DEV.md b/README.DEV.md index 7471fde..84c5be8 100644 --- a/README.DEV.md +++ b/README.DEV.md @@ -1856,13 +1856,16 @@ install. "Deferred" with no configuration as of 2026-10-13, and its `blender:blender` rows stop at 2022. The record states `3.0.0 .. lessThanOrEqual "5.1.2"` (fix shown as `>5.1.2`) plus an `unaffected` git commit, which is not a version and -is ignored. Only the vendor's spelling `blender / blender` is paired; the 2022 -records name the vendor "n/a" and write prose ("Blender versions prior to -2.83.19, 2.93.8 and 3.1"). Names are anchored ("Blender", "Blender 4.2 LTS"), -so launchers and add-ons stay out. Windows and macOS hosts only: a distro +is ignored. CVE-2026-119718..119722 (published 2026-10-30, NVD "Awaiting +Analysis") come from the same CNA (VulnCheck) as `Blender Foundation / Blender` +with two lines, `0 .. <4.5.15` and `5.0.0 .. <5.2.3`; both spellings are +paired, and a 4.5 LTS host is shown the 4.5.15 fix. "n/a" stays out: the 2022 +records there write prose ("Blender versions prior to 2.83.19, 2.93.8 and +3.1"). Names are anchored ("Blender", "Blender 4.2 LTS"), so launchers and +add-ons stay out. Windows and macOS hosts only: a distro package ("4.3.2+dfsg-2") cleans to 4.3.2 and says nothing about backports. -Known limit: the range covers the 4.5 LTS line, and a fix backported there -closes the finding only once the record names it. +Known limit: CVE-2026-60103's range covers the 4.5 LTS line, and a fix +backported there closes the finding only once the record names it. **GnuPG** (`gnupg:gnupg` on NVD, no cvelistV5 row). The inventory names GnuPG's own Windows installer "GNU Privacy Guard". NVD bounds each branch diff --git a/app/services/cvelistv5_scan_service.py b/app/services/cvelistv5_scan_service.py index af0577a..d882515 100644 --- a/app/services/cvelistv5_scan_service.py +++ b/app/services/cvelistv5_scan_service.py @@ -44,7 +44,7 @@ _ZIP_PATH = "/tmp/truevuln-cvelistv5-cache.zip" _ZIP_URL = "https://github.com/CVEProject/cvelistV5/archive/refs/heads/main.zip" _ZIP_TTL = 12 * 3600 _ZIP_MIN_BYTES = 100_000_000 # sanity floor; the real ZIP is ~557 MB -_INDEX_SETTING = "cvelistv5_product_index_v55" # v55: Cisco Webex +_INDEX_SETTING = "cvelistv5_product_index_v56" # v56: Blender Foundation _INDEX_TTL = timedelta(hours=26) # rebuilt nightly; a missed night still serves # Curated registry: name-regex (installed software) → cvelistV5 (vendor, @@ -463,10 +463,12 @@ _REGISTRY: List[dict] = [ "pairs": [("lenovo", "system update")]}, # Blender. NVD left CVE-2026-60103 "Deferred" with no configuration and its # blender:blender rows stop at 2022, so this is the only structured source - # ("3.0.0" .. lessThanOrEqual "5.1.2"). Vendor's own spelling only: the - # 2022 records under "n/a" write prose into the version field. + # ("3.0.0" .. lessThanOrEqual "5.1.2"). The same CNA (VulnCheck) wrote + # CVE-2026-119718..119722 as "Blender Foundation" (0 .. <4.5.15, 5.0.0 .. + # <5.2.3), so both spellings. Not "n/a": the 2022 records there write + # prose into the version field. {"key": "blender", "re": r"^blender(?: \d+(?:\.\d+)*(?: lts)?)?$", - "pairs": [("blender", "blender")]}, + "pairs": [("blender", "blender"), ("blender foundation", "blender")]}, # Ghostscript. NVD had no configuration for CVE-2026-39919 ("0" .. lessThan # "10.08.0"). Artifex's own spellings only: the 2019-2020 Red Hat records # ("n/a", "The ghostscript Project", ...) write prose or the FIXED version diff --git a/tests/test_blender.py b/tests/test_blender.py index cc6e84b..334ed51 100644 --- a/tests/test_blender.py +++ b/tests/test_blender.py @@ -32,6 +32,16 @@ B_60103 = {"defaultStatus": "affected", "vendor": "blender", "product": "blender "lessThanOrEqual": "5.1.2"}, {"status": "unaffected", "version": "968972a918b5ed2d534295b639c54449d7de11cd", "versionType": "git"}]} +# CVE-2026-119718..119722 (published 2026-10-30): another CNA, vendor spelled +# "Blender Foundation", two lines: everything before the 4.5 LTS fix, and 5.x +# before 5.2.3. All five carry this same block. +NEW = [f"CVE-2026-1197{n}" for n in (18, 19, 20, 21, 22)] +B_119722 = {"vendor": "Blender Foundation", "product": "Blender", "defaultStatus": "unaffected", + "repo": "https://projects.blender.org/blender/blender", + "versions": [{"version": "0", "lessThan": "4.5.15", "status": "affected", + "versionType": "semver"}, + {"version": "5.0.0", "lessThan": "5.2.3", "status": "affected", + "versionType": "semver"}]} # CVE-2017-2899: one exact release. B_2899 = {"vendor": "Blender", "product": "Blender", "versions": [{"status": "affected", "version": "v2.78c"}]} @@ -54,7 +64,7 @@ def _build_index(): os.close(fd) with zipfile.ZipFile(path, "w") as zf: for cve_id, aff in (("CVE-2026-60103", B_60103), ("CVE-2017-2899", B_2899), - ("CVE-2022-0544", B_0544)): + ("CVE-2022-0544", B_0544), *((c, B_119722) for c in NEW)): year, num = cve_id.split("-")[1:] zf.writestr(f"cvelistV5-main/cves/{year}/{num[:-3]}xxx/{cve_id}.json", json.dumps({"cveMetadata": {"cveId": cve_id}, @@ -98,27 +108,39 @@ def demo(): assert c5.resolve(other) is None, other assert c5._pair_key("blender", "blender") == "blender" assert c5._pair_key("Blender", "Blender") == "blender" + assert c5._pair_key("Blender Foundation", "Blender") == "blender" assert c5._pair_key("n/a", "Blender") is None # --- end to end: index build → scan -------------------------------------- index = _build_index() - assert sorted(e["cve"] for e in index["blender"]) == ["CVE-2017-2899", "CVE-2026-60103"] - # The reference hosts: Intune 5.0.0, Wazuh 5.0.1. No patched build is named. - assert _scan(index, "Blender", "5.0.0") == [("CVE-2026-60103", ">5.1.2")] - assert _scan(index, "Blender", "5.0.1") == [("CVE-2026-60103", ">5.1.2")] - assert _scan(index, "Blender", "5.1.2") == [("CVE-2026-60103", ">5.1.2")] - assert _scan(index, "Blender", "3.0.0") == [("CVE-2026-60103", ">5.1.2")] - # Above the bound, and below the 3.0.0 floor: nothing. - assert _scan(index, "Blender", "5.1.3") == [] - assert _scan(index, "Blender", "2.93.18") == [] + assert sorted({e["cve"] for e in index["blender"]}) == sorted( + ["CVE-2017-2899", "CVE-2026-60103", *NEW]) + + def new(fix): + return [(c, fix) for c in NEW] + on_5x = sorted(new("5.2.3") + [("CVE-2026-60103", ">5.1.2")]) + on_lts = sorted(new("4.5.15") + [("CVE-2026-60103", ">5.1.2")]) + # The reference hosts: Intune 5.0.0, Wazuh 5.0.1. + assert _scan(index, "Blender", "5.0.0") == on_5x + assert _scan(index, "Blender", "5.0.1") == on_5x + assert _scan(index, "Blender", "5.1.2") == on_5x + # Between the two bounds: only the 5.x line of the new records. + assert _scan(index, "Blender", "5.1.3") == new("5.2.3") + assert _scan(index, "Blender", "5.2.2") == new("5.2.3") + assert _scan(index, "Blender", "5.2.3") == [] + # The 4.5 LTS line: fixed in 4.5.15, so the fix shown is that one, not 5.2.3. + assert _scan(index, "Blender", "4.5.14") == on_lts + assert _scan(index, "Blender", "4.5.15") == [("CVE-2026-60103", ">5.1.2")] + assert _scan(index, "Blender", "3.0.0") == on_lts + assert _scan(index, "Blender", "2.93.18") == new("4.5.15") # macOS lists the app as plain "Blender" too. - assert _scan(index, "Blender", "5.0.1", "macOS 15.5") == [("CVE-2026-60103", ">5.1.2")] + assert _scan(index, "Blender", "5.0.1", "macOS 15.5") == on_5x # A distro package: "+dfsg-2" is trimmed to 4.3.2, which says nothing about # backports. Linux is Wazuh's. for os_name in ("Debian GNU/Linux 13", "Ubuntu 24.04.3 LTS", "Linux"): assert _scan(index, "blender", "4.3.2+dfsg-2", os_name) == [], os_name - print("ok blender resolves, 5.0.0 and 5.0.1 get CVE-2026-60103, 5.1.3 and 2.93 do not") + print("ok blender resolves under both vendor spellings, both ranges of CVE-2026-119718..119722 hold") def test_blender():