Fix PoC discovery, freshness and source validation

This commit is contained in:
0xMarcio committed 2026-10-06 23:23:48 +02:00
1 parent d474e60c83
commit 82ab6a1c19
493 files changed
+1939 -3528

No files matched your search

+53
View File
@@ -1,7 +1,9 @@
from __future__ import annotations
import io
import json
import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
@@ -85,5 +87,56 @@ class ReferenceAuditTests(unittest.TestCase):
self.assertEqual([call.args[0].method for call in fetch.call_args_list], ["HEAD", "GET"])
class KevRefreshTests(unittest.TestCase):
def row(self, cve: str = "CVE-2026-12345") -> dict:
return {"cveID": cve, "dateAdded": "2026-10-06", "knownRansomwareCampaignUse": "Known"}
def test_incomplete_or_invalid_catalogue_preserves_stored_flags(self) -> None:
row = self.row()
invalid = [
{"count": 2, "vulnerabilities": [row]},
{"count": 2, "vulnerabilities": [row, row]},
{"count": 1, "vulnerabilities": [self.row("NOT-A-CVE")]},
{"count": 1, "vulnerabilities": [{"cveID": "CVE-2026-12345"}]},
{"vulnerabilities": [row]},
{"count": True, "vulnerabilities": [row]},
]
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "kev.json"
old = '{"CVE-2026-10000":["2026-01-01",0]}\n'
for payload in invalid:
path.write_text(old, encoding="utf-8")
with self.subTest(payload=payload), patch.object(audit_poc_links, "KEV_FILE", path), patch.object(
audit_poc_links, "http_json", return_value=payload
):
audit_poc_links.refresh_kev(dry_run=False)
self.assertEqual(path.read_text(), old)
def test_complete_catalogue_can_remove_entries(self) -> None:
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "kev.json"
for rows in ([self.row()], []):
path.write_text('{"CVE-2026-10000":["2026-01-01",0]}\n', encoding="utf-8")
with self.subTest(rows=rows), patch.object(audit_poc_links, "KEV_FILE", path), patch.object(
audit_poc_links, "http_json", return_value={"count": len(rows), "vulnerabilities": rows}
):
self.assertEqual(audit_poc_links.refresh_kev(dry_run=False), len(rows))
expected = {"CVE-2026-12345": ["2026-10-06", 1]} if rows else {}
self.assertEqual(json.loads(path.read_text()), expected)
def test_failed_atomic_replace_preserves_catalogue(self) -> None:
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "kev.json"
old = '{"CVE-2026-10000":["2026-01-01",0]}\n'
path.write_text(old, encoding="utf-8")
with patch.object(audit_poc_links, "KEV_FILE", path), patch.object(
audit_poc_links, "http_json", return_value={"count": 1, "vulnerabilities": [self.row()]}
), patch.object(Path, "replace", side_effect=OSError("Replace unavailable")):
with self.assertRaisesRegex(OSError, "Replace unavailable"):
audit_poc_links.refresh_kev(dry_run=False)
self.assertEqual(path.read_text(), old)
self.assertEqual(list(Path(directory).iterdir()), [path])
if __name__ == "__main__":
unittest.main()
+179
View File
@@ -0,0 +1,179 @@
from __future__ import annotations
import importlib.util
import io
import json
import re
import tempfile
import unittest
from contextlib import redirect_stdout
from datetime import datetime, timedelta, timezone
from pathlib import Path
from unittest.mock import patch
from urllib.parse import parse_qs, urlparse
ROOT = Path(__file__).resolve().parents[1]
SPEC = importlib.util.spec_from_file_location("getTrending", str(ROOT / ".github" / "getTrending.py"))
trending = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(trending)
NOW = datetime.now(timezone.utc)
CVE = f"CVE-{NOW.year}-21589"
def stamp(days: float) -> str:
return (NOW - timedelta(days=days)).strftime("%Y-%m-%dT%H:%M:%SZ")
def repo(full_name: str, created: object, stars: int = 0, description: str = "Proof of concept") -> dict:
return {
"full_name": full_name,
"name": full_name.split("/", 1)[1],
"html_url": f"https://github.com/{full_name}",
"description": description,
"stargazers_count": stars,
"created_at": created,
"pushed_at": stamp(0.1),
}
def gate(query: str, token: str) -> dict:
"""GraphQL content for the real PoC gate: a README and an exploit at the root."""
return {
alias: {
"readmeMd": {"text": f"Proof of concept for {name}"},
"root": {"entries": [{"name": "exploit.py", "type": "blob"}]},
}
for alias, name in re.findall(r'(r\d+): repository\(owner: "[^"]*", name: "([^"]*)"\)', query)
}
def shipped(names: list[str], token: str, paths: dict | None = None) -> dict[str, str]:
return {name: stamp(0.2) for name in names}
class JustLandedTests(unittest.TestCase):
def test_lists_new_repositories_for_any_cve_year_once(self) -> None:
rows = [
repo("example/CVE-2019-0708-poc", stamp(1)),
repo("example/CVE-2021-44228-rce", stamp(400)),
repo("example/cve-exploits", stamp(1), description="Exploit for CVE-2021-44228"),
repo("example/CVE-2024-1111", None),
repo("example/CVE-2024-2222", "last week"),
repo(f"Example/{CVE}", stamp(1), stars=5),
repo(f"example/{CVE.lower()}", stamp(1)),
]
queries: list[str] = []
def search(query: str) -> list[dict]:
queries.append(query)
return rows
with patch.multiple(trending, search=search, graphql=gate, code_pushed=shipped), \
redirect_stdout(io.StringIO()):
landed = trending.just_landed("")
self.assertCountEqual(
[found["full_name"] for found in landed],
["example/CVE-2019-0708-poc", f"Example/{CVE}"],
)
self.assertEqual(len(queries), 1)
self.assertIn("created:>=", queries[0])
self.assertNotRegex(queries[0], r"pushed:|CVE-\d{4}")
class MainTests(unittest.TestCase):
def test_new_trending_repository_is_in_both_sections_and_written_once(self) -> None:
both = repo(f"MarcusProgram/{CVE}", stamp(0.3), stars=5)
older = repo(f"example/CVE-{NOW.year}-1000-poc", stamp(300), stars=40)
fresh = repo("example/CVE-2019-0708-poc", stamp(1))
with tempfile.TemporaryDirectory() as tmp:
readme, output = Path(tmp, "README.md"), Path(tmp, "trending.json")
with patch.multiple(
trending,
README=str(readme),
TRENDING=str(output),
figures=lambda: {"total_cves": 3, "with_pocs": 2, "kev": 1},
known_exploited=lambda: {CVE},
search_year=lambda year, since: (2, [dict(both), dict(older)]) if year == NOW.year else (0, []),
search=lambda query: [dict(fresh), dict(both), dict(older)],
graphql=gate,
code_pushed=shipped,
), redirect_stdout(io.StringIO()) as log:
self.assertEqual(trending.main(), 0)
text = readme.read_text(encoding="utf-8")
items = json.loads(output.read_text(encoding="utf-8"))
landed, trend = text.split("## Just landed", 1)[1].split(f"## Trending in {NOW.year}", 1)
self.assertIn(f"]({both['html_url']})", landed)
self.assertIn(f"]({both['html_url']})", trend)
self.assertNotIn(older["html_url"], landed)
self.assertCountEqual(
[(item["url"], item.get("landed", False), item["kev"]) for item in items],
[
(both["html_url"], True, True),
(older["html_url"], False, False),
(fresh["html_url"], True, False),
],
)
self.assertIn("(3 repositories, 1 known-exploited)", log.getvalue())
class ArtifactDateTests(unittest.TestCase):
def test_commit_clock_cannot_postdate_the_repository_push(self) -> None:
pushed = stamp(0.2)
payload = {"r0": {
"pushedAt": pushed,
"defaultBranchRef": {"target": {
"p0": {"nodes": [{"committedDate": stamp(-1)}]},
"p1": {"nodes": [{"committedDate": stamp(1)}]},
}},
}}
with patch.object(trending, "graphql", return_value=payload) as query:
result = trending.code_pushed(["owner/repo"], "test", {"owner/repo": ["exploit.py", "poc.c"]})
self.assertEqual(result, {"owner/repo": pushed})
self.assertIn("pushedAt", query.call_args.args[0])
def test_future_timestamp_does_not_wrap_into_yesterdays_age(self) -> None:
self.assertEqual(trending.time_ago(stamp(-0.1)), "just now")
class SearchTests(unittest.TestCase):
def replies(self, *payloads: dict) -> tuple[list[str], object]:
urls: list[str] = []
queue = iter(payloads)
def urlopen(req, timeout):
urls.append(req.full_url)
return io.BytesIO(json.dumps(next(queue)).encode())
return urls, urlopen
def test_reads_every_page_in_rest_sort_order(self) -> None:
urls, urlopen = self.replies(
{"total_count": 101, "incomplete_results": False,
"items": [{"full_name": f"a/{n}"} for n in range(100)]},
{"total_count": 101, "incomplete_results": False, "items": [{"full_name": "a/100"}]},
)
with patch.object(trending.request, "urlopen", urlopen):
self.assertEqual(len(trending.search("CVE in:name")), 101)
params = [parse_qs(urlparse(url).query) for url in urls]
self.assertEqual(
[(p["sort"], p["order"], p["page"]) for p in params],
[(["updated"], ["desc"], ["1"]), (["updated"], ["desc"], ["2"])],
)
def test_incomplete_results_are_retried_and_never_returned(self) -> None:
partial = {"total_count": 9, "incomplete_results": True, "items": [{"full_name": "a/b"}]}
complete = {"total_count": 1, "incomplete_results": False, "items": [{"full_name": "a/b"}]}
with patch.object(trending.time, "sleep"):
_, urlopen = self.replies(partial, complete)
with patch.object(trending.request, "urlopen", urlopen):
self.assertEqual(trending.search("CVE in:name"), [{"full_name": "a/b"}])
_, urlopen = self.replies(partial, partial, partial)
with patch.object(trending.request, "urlopen", urlopen), self.assertRaises(RuntimeError):
trending.search("CVE in:name")
if __name__ == "__main__":
unittest.main()
+100
View File
@@ -0,0 +1,100 @@
from __future__ import annotations
import sys
import unittest
from pathlib import Path
from unittest.mock import patch
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
import sync_collections
class ResearchCollectionTests(unittest.TestCase):
def tree(self, *paths: str) -> dict:
return {"truncated": False, "tree": [
{"path": path, "type": "blob", "mode": "100644", "size": 100}
for path in paths
]}
def test_cve_directory_requires_its_own_artifact(self) -> None:
payload = self.tree(
"pocs/linux/CVE-2024-1001/exploit/lts/exploit.c",
"pocs/linux/CVE-2024-1001/docs/exploit.md",
"pocs/linux/CVE-2024-1002/README.md",
"pocs/linux/CVE-2024-1002/Makefile",
"pocs/linux/CVE-2024-1002/helpers.c",
"pocs/linux/CVE-2024-1002/tests/poc.py",
"pocs/linux/CVE-2024-1002/docs/poc.py",
"pocs/linux/CVE-2024-1003-and-CVE-2024-1004/exploit.c",
"pocs/linux/CVE-2024-1005/CVE-2024-1006/exploit.c",
"advisories/CVE-2024-1007/exploit.c",
)
self.assertEqual(sync_collections.collect_research_tree(
"google/security-research", "master", "pocs/", payload,
), {"CVE-2024-1001": [
"https://github.com/google/security-research/tree/master/pocs/linux/CVE-2024-1001",
]})
def test_security_lab_reproduction_files_and_code_qualify(self) -> None:
paths = (
"SecurityExploits/libcue/track_set_index_CVE-2023-43641/CVE-2023-43641-poc-simple.cue",
"SecurityExploits/poppler/CVE-2025-52885/bug.pdf",
"SecurityExploits/poppler/CVE-2025-52886/pdfgen.cpp",
"SecurityExploits/DjVuLibre/CVE-2025-53367/fuzzer-poc.djvu",
"SecurityExploits/libssh2/CVE-2019-17498/server/home/poc.bin",
"SecurityExploits/unknown/CVE-2024-1001/writeup.pdf",
)
found = sync_collections.collect_research_tree(
"github/securitylab", "main", "SecurityExploits/", self.tree(*paths),
)
self.assertEqual(set(found), {
"CVE-2023-43641", "CVE-2025-52885", "CVE-2025-52886", "CVE-2025-53367", "CVE-2019-17498",
})
def test_truncated_or_missing_tree_is_not_a_complete_source(self) -> None:
for payload in ({"truncated": True, "tree": []}, {"tree": []}, {"truncated": False}):
with self.subTest(payload=payload), self.assertRaises(ValueError):
sync_collections.collect_research_tree("google/security-research", "master", "pocs/", payload)
def test_reviewed_exploit_patches_qualify_but_generic_text_does_not(self) -> None:
payload = self.tree(
"SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/diff.txt",
"SecurityExploits/libssh2/out_of_bounds_read_kex_CVE-2019-13115/server/home/diff.txt",
"SecurityExploits/strongSwan/CVE-2018-5388/stroke_patch.txt",
"SecurityExploits/unknown/CVE-2024-1001/diff.txt",
"SecurityExploits/unknown/CVE-2024-1002/exploit.patch",
"SecurityExploits/unknown/CVE-2024-1003/stroke_patch.txt",
)
found = sync_collections.collect_research_tree("github/securitylab", "main", "SecurityExploits/", payload)
self.assertEqual(set(found), {"CVE-2023-2283", "CVE-2019-13115", "CVE-2018-5388"})
def test_failed_research_source_stops_before_any_index_mutation(self) -> None:
with patch.object(sys, "argv", ["sync_collections.py"]), \
patch.object(sync_collections, "SOURCES", ()), \
patch.object(sync_collections, "download_tree", return_value={"truncated": True, "tree": []}), \
patch.object(sync_collections, "ensure_cve_entries") as ensure:
self.assertEqual(sync_collections.main(), 1)
ensure.assert_not_called()
def test_research_links_merge_with_existing_collection_sources(self) -> None:
cid = "CVE-2024-1001"
payload = self.tree(f"pocs/linux/{cid}/exploit.c")
old_url = "https://github.com/chaitin/xray/blob/master/pocs/example.yml"
new_url = f"https://github.com/google/security-research/tree/master/pocs/linux/{cid}"
with patch.object(sys, "argv", ["sync_collections.py"]), \
patch.object(sync_collections, "SOURCES", (("xray", "chaitin/xray", "master"),)), \
patch.object(sync_collections, "TREE_SOURCES", (("google", "google/security-research", "master", "pocs/"),)), \
patch.object(sync_collections, "download", return_value=b"archive"), \
patch.object(sync_collections, "collect", return_value={cid: [old_url]}), \
patch.object(sync_collections, "download_tree", return_value=payload), \
patch.object(sync_collections, "ensure_cve_entries", return_value=(set(), set())), \
patch.object(sync_collections, "apply_links", return_value="unchanged") as apply, \
patch.object(sync_collections, "CVES") as cves:
cves.glob.return_value = []
self.assertEqual(sync_collections.main(), 0)
apply.assert_called_once_with(cid, sorted([old_url, new_url]), dry_run=False)
if __name__ == "__main__":
unittest.main()
+163
View File
@@ -0,0 +1,163 @@
from __future__ import annotations
import copy
import io
import sys
import tempfile
import unittest
from argparse import Namespace
from contextlib import redirect_stdout
from pathlib import Path
from unittest.mock import patch
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
import build_site
import sync_cve_metadata as metadata
import update_cves
CVE = "CVE-2026-12345"
URL = "https://github.com/advisories/GHSA-abcd-efgh-2345"
SCORE = ["3.1", 8.1, "HIGH", "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N", "CNA", "CVE Program CNA"]
class AdvisoryLifecycleTests(unittest.TestCase):
def merge(self, entries: dict, cache: dict) -> None:
with patch.object(metadata, "enrich_fallback_metrics"), patch.object(
metadata, "load_vendor_entries", return_value={}
):
metadata.merge_enrichment(entries, set(entries), cache)
def test_revocation_removes_only_github_owned_exploit_evidence(self) -> None:
for independent in (False, True):
for withdrawn in (False, True):
with self.subTest(independent=independent, withdrawn=withdrawn):
tags = ["Vendor Advisory", *(["Exploit"] if independent else [])]
entries = {CVE: {"advisories": [[URL, tags]]}}
self.merge(entries, {"GHSA": [CVE, URL, [], 1]})
self.assertTrue(build_site.reference_is_verified_poc(CVE, URL, entries, set()))
self.merge(entries, {} if withdrawn else {"GHSA": [CVE, URL, [], 0]})
self.assertEqual(
build_site.reference_is_verified_poc(CVE, URL, entries, set()), independent
)
self.assertIn("Vendor Advisory", entries[CVE]["advisories"][0][1])
def test_legacy_cache_only_advisory_is_removed(self) -> None:
entries = {CVE: {"advisories": [[URL, ["GitHub Advisory", "Exploit"]]]}}
self.merge(entries, {})
self.assertFalse(build_site.reference_is_verified_poc(CVE, URL, entries, set()))
self.assertFalse(entries[CVE].get("advisories"))
def test_legacy_mixed_source_evidence_is_preserved(self) -> None:
entries = {CVE: {"advisories": [[URL, ["Vendor Advisory", "GitHub Advisory", "Exploit"]]]}}
self.merge(entries, {})
self.assertEqual(entries[CVE]["advisories"], [[URL, ["Vendor Advisory", "Exploit"]]])
def test_withdrawal_prunes_only_unverified_github_owned_references(self) -> None:
cves = [CVE, "CVE-2026-12346", "CVE-2026-12347"]
entries = {cve: {"advisories": [[URL, ["GitHub Advisory", "Exploit"]]]} for cve in cves}
entries[cves[2]]["advisories"][0][1].append("Vendor Advisory")
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
(root / "2026").mkdir()
for cve in cves:
(root / "2026" / f"{cve}.md").write_text(
"### Description\n\nKeep this description.\n\n### POC\n\n"
f"#### Reference\n- {URL}\n- https://example.com/independent\n\n"
"#### Github\nNo PoCs found on GitHub currently.\n",
encoding="utf-8",
)
inventory = root / "references.txt"
inventory.write_text("".join(f"{cve} - {URL}\n" for cve in cves), encoding="utf-8")
verified = root / "reference_pocs.txt"
verified.write_text(f"{cves[1]} - {URL}\n", encoding="utf-8")
with patch.object(metadata, "CVES", root), patch.object(
update_cves, "REFERENCE_LIST", inventory
), patch.object(update_cves, "VERIFIED_REFERENCE_LIST", verified):
refreshed = run_sync(entries, [], dry_run=False, fallback=False)
for index, cve in enumerate(cves):
text = (root / "2026" / f"{cve}.md").read_text(encoding="utf-8")
self.assertEqual(URL in text, index != 0)
self.assertIn("Keep this description.", text)
self.assertIn("https://example.com/independent", text)
self.assertEqual(f"{cve} - {URL}" in inventory.read_text(), index != 0)
self.assertEqual(verified.read_text(), f"{cves[1]} - {URL}\n")
with patch.object(metadata, "CVES", root), patch.object(
update_cves, "REFERENCE_LIST", inventory
), patch.object(update_cves, "VERIFIED_REFERENCE_LIST", verified):
run_sync(refreshed, [{"cve": {"id": cves[2], "references": [
{"url": URL, "tags": ["Vendor Advisory"]}
]}}], dry_run=False, fallback=False)
self.assertNotIn(URL, (root / "2026" / f"{cves[2]}.md").read_text())
self.assertNotIn(f"{cves[2]} - {URL}", inventory.read_text())
def run_sync(entries: dict, feed: list, *, full: bool = False, dry_run: bool = True, fallback: bool = True) -> dict:
with patch.object(metadata, "parse_args", return_value=Namespace(
full=full, github_full=False, dry_run=dry_run, year=[] if full else [2026]
)), patch.object(metadata, "sync_github_cache", return_value={}), patch.object(
metadata, "sync_github_pocs"
), patch.object(metadata, "local_cves", return_value=set(entries)), patch.object(
metadata, "load_advisory_rules", return_value=[]
), patch.object(metadata, "load_shards", return_value=copy.deepcopy(entries)), patch.object(
metadata, "load_feed", return_value=feed
), patch.object(metadata, "load_vendor_entries", return_value={}), patch.object(
metadata, "write_shards", return_value=(1, 0)
) as write, patch.object(
metadata, "enrich_fallback_metrics",
wraps=metadata.enrich_fallback_metrics if fallback else lambda *args, **kwargs: None,
), redirect_stdout(io.StringIO()):
metadata.main()
return write.call_args.args[0]
class FallbackLifecycleTests(unittest.TestCase):
def setUp(self) -> None:
self.old = {CVE: {"cvss": [SCORE], "advisories": [["https://example.com/advisory", ["Vendor Advisory"]]]}}
self.feed = [{"cve": {"id": CVE, "references": [
{"url": "https://example.com/advisory", "tags": ["Vendor Advisory"]}
]}}]
def test_failed_or_incomplete_refresh_preserves_scores_in_incremental_and_full_runs(self) -> None:
for full in (False, True):
for response in (TimeoutError("Source unavailable"), {}, {"cveMetadata": {"cveId": "CVE-2026-99999"}}):
with self.subTest(full=full, response=response), patch.object(
metadata, "load_json_url",
side_effect=response if isinstance(response, Exception) else lambda *args, **kwargs: response,
):
result = run_sync(self.old, self.feed, full=full)
self.assertEqual(result[CVE].get("cvss"), [SCORE])
def test_confirmed_removal_clears_stale_scores(self) -> None:
empty_record = {"cveMetadata": {"cveId": CVE, "state": "PUBLISHED"}, "containers": {"cna": {}}}
for full in (False, True):
for response in (None, empty_record):
with self.subTest(full=full, response=response), patch.object(metadata, "load_json_url", return_value=response):
result = run_sync(self.old, self.feed, full=full)
self.assertNotIn("cvss", result[CVE])
def test_one_failed_source_keeps_prior_score(self) -> None:
def fetch(url: str, **kwargs: object) -> None:
if url.startswith(metadata.VULNRICHMENT_ROOT):
raise TimeoutError("CISA unavailable")
return None
with patch.object(metadata, "load_json_url", side_effect=fetch):
result = run_sync(self.old, self.feed, full=True)
self.assertEqual(result[CVE]["cvss"], [SCORE])
def test_successful_refresh_replaces_prior_score(self) -> None:
record = {
"cveMetadata": {"cveId": CVE, "state": "PUBLISHED"},
"containers": {"cna": {"providerMetadata": {"shortName": "CNA"}, "metrics": [{
"cvssV3_1": {"baseScore": 9.8, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"},
}]}},
}
with patch.object(metadata, "load_json_url", return_value=record):
result = run_sync(self.old, self.feed, full=True)
self.assertEqual([row[1] for row in result[CVE]["cvss"]], [9.8])
if __name__ == "__main__":
unittest.main()
+55
View File
@@ -0,0 +1,55 @@
from __future__ import annotations
import sys
import unittest
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
import sync_nuclei
CORE_VERSION_PROBE = '''id: CVE-2003-1598
info:
name: WordPress Core SQL Injection
http:
- method: GET
path:
- "{{BaseURL}}"
- "{{BaseURL}}/wp-admin/install.php"
- "{{BaseURL}}/feed/"
- "{{BaseURL}}/?feed=rss2"
matchers:
- type: dsl
dsl:
- compare_versions(version_by_generator, '< 0.72')
- compare_versions(version_by_js, '< 0.72')
- compare_versions(version_by_css, '< 0.72')
extractors:
- type: regex
name: version_by_generator
regex:
- 'wordpress.org/\\?v=([0-9.]+)'
'''
class VersionProbeTests(unittest.TestCase):
def test_wordpress_core_version_probe_is_not_a_poc(self) -> None:
self.assertTrue(sync_nuclei.version_probe_only(CORE_VERSION_PROBE))
def test_actual_exploit_is_kept_even_with_a_version_check(self) -> None:
for change in (
CORE_VERSION_PROBE.replace('"{{BaseURL}}/feed/"', '"{{BaseURL}}/?posts=1+UNION+SELECT+1"'),
CORE_VERSION_PROBE.replace('"{{BaseURL}}/feed/"', "'{{BaseURL}}/?posts=1+UNION+SELECT+1'"),
CORE_VERSION_PROBE.replace('"{{BaseURL}}/feed/"', '"{{RootURL}}/exploit"'),
CORE_VERSION_PROBE.replace('"{{BaseURL}}/feed/"', '"https://example.com/exploit"'),
CORE_VERSION_PROBE.replace("method: GET", "method: POST"),
CORE_VERSION_PROBE + '\n raw: ["GET /exploit HTTP/1.1"]\n',
CORE_VERSION_PROBE + '\ncode:\n - engine: [python3]\n source: exploit_code\n',
):
with self.subTest(template=change):
self.assertFalse(sync_nuclei.version_probe_only(change))
if __name__ == "__main__":
unittest.main()
+97
View File
@@ -0,0 +1,97 @@
from __future__ import annotations
import os
import sys
import unittest
from pathlib import Path
from unittest.mock import patch
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
import sync_pocingithub
import update_cves
class GitHubResponseTests(unittest.TestCase):
def readers(self):
client = update_cves.GitHubClient("test")
return (
("repo0", update_cves, lambda: client.fetch_readmes(["owner/repo"])),
("r0", sync_pocingithub, lambda: sync_pocingithub.describe(client, ["owner/repo"])),
)
def test_operational_graphql_errors_are_not_missing_repositories(self) -> None:
for alias, module, read in self.readers():
for error in (
{"type": "RATE_LIMITED", "message": "Rate limited"},
{"type": "INTERNAL", "message": "Internal error", "path": [alias]},
{"type": "NOT_FOUND", "message": "Nested lookup failed", "path": [alias, "root"]},
):
with self.subTest(reader=alias, error=error["message"]):
payload = {"data": {alias: None, "rateLimit": {"remaining": 100}}, "errors": [error]}
with patch.object(module, "http_json", return_value=payload):
with self.assertRaises(RuntimeError):
read()
def test_missing_repository_is_tolerated(self) -> None:
for alias, module, read in self.readers():
with self.subTest(reader=alias):
payload = {
"data": {alias: None, "rateLimit": {"remaining": 100}},
"errors": [{"type": "NOT_FOUND", "path": [alias], "message": "Repository missing"}],
}
with patch.object(module, "http_json", return_value=payload):
result = read()
self.assertFalse(result[0] if isinstance(result, tuple) else result)
def test_incomplete_response_is_not_a_missing_repository(self) -> None:
for alias, module, read in self.readers():
with self.subTest(reader=alias), patch.object(
module, "http_json", return_value={"data": {"rateLimit": {"remaining": 100}}}
):
with self.assertRaises(RuntimeError):
read()
def test_deleted_repository_does_not_discard_other_batch_results(self) -> None:
client = update_cves.GitHubClient("test")
names = ["owner/deleted", "owner/repo"]
for prefix, module, read in (
("repo", update_cves, lambda: client.fetch_readmes(names)),
("r", sync_pocingithub, lambda: sync_pocingithub.describe(client, names)),
):
payload = {
"data": {f"{prefix}0": None, f"{prefix}1": {"readmeMd": {"text": "PoC"}},
"rateLimit": {"remaining": 100}},
"errors": [{"type": "NOT_FOUND", "path": [f"{prefix}0"]}],
}
with self.subTest(reader=prefix), patch.object(module, "http_json", return_value=payload):
result = read()
self.assertEqual(set(result[0] if isinstance(result, tuple) else result), {"owner/repo"})
def test_near_exhausted_quota_aborts(self) -> None:
for alias, module, read in self.readers():
with self.subTest(reader=alias), patch.object(
module, "http_json", return_value={"data": {alias: {}, "rateLimit": {"remaining": 0}}}
):
with self.assertRaises(RuntimeError):
read()
def test_batch_transport_failure_aborts_before_writes(self) -> None:
with patch.object(sys, "argv", ["sync_pocingithub.py"]), patch.dict(
os.environ, {"GITHUB_TOKEN": "test"}
), patch.object(
sync_pocingithub, "candidates",
return_value={"CVE-2026-1234": {"https://github.com/owner/repo"}},
), patch.object(sync_pocingithub, "already_linked", return_value={}), patch.object(
sync_pocingithub, "describe", side_effect=RuntimeError("API unavailable")
), patch.object(sync_pocingithub, "ensure_cve_entries", return_value=(set(), set())) as ensure, patch.object(
sync_pocingithub, "reconcile_inventory", return_value=(0, 0)
) as reconcile:
with self.assertRaisesRegex(RuntimeError, "API unavailable"):
sync_pocingithub.main()
ensure.assert_not_called()
reconcile.assert_not_called()
if __name__ == "__main__":
unittest.main()
+43
View File
@@ -4,6 +4,7 @@ import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
@@ -326,6 +327,48 @@ No PoCs found on GitHub currently.
self.assertTrue(updated.endswith("\n\n"))
class DiscoveryFailureTests(unittest.TestCase):
def test_search_does_not_accept_partial_graphql_results(self) -> None:
payload = {
"data": {"search": {"repositoryCount": 1, "nodes": []}, "rateLimit": {"remaining": 100}},
"errors": [{"type": "INTERNAL", "message": "Search unavailable"}],
}
with patch.object(update_cves, "http_json", return_value=payload):
with self.assertRaisesRegex(RuntimeError, "Search unavailable"):
update_cves.GitHubClient("test").search_page("CVE-2026")
def test_readme_batch_failure_aborts_discovery(self) -> None:
repo = {
"nameWithOwner": "owner/CVE-2026-1234-poc",
"url": "https://github.com/owner/CVE-2026-1234-poc",
}
with patch.object(update_cves, "search_range", return_value=[repo]), patch.object(
update_cves.GitHubClient, "fetch_readmes", side_effect=RuntimeError("API unavailable")
):
with self.assertRaisesRegex(RuntimeError, "API unavailable"):
update_cves.discover_github_pocs(
"test", years=[2026], lookback_days=3, backfill=False, cve_filter=set()
)
def test_qualified_repository_survives_an_unverified_reference(self) -> None:
cve = "CVE-2026-1234"
url = "https://github.com/owner/CVE-2026-1234-poc"
details = update_cves.CVEDetails("A vulnerability.", [], [], [])
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
path = root / "2026" / f"{cve}.md"
path.parent.mkdir()
path.write_text(update_cves.build_markdown(cve, details, [], [url]), encoding="utf-8")
update_cves.update_existing_markdown(path, [url], [], dry_run=False)
with patch.object(build_site, "CVES", root), patch.object(
build_site, "load_metadata", return_value={}
), patch.object(build_site, "load_dates", return_value={}), patch.object(
build_site, "load_verified_references", return_value=set()
):
entries, _ = build_site.build_cve_list(set())
self.assertEqual([(entry["cve"], entry["poc"]) for entry in entries], [(cve, [url])])
class MetadataSourceTests(unittest.TestCase):
def test_uses_vector_prefix_when_source_field_is_mislabeled(self) -> None:
row = sync_cve_metadata.published_metric(