mirror of
https://github.com/0xMarcio/cve.git
synced 2026-10-27 16:51:03 +00:00
Mark the CVEs attackers are actually using
CISA publishes a catalogue of vulnerabilities observed being exploited in the wild, and 1,111 of its 1,685 entries are CVEs this index already holds PoCs for. That is the one fact a reader most wants before opening an exploit, and it was nowhere on the page. The weekly audit stores the catalogue, the build trims it to the CVEs the index carries, and a result row shows a KEV chip -- with the ransomware flag where CISA records one, which covers 352 of them. 37 KB served. A failed fetch keeps the stored catalogue rather than dropping the badge off every entry. The chip reuses the header pill's treatment: the palette still spends green on things you can click and amber on star counts only.
This commit is contained in:
1 parent
1377e03b6a
commit
1aa51fa852
7 files changed
+79
-1
No files matched your search
@@ -26,6 +26,11 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
STATE_FILE = ROOT / ".github" / "link_audit_state.json"
|
||||
GITHUB_LIST = ROOT / "github.txt"
|
||||
REPO_META = ROOT / "repo_meta.json"
|
||||
KEV_FILE = ROOT / "kev.json"
|
||||
KEV_URL = (
|
||||
"https://www.cisa.gov/sites/default/files/feeds/"
|
||||
"known_exploited_vulnerabilities.json"
|
||||
)
|
||||
GITHUB_SECTION = "#### Github"
|
||||
REFERENCE_SECTION = "#### Reference"
|
||||
GITHUB_EMPTY = "No PoCs found on GitHub currently."
|
||||
@@ -164,6 +169,34 @@ def prune_inventory(dead_urls: set[str], *, dry_run: bool) -> int:
|
||||
return len(lines) - len(kept)
|
||||
|
||||
|
||||
def refresh_kev(*, dry_run: bool) -> int:
|
||||
"""Store CISA's known-exploited catalogue: the CVEs attackers actually use.
|
||||
|
||||
Keeps only what the page shows, so the whole catalogue costs a few tens of
|
||||
kilobytes. A failure leaves the previous file in place rather than
|
||||
dropping the badge off every entry.
|
||||
"""
|
||||
try:
|
||||
payload = http_json(KEV_URL)
|
||||
except Exception as exc:
|
||||
print(f"Kept the stored KEV catalogue after a failed fetch: {exc}", file=sys.stderr)
|
||||
return 0
|
||||
entries = {}
|
||||
for item in (payload or {}).get("vulnerabilities") or []:
|
||||
cve_id = str(item.get("cveID") or "").upper()
|
||||
if cve_id:
|
||||
entries[cve_id] = [
|
||||
str(item.get("dateAdded") or "")[:10],
|
||||
1 if str(item.get("knownRansomwareCampaignUse") or "").lower() == "known" else 0,
|
||||
]
|
||||
if entries and not dry_run:
|
||||
KEV_FILE.write_text(
|
||||
json.dumps(entries, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
return len(entries)
|
||||
|
||||
|
||||
def load_cursor() -> str:
|
||||
try:
|
||||
return str(json.loads(STATE_FILE.read_text(encoding="utf-8")).get("cursor") or "")
|
||||
@@ -212,6 +245,7 @@ def main() -> int:
|
||||
|
||||
print(f"Removed {links} links from {entries} entries | {inventory} inventory lines")
|
||||
print(f"Refreshed stars and last-push for {len(metadata)} repositories | {tracked} tracked")
|
||||
print(f"KEV catalogue: {refresh_kev(dry_run=args.dry_run)} known-exploited CVEs")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
+17
-1
@@ -16,6 +16,8 @@ CVE_OUTPUT = DOCS_DIR / "CVE_list.json"
|
||||
TRENDING_OUTPUT = DOCS_DIR / "trending_poc.json"
|
||||
REPO_META = ROOT / "repo_meta.json"
|
||||
REPO_META_OUTPUT = DOCS_DIR / "repo_meta.json"
|
||||
KEV_INPUT = ROOT / "kev.json"
|
||||
KEV_OUTPUT = DOCS_DIR / "kev.json"
|
||||
def load_blacklist() -> set[str]:
|
||||
if not BLACKLIST.exists():
|
||||
return set()
|
||||
@@ -154,6 +156,16 @@ def build_repo_meta(cve_entries: List[Dict[str, object]]) -> Dict[str, object]:
|
||||
return {name: value for name, value in stored.items() if name in linked}
|
||||
|
||||
|
||||
def build_kev(cve_entries: List[Dict[str, object]]) -> Dict[str, object]:
|
||||
"""CISA's known-exploited entries, limited to CVEs the index carries."""
|
||||
try:
|
||||
stored = json.loads(KEV_INPUT.read_text(encoding="utf-8"))
|
||||
except (OSError, json.JSONDecodeError):
|
||||
return {}
|
||||
have = {str(entry["cve"]) for entry in cve_entries}
|
||||
return {cve: value for cve, value in stored.items() if cve in have}
|
||||
|
||||
|
||||
def build_trending(blacklist: Collection[str]) -> List[Dict[str, object]]:
|
||||
"""The trending job's own output, minus anything the blacklist covers."""
|
||||
try:
|
||||
@@ -185,6 +197,9 @@ def main() -> int:
|
||||
repo_meta = build_repo_meta(cve_payload)
|
||||
write_json(REPO_META_OUTPUT, repo_meta)
|
||||
|
||||
kev = build_kev(cve_payload)
|
||||
write_json(KEV_OUTPUT, kev)
|
||||
|
||||
trending_items = build_trending(blacklist)
|
||||
write_json(
|
||||
TRENDING_OUTPUT,
|
||||
@@ -199,7 +214,8 @@ def main() -> int:
|
||||
|
||||
print(
|
||||
f"Wrote {CVE_OUTPUT.name} ({len(cve_payload)} of {total_cves} CVEs), "
|
||||
f"{REPO_META_OUTPUT.name} ({len(repo_meta)} repositories) and {TRENDING_OUTPUT.name}"
|
||||
f"{REPO_META_OUTPUT.name} ({len(repo_meta)} repositories), "
|
||||
f"{KEV_OUTPUT.name} ({len(kev)} known-exploited) and {TRENDING_OUTPUT.name}"
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
Reference in new issue
Block a user