fix(app-scan): pair Blender's "Blender Foundation" spelling on cvelistV5

Field case: Blender 5.0.0 (Intune) and 5.0.1 (Wazuh) got CVE-2026-60103
but none of CVE-2026-119718..119722, published 2026-10-30.

The same CNA (VulnCheck) wrote the new records as vendor "Blender
Foundation" / product "Blender", while CVE-2026-60103 says "blender" /
"blender". Only the latter was paired, so the five records passed the byte
prefilter and were dropped at pairing. A fresher nightly index would not
have changed that. NVD has CVE-2026-119722 "Awaiting Analysis" with no
configuration, so cvelistV5 is still the only structured source.

Each record states two lines, "0" .. lessThan "4.5.15" and "5.0.0" ..
lessThan "5.2.3"; they are matched per line, so 5.0.x is shown the 5.2.3
fix and the 4.5 LTS line the 4.5.15 one. All five IDs go through the real
index build in the test. Index key bumped to v56.
This commit is contained in:
2026-10-31 16:23:57 +01:00
parent 49e7c55938
commit ac966cb7ed
3 changed files with 49 additions and 22 deletions
+9 -6
View File
@@ -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
+6 -4
View File
@@ -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
+34 -12
View File
@@ -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():