Files
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands
- Fix 49 verify=False in defensive skills (env-var override)
- Add timeout to 231 HTTP/subprocess/socket calls
- Fix 6 SQL injection patterns with whitelist validation
- Replace 8 __import__() with standard imports
- Remove 701 unused imports across 442 files
- Add authorized-testing disclaimers to all offensive skills
- Complete 11 incomplete skill directories
- Expand 10 stub SKILL.md files with full content
- Fix 2 YAML parse errors in frontmatter
- Fix 5 pre-existing syntax errors
- Convert 22 hardcoded paths/ports to environment variables
- Back up 21 redundant skill pairs to .bak
- Fix 2 global declaration errors
- 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE)
- 0 compile errors across all 724 agent.py files
2026-03-19 13:26:49 +01:00

240 lines
8.7 KiB
Python

#!/usr/bin/env python3
"""CISA KEV CVE prioritization agent.
Downloads the CISA Known Exploited Vulnerabilities (KEV) catalog and
cross-references it against a list of CVEs from vulnerability scans to
prioritize remediation based on active exploitation status, due dates,
and vendor/product impact.
"""
import argparse
import csv
import io
import json
import os
import sys
from datetime import datetime, timezone
try:
import requests
except ImportError:
print("[!] 'requests' library required: pip install requests", file=sys.stderr)
sys.exit(1)
KEV_JSON_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
def download_kev_catalog():
"""Download the latest CISA KEV catalog as JSON."""
print(f"[*] Downloading CISA KEV catalog from {KEV_JSON_URL}")
resp = requests.get(KEV_JSON_URL, timeout=30)
resp.raise_for_status()
data = resp.json()
vulns = data.get("vulnerabilities", [])
catalog_version = data.get("catalogVersion", "unknown")
count = data.get("count", len(vulns))
date_released = data.get("dateReleased", "unknown")
print(f"[+] KEV catalog v{catalog_version}: {count} vulnerabilities "
f"(released: {date_released})")
return vulns, {
"catalog_version": catalog_version,
"count": count,
"date_released": date_released,
}
def build_kev_index(kev_vulns):
"""Build a lookup dict keyed by CVE ID for fast matching."""
index = {}
for vuln in kev_vulns:
cve_id = vuln.get("cveID", "")
if cve_id:
index[cve_id.upper()] = {
"cve_id": cve_id,
"vendor": vuln.get("vendorProject", ""),
"product": vuln.get("product", ""),
"vulnerability_name": vuln.get("vulnerabilityName", ""),
"date_added": vuln.get("dateAdded", ""),
"short_description": vuln.get("shortDescription", ""),
"required_action": vuln.get("requiredAction", ""),
"due_date": vuln.get("dueDate", ""),
"known_ransomware_campaign": vuln.get("knownRansomwareCampaignUse", "Unknown"),
"notes": vuln.get("notes", ""),
}
return index
def load_cve_list(source):
"""Load CVE list from file (one CVE per line or CSV) or comma-separated string."""
cves = []
if os.path.isfile(source):
with open(source, "r") as f:
content = f.read()
for line in content.strip().splitlines():
line = line.strip().strip(",").strip('"')
if line.upper().startswith("CVE-"):
cves.append(line.upper())
elif "," in line:
parts = line.split(",")
for part in parts:
part = part.strip().strip('"')
if part.upper().startswith("CVE-"):
cves.append(part.upper())
else:
for part in source.split(","):
part = part.strip()
if part.upper().startswith("CVE-"):
cves.append(part.upper())
return list(set(cves))
def prioritize_cves(cve_list, kev_index):
"""Cross-reference CVEs against KEV catalog and prioritize."""
in_kev = []
not_in_kev = []
for cve_id in cve_list:
kev_entry = kev_index.get(cve_id)
if kev_entry:
entry = dict(kev_entry)
entry["in_kev"] = True
entry["priority"] = "CRITICAL"
if entry.get("known_ransomware_campaign", "").lower() == "known":
entry["priority"] = "CRITICAL-RANSOMWARE"
try:
due = datetime.strptime(entry["due_date"], "%Y-%m-%d")
if due < datetime.now():
entry["overdue"] = True
entry["priority"] = "CRITICAL-OVERDUE"
else:
entry["overdue"] = False
except (ValueError, KeyError):
entry["overdue"] = False
in_kev.append(entry)
else:
not_in_kev.append({
"cve_id": cve_id,
"in_kev": False,
"priority": "STANDARD",
})
# Sort: overdue first, then ransomware, then by due date
priority_order = {"CRITICAL-OVERDUE": 0, "CRITICAL-RANSOMWARE": 1, "CRITICAL": 2}
in_kev.sort(key=lambda x: (priority_order.get(x["priority"], 9), x.get("due_date", "")))
return in_kev, not_in_kev
def format_summary(in_kev, not_in_kev, total_cves, catalog_info):
"""Print a human-readable prioritization report."""
print(f"\n{'='*60}")
print(f" CISA KEV CVE Prioritization Report")
print(f"{'='*60}")
print(f" KEV Catalog : v{catalog_info['catalog_version']} "
f"({catalog_info['count']} total KEVs)")
print(f" Input CVEs : {total_cves}")
print(f" In KEV : {len(in_kev)} (actively exploited)")
print(f" Not in KEV : {len(not_in_kev)}")
overdue = [v for v in in_kev if v.get("overdue")]
ransomware = [v for v in in_kev if "RANSOMWARE" in v.get("priority", "")]
print(f"\n Priority Breakdown:")
print(f" CRITICAL-OVERDUE : {len(overdue)}")
print(f" CRITICAL-RANSOMWARE : {len(ransomware)}")
print(f" CRITICAL (in KEV) : {len(in_kev)}")
print(f" STANDARD (not in KEV): {len(not_in_kev)}")
if in_kev:
print(f"\n KEV-Listed CVEs (prioritize remediation):")
for v in in_kev[:20]:
overdue_flag = " [OVERDUE]" if v.get("overdue") else ""
ransomware_flag = " [RANSOMWARE]" if "RANSOMWARE" in v.get("priority", "") else ""
print(f" {v['cve_id']:18s} | Due: {v.get('due_date', 'N/A'):10s} | "
f"{v.get('vendor', ''):15s} | {v.get('product', ''):20s}"
f"{overdue_flag}{ransomware_flag}")
if v.get("required_action"):
print(f" Action: {v['required_action'][:70]}")
def kev_stats(kev_vulns):
"""Compute statistics about the KEV catalog."""
by_vendor = {}
ransomware_count = 0
for v in kev_vulns:
vendor = v.get("vendorProject", "Unknown")
by_vendor[vendor] = by_vendor.get(vendor, 0) + 1
if v.get("knownRansomwareCampaignUse", "").lower() == "known":
ransomware_count += 1
print(f"\n KEV Catalog Statistics:")
print(f" Total vulnerabilities : {len(kev_vulns)}")
print(f" Ransomware-associated : {ransomware_count}")
print(f" Top vendors:")
for vendor, count in sorted(by_vendor.items(), key=lambda x: -x[1])[:10]:
print(f" {vendor:30s}: {count}")
def main():
parser = argparse.ArgumentParser(
description="CISA KEV CVE prioritization agent"
)
parser.add_argument("--cves", required=True,
help="CVE list: comma-separated string or path to file")
parser.add_argument("--kev-file",
help="Path to local KEV JSON (skip download)")
parser.add_argument("--stats", action="store_true",
help="Show KEV catalog statistics")
parser.add_argument("--output", "-o", help="Output JSON report path")
parser.add_argument("--verbose", "-v", action="store_true")
args = parser.parse_args()
if args.kev_file:
print(f"[*] Loading local KEV file: {args.kev_file}")
with open(args.kev_file, "r") as f:
data = json.load(f)
kev_vulns = data.get("vulnerabilities", [])
catalog_info = {
"catalog_version": data.get("catalogVersion", "local"),
"count": len(kev_vulns),
"date_released": data.get("dateReleased", "unknown"),
}
else:
kev_vulns, catalog_info = download_kev_catalog()
if args.stats:
kev_stats(kev_vulns)
kev_index = build_kev_index(kev_vulns)
cve_list = load_cve_list(args.cves)
print(f"[*] Loaded {len(cve_list)} CVE(s) to check")
in_kev, not_in_kev = prioritize_cves(cve_list, kev_index)
format_summary(in_kev, not_in_kev, len(cve_list), catalog_info)
report = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"tool": "CISA KEV Prioritization",
"catalog_info": catalog_info,
"input_cve_count": len(cve_list),
"kev_matched": len(in_kev),
"kev_unmatched": len(not_in_kev),
"prioritized": in_kev,
"standard_priority": not_in_kev,
"risk_level": (
"CRITICAL" if any(v.get("overdue") for v in in_kev)
else "HIGH" if in_kev
else "LOW"
),
}
if args.output:
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(f"\n[+] Report saved to {args.output}")
elif args.verbose:
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()