{cid}
{f'{esc(headline)}
' if headline else ''}{esc(desc)}
- {''.join(facts)}
#!/usr/bin/env python3 """Render one static page per CVE, at the site root. The index held 82,000 CVEs behind a single URL. Every one of them is a query somebody types ("CVE-2026-72898 poc"), and the pages that answered those queries belonged to other people. These are flat files at /CVE-2026-72898, no prefix and no extension: GitHub Pages resolves an extensionless request to the .html beside it, verified against the live site rather than assumed. Nothing here is committed. The payloads this reads are already gitignored and built in CI, so the pages go straight into the Pages artifact and git never sees them. Rendering all of them costs about fifteen seconds. """ from __future__ import annotations import html import hashlib import json import re import os import sys from collections import defaultdict from datetime import date, datetime, timezone from decimal import ROUND_HALF_UP, Decimal from functools import lru_cache from string import Template from urllib import error, request from urllib.parse import unquote, urlsplit ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir) sys.path.insert(0, os.path.join(ROOT, "scripts")) from brand import BRAND, DESCRIPTION, FONTS, REPO_LINK, SITE, SLUG, SOURCES_LINE, SPONSOR_LINK, SUBTITLE, TITLE, host DOCS = os.path.join(ROOT, "docs") RELATED = 6 GITHUB = "https://github.com/" SEVERITIES = {"NONE", "LOW", "MEDIUM", "HIGH", "CRITICAL"} LASTMOD = "page_lastmod.json" PAGE_STATE = "page_state.json" TREND_ROWS = 20 SOURCES = ( ("nuclei", "Nuclei templates", "projectdiscovery/nuclei-templates"), ("msf", "Metasploit modules", "rapid7/metasploit-framework"), ("edb", "ExploitDB entries", "exploit-db.com"), ("vulhub", "Vulhub environments", "vulhub/vulhub"), ("collections", "Exploit collections", ""), ) # As logic.js labels curated paths: by leaf for these (CURATED there), and # below these collection roots for the rest (COLLECTION_SOURCES there). NAMED_BY_LEAF = {"zan8in/afrog", "chaitin/xray", "helloexp/0day", "tzwlhack/vulnerability"} COLLECTION_ROOTS = {"google/security-research": "pocs/", "github/securitylab": "SecurityExploits/"} @lru_cache(maxsize=2) def asset_url(name: str) -> str: with open(os.path.join(DOCS, name), "rb") as handle: version = hashlib.sha256(handle.read()).hexdigest()[:16] return f"/{name}?v={version}" def load(name: str) -> dict | list: with open(os.path.join(DOCS, name), encoding="utf-8") as handle: return json.load(handle) def esc(value) -> str: return html.escape(str("" if value is None else value), quote=True) def json_ld(payload: dict) -> str: """Serialise for a " ends the element and everything after it becomes live markup. 78 descriptions in the corpus already contain that sequence; only the 155-character trim keeps them out of the payload today. Escaping to \\u00XX keeps the JSON identical to a parser and inert to the HTML tokeniser. """ text = json.dumps(payload, separators=(",", ":")) for char in ("<", ">", "&"): text = text.replace(char, f"\\u{ord(char):04x}") return text def short(text: str, limit: int) -> str: text = " ".join((text or "").split()) if len(text) <= limit: return text return text[: limit - 1].rsplit(" ", 1)[0].rstrip(".,;: ") + "…" def canonical_cvss(rows: list) -> list | None: """The first assessment, which is NVD's own where NVD scored the record. The search results take rows[0] and these pages took the highest score, so 18,434 CVEs showed one severity in the list and another on the page. The primary assessment is the authority, and matching it makes the two agree. """ for row in rows or []: if isinstance(row[1], (int, float)): return row return None def unique_cvss(rows: list) -> list: """One row per distinct assessment. NVD, a CNA and the GitHub Advisory Database frequently publish the same vector, and the source column that told them apart was never rendered, so 13,340 pages printed the same line two or three times over. """ seen: set = set() kept = [] for row in rows or []: if not isinstance(row[1], (int, float)): continue key = (str(row[0]), row[1], str(row[3])) if key in seen: continue seen.add(key) kept.append(row) return kept ASSESSORS = { "nvd@nist.gov": "NVD", "cve@mitre.org": "MITRE", "github advisory database": "GitHub", "psirt@us.ibm.com": "IBM", } def assessor(row: list) -> str: """Who scored it, from row[4]. row[5] is the assessment type, Primary or Secondary, and labelling with it left 129 pages showing two rows both marked "Secondary", the repetition the label was added to explain. row[4] names the party, which is the thing that actually differs. Scoring CNAs identify themselves by contact address or by UUID, so an address becomes its domain and a UUID stays generic. """ raw = str(row[4]).strip() if len(row) > 4 and row[4] else "" if not raw: return str(row[5]) if len(row) > 5 and row[5] else "" known = ASSESSORS.get(raw.lower()) if known: return known if "@" in raw: domain = raw.rsplit("@", 1)[1].lower() parts = [p for p in domain.split(".") if p not in ("com", "org", "net", "gov", "io")] return parts[0].upper() if parts else domain if re.fullmatch(r"[0-9a-f-]{32,36}", raw.lower()): return "CNA" return raw def ordinal(value: int) -> str: """1st, 2nd, 3rd, 4th. A hard-coded "th" printed 51th on 21,100 pages.""" if 11 <= value % 100 <= 13: return f"{value}th" return f"{value}{ {1: 'st', 2: 'nd', 3: 'rd'}.get(value % 10, 'th') }" def owner_of(url: str) -> str | None: repo = repo_of(url) return repo.split("/")[0] if repo else None def repo_of(url: str) -> str | None: """owner/repo, the key repo_meta is stored under.""" if not url.startswith(GITHUB): return None parts = [p for p in url[len(GITHUB):].split("/") if p] if len(parts) < 2: return None if [p.lower() for p in parts[2:4]] == ["security", "advisories"]: return None return f"{parts[0].lower()}/{re.sub(r'[.]git$', '', parts[1], flags=re.I).lower()}" def artifact_label(url: str) -> str: """A curated link's path inside its repository, as logic.js labels it. Empty for a repository root or a link that is not into a repository.""" repo = repo_of(url) if not repo: return "" parts = [part for part in urlsplit(url).path.split("/") if part][2:] if parts[:1] in (["tree"], ["blob"]): parts = parts[2:] if repo in NAMED_BY_LEAF: parts = parts[-1:] path = unquote("/".join(parts)) root = COLLECTION_ROOTS.get(repo, "") return path[len(root):] if root and path.startswith(root) and len(path) > len(root) else path def page_lastmod(entry: dict, data: dict) -> str: """Bootstrap date before a page has a published content fingerprint: the record's own.""" stamps = (entry.get("modified") or "", entry.get("published") or "") return max((s for s in stamps if len(s) == 10), default=data["today"]) def previous_page_state() -> dict: """Use the last successful deployment, including across fresh CI checkouts. A missing manifest bootstraps an existing or new site. Other failures stop the build rather than silently discarding the dates we already published. """ stamp = int(datetime.now(timezone.utc).timestamp()) url = f"{SITE}/{PAGE_STATE}?build={stamp}" try: with request.urlopen(url, timeout=30) as response: payload = json.load(response) except error.HTTPError as problem: if problem.code == 404: return {} raise if payload.get("version") != 1 or not isinstance(payload.get("pages"), dict): raise ValueError("Invalid published page-state manifest") return payload["pages"] def digest(payload: object) -> str: return hashlib.sha256(json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()).hexdigest() def tracked_state(key: str, payload: object, today: str, fallback: str, previous: dict, legacy=None) -> list[str]: fingerprint = digest(payload) old = previous.get(key) if old is not None: if not isinstance(old, list) or len(old) != 2: raise ValueError(f"Invalid published content date for {key}") date.fromisoformat(old[1]) unchanged = old[0] == fingerprint or (legacy is not None and old[0] == digest(legacy())) lastmod = old[1] if unchanged else today else: lastmod = today if previous else fallback return [fingerprint, lastmod] def content_state(entry: dict, data: dict, previous: dict) -> list[str]: """Track significant content: the record, every link, CVSS, advisories, Nuclei and KEV. Daily EPSS fluctuations, star counts, repository pushes and site-wide totals are deliberately excluded; they are not a new exploit, assessment, or vulnerability record. """ cid = entry["cve"] meta = data["meta"].get(cid) or {} payload = {"entry": entry, "cvss": meta.get("cvss"), "advisories": meta.get("advisories"), "nuclei": data["nuclei"].get(cid), "kev": data["kev"].get(cid)} def legacy() -> dict: # Manifests published while pushes were fingerprinted still match, so they keep their dates. repos = data.get("repo_meta") or {} pushed = {repo: stored[1] for url in entry.get("poc") or [] if (repo := repo_of(url)) and (stored := repos.get(repo)) and len(stored) > 1} return {**payload, "pushed": pushed} return tracked_state(cid, payload, data["today"], page_lastmod(entry, data), previous, legacy) def build_related(cves: list) -> dict: """CVEs that share a proof-of-concept author. A sitemap gets pages discovered; links get them crawled sooner and pass context. That only holds when the link means something. The year-adjacent fallback that used to fill this section had no relation behind it at all: CVE-2024-1235 is not related to CVE-2024-1234, it was merely numbered next, and 61,936 pages carried six such links apiece. A shared author is a real relation, so it is the only one kept; pages without one show no section. """ by_owner = defaultdict(list) for entry in cves: for url in entry.get("poc") or []: owner = owner_of(url) if owner: by_owner[owner].append(entry["cve"]) # An author with hundreds of PoCs relates nothing in particular. by_owner = {o: v for o, v in by_owner.items() if 1 < len(v) <= 40} related = {} for entry in cves: cid = entry["cve"] seen = {cid} picks = [] for url in entry.get("poc") or []: owner = owner_of(url) for other in by_owner.get(owner, ()): if other not in seen: seen.add(other) picks.append(other) if len(picks) >= RELATED: break if len(picks) >= RELATED: break if picks: related[cid] = picks[:RELATED] return related def link_rows(urls: list, repo_meta: dict, *, trusted: bool = False, collection: bool = False) -> str: """One row per source link. The repository list is crowd-sourced: anyone can publish a repository named after a CVE and land in it, so those stay nofollow. Nuclei, ExploitDB, Metasploit, Vulhub and the CVE and NVD records are curated destinations the index deliberately vouches for, and Google's guidance is to leave a link unqualified when the association is intended. A curated path shows neither its repository's stars nor its push date. Collection paths are labelled as search labels them, with the repository as the note. """ rel = "noopener" if trusted else "nofollow noopener" rows = [] for url in urls: label = url[len(GITHUB):] if url.startswith(GITHUB) else url path = artifact_label(url) if trusted else "" meta = None if path else repo_meta.get(repo_of(url) or "") note = f"{meta[0]}★ · {esc(meta[1])}" if meta else "" if path and collection: repository = "/".join(url[len(GITHUB):].split("/")[:2]) label, note = path, f"{esc(repository)}" rows.append( f'
{esc(row[3])}{esc(headline)}
' if headline else ''}{esc(desc)}
{len(entries):,} CVEs with public proof-of-concept exploits.
' f'{count:,} CVEs with public proof-of-concept exploits, in {len(year_blocks)} blocks of a thousand ids.
' f'{" · ".join(neighbours)}
' if neighbours else "")) pages[f"{year}.html"] = hub_shell(title, description, year, body, total) fallback = max(lastmod[f"CVE-{year}-{block}"] for block, _ in year_blocks) states[year] = tracked_state(year, [title, description, body], data["today"], fallback, previous) lastmod[year] = states[year][1] return {"pages": pages, "lastmod": lastmod, "states": states} def utc_day(value: object) -> str: """UTC date of an ISO date or timestamp, read as logic.js reads it: a time without a zone is UTC. Empty when there is no readable date.""" if not isinstance(value, str) or not re.match(r"\d{4}-\d{2}-\d{2}", value): return "" try: moment = datetime.fromisoformat(value.replace("Z", "+00:00")) except ValueError: return "" return (moment.astimezone(timezone.utc) if moment.tzinfo else moment).date().isoformat() def star_label(stars: int) -> str: """formatStars in logic.js. toFixed rounds the exact binary value half up, so 1,250 stars is 1.3k there where Python's format would print 1.2k.""" if stars < 1000: return str(stars) return f"{Decimal(stars / 1000).quantize(Decimal('0.1'), ROUND_HALF_UP)}k" def trend_rows(items: object, landed: bool) -> str: """A front-page list as logic.js renders it, in feed order. Just landed is dated by first release, Trending by latest artifact commit.""" rows = [] for item in (items if isinstance(items, list) else [])[:TREND_ROWS]: # A path in a collection does not have the collection's stars. stars = None if item.get("artifact") else item.get("stars") star_cell = "" if stars is None else f'{star_label(stars)} ★' popular = " is-popular" if stars is not None and stars >= 500 else "" # Off GitHub the last path segment is a bare id, so it keeps its host. source = str(item.get("source") or "").removeprefix("www.") name = (f"{source}/{item.get('name') or ''}" if item.get("artifact") and source and source != "github.com" else item.get("name")) flagged = 'KEV' if item.get("kev") else "" detail = (f'{esc(item.get("cve"))} details' if item.get("page") else "") rows.append( f'No page at this address. If you were after a CVE, search for it above, or browse the whole index.