From 0aa40a5f49ebecece3ca9e7bf79faf2617f3a161 Mon Sep 17 00:00:00 2001 From: 0xMarcio Date: Sun, 30 Aug 2026 17:38:58 +0200 Subject: [PATCH] Reject CVE ids that are a truncation of the one a repo is about A README caption in maniakh/CVE-2026-31431---Copy-Fail-PoC reads "Copy Fail (CVE-2026-3143)", one digit short. That still parses as a valid identifier, and the guard meant to catch it was satisfied by the repo's generic exploit.py, so the PoC was filed against an unrelated WordPress advisory. CVE-2026-8222 (Open5GS) collected a GiveWP lab the same way. Drop an id from the same year that a longer mentioned id extends, unless the repository name deliberately carries both. Underscores are word characters, so the id regex matched neither half of a name like CVE-2022-4174_CVE-2022-41742; read the separator-normalised name too. Over 600 live candidates that recovers two real PoCs the gap was hiding, Keycloak_CVE-2026-18963_PoC and CVE-2026-42945_NginxRift. --- scripts/update_cves.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/scripts/update_cves.py b/scripts/update_cves.py index 8756be61aff..673c7c4328b 100755 --- a/scripts/update_cves.py +++ b/scripts/update_cves.py @@ -244,6 +244,20 @@ def root_has_code(repo: dict[str, Any]) -> bool: return any(CODE_FILE_RE.search(name) for name in root_entry_names(repo)) +def truncated_ids(cve_ids: Collection[str]) -> set[str]: + """Identifiers that a longer identifier from the same year extends.""" + numbers: dict[str, set[str]] = defaultdict(set) + for cve_id in cve_ids: + _, year, number = cve_id.split("-") + numbers[year].add(number) + return { + f"CVE-{year}-{number}" + for year, values in numbers.items() + for number in values + if any(other.startswith(number) for other in values - {number}) + } + + def readme_has_poc_context(readme: str, cve_id: str, full_name: str = "") -> bool: year, number = cve_id.split("-")[1:] pattern = re.compile(rf"\bCVE[-_ ]{re.escape(year)}[-_ ]{re.escape(number)}\b", re.IGNORECASE) @@ -282,10 +296,20 @@ def qualifying_repo_cves( description = str(repo.get("description") or "") topics = " ".join(topic_names(repo)) readme = readme_text(repo) - name_cves = extract_cves(full_name, year) + # Underscores are word characters, so a raw name like CVE-A_CVE-B matches + # neither identifier; read the separator-normalised form as well. + name_cves = extract_cves(full_name, year) | extract_cves(normalise_identity(full_name), year) description_cves = extract_cves(description, year) topic_cves = extract_cves(topics, year) readme_cves = extract_cves(readme, year) + # A caption or description that drops a digit — "CVE-2023-3883" written for + # CVE-2023-38831 — still parses as a valid identifier and would otherwise + # enrol the repository against whatever unrelated advisory owns it. Trust + # the repository name, which may deliberately carry both identifiers. + truncated = truncated_ids(name_cves | description_cves | topic_cves | readme_cves) - name_cves + description_cves -= truncated + topic_cves -= truncated + readme_cves -= truncated identity_cves = name_cves | description_cves | topic_cves identity_text = normalise_identity(f"{full_name} {description}") identity_has_poc = bool(POC_RE.search(identity_text))