#!/usr/bin/env python3 """Agent for implementing EPSS (Exploit Prediction Scoring System) for vulnerability prioritization.""" import json import argparse import csv try: import requests except ImportError: requests = None EPSS_API_URL = "https://api.first.org/data/v1/epss" def get_epss_scores(cve_list): """Fetch EPSS scores for a list of CVE IDs from the FIRST.org API.""" if not requests: return {"error": "requests library not installed"} results = [] # API supports up to 100 CVEs per request for i in range(0, len(cve_list), 100): batch = cve_list[i:i + 100] params = {"cve": ",".join(batch)} resp = requests.get(EPSS_API_URL, params=params, timeout=30) resp.raise_for_status() data = resp.json() for item in data.get("data", []): results.append({ "cve": item["cve"], "epss": float(item["epss"]), "percentile": float(item["percentile"]), }) return {"total": len(results), "scores": results} def get_epss_csv(): """Download the full EPSS score CSV from FIRST.org.""" if not requests: return {"error": "requests library not installed"} resp = requests.get(f"{EPSS_API_URL}?envelope=true&pretty=true", timeout=60) resp.raise_for_status() return resp.json() def prioritize_vulnerabilities(cve_scores, epss_threshold=0.1, percentile_threshold=0.9): """Prioritize vulnerabilities based on EPSS score and percentile.""" critical = [] high = [] medium = [] low = [] for item in cve_scores: epss = item["epss"] pct = item["percentile"] if epss >= epss_threshold or pct >= percentile_threshold: item["priority"] = "CRITICAL" critical.append(item) elif epss >= 0.05: item["priority"] = "HIGH" high.append(item) elif epss >= 0.01: item["priority"] = "MEDIUM" medium.append(item) else: item["priority"] = "LOW" low.append(item) return { "thresholds": {"epss": epss_threshold, "percentile": percentile_threshold}, "summary": { "critical": len(critical), "high": len(high), "medium": len(medium), "low": len(low), }, "critical": sorted(critical, key=lambda x: x["epss"], reverse=True), "high": sorted(high, key=lambda x: x["epss"], reverse=True), } def enrich_from_scan(scan_file, output_file=None): """Enrich a vulnerability scan CSV with EPSS scores.""" with open(scan_file, "r") as f: reader = csv.DictReader(f) rows = list(reader) cve_col = None for col in ["CVE", "cve", "CVE-ID", "cve_id", "vulnerability_id"]: if col in (rows[0] if rows else {}): cve_col = col break if not cve_col: return {"error": "No CVE column found in scan file"} cves = [row[cve_col] for row in rows if row.get(cve_col, "").startswith("CVE-")] if not cves: return {"error": "No CVE IDs found in scan file"} epss_data = get_epss_scores(cves) epss_map = {s["cve"]: s for s in epss_data.get("scores", [])} enriched = [] for row in rows: cve = row.get(cve_col, "") epss_info = epss_map.get(cve, {}) row["epss_score"] = epss_info.get("epss", "N/A") row["epss_percentile"] = epss_info.get("percentile", "N/A") enriched.append(row) if output_file: fieldnames = list(enriched[0].keys()) with open(output_file, "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(enriched) prioritized = prioritize_vulnerabilities( [s for s in epss_data.get("scores", [])] ) return { "scan_file": scan_file, "total_cves": len(cves), "enriched_count": sum(1 for r in enriched if r["epss_score"] != "N/A"), "prioritization": prioritized["summary"], "top_10_exploitable": prioritized.get("critical", [])[:10], } def main(): parser = argparse.ArgumentParser(description="EPSS Vulnerability Prioritization Agent") sub = parser.add_subparsers(dest="command") s = sub.add_parser("score", help="Get EPSS scores for CVE IDs") s.add_argument("--cves", nargs="+", required=True, help="CVE IDs (e.g., CVE-2024-1234)") e = sub.add_parser("enrich", help="Enrich vulnerability scan with EPSS scores") e.add_argument("--scan-file", required=True, help="CSV vulnerability scan report") e.add_argument("--output", help="Output enriched CSV file") args = parser.parse_args() if args.command == "score": epss = get_epss_scores(args.cves) result = prioritize_vulnerabilities(epss.get("scores", [])) result["raw_scores"] = epss["scores"] elif args.command == "enrich": result = enrich_from_scan(args.scan_file, args.output) else: parser.print_help() return print(json.dumps(result, indent=2, default=str)) if __name__ == "__main__": main()