Claude 24f816b6a3
Consolidate 22 sibling repos into layered organism structure
Place useful parts of the surrounding repos into sica-fondt by layer, per the
body model (Ada = membrane; brain/endocrine/capabilities/knowledge non-Ada):

- brain/        LLM reasoning + providers (dapr, hermes, MoMoA)
- capabilities/ REPRAG sidecars: hermes tools/skills, dapr tools, parallel
                dispatch, A51 channels, and the OSINT cluster
- knowledge/    LORAG corpus: 754 cyber-skills, agency personas, secure-coding,
                MITRE ATT&CK data
- reference/    defensive threat-reference (C3, shhbruh doc) + AdaYaml parser

License handling: AGPL sources (worldosint, advanced_evolution, mercury,
Reticulum) and GPL DeTTECT are SPEC-only clean-room/port descriptions — no
copyleft code copied. MIT/Apache/data parts copied as working trees.

Safety: shhbruh escape/persistence material and C3 covert-C2 kept as reference
only, not wired into the running organism. See CONSOLIDATION.md.

https://claude.ai/code/session_01UehUqEXXJJCsHoA4voCU5c
2026-06-10 06:53:01 +00:00

232 lines
7.8 KiB
Python

#!/usr/bin/env python3
"""
Endpoint Vulnerability Remediation Prioritizer
Parses vulnerability scan exports (Nessus CSV), enriches with EPSS scores,
cross-references CISA KEV catalog, and generates prioritized remediation plans.
"""
import json
import csv
import sys
import os
from datetime import datetime, timedelta
from collections import defaultdict
from urllib.request import urlopen
from urllib.error import URLError
CISA_KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
EPSS_API_URL = "https://api.first.org/data/v1/epss"
def load_nessus_csv(csv_path: str) -> list:
"""Parse Nessus scan export CSV into vulnerability records."""
vulns = []
with open(csv_path, "r", encoding="utf-8-sig") as f:
reader = csv.DictReader(f)
for row in reader:
cvss = 0.0
try:
cvss = float(row.get("CVSS v3.0 Base Score", "0") or
row.get("CVSS v2.0 Base Score", "0") or "0")
except ValueError:
pass
cve_list = [c.strip() for c in row.get("CVE", "").split(",") if c.strip()]
vuln = {
"plugin_id": row.get("Plugin ID", ""),
"plugin_name": row.get("Name", ""),
"severity": row.get("Risk", "None"),
"host": row.get("Host", ""),
"protocol": row.get("Protocol", ""),
"port": row.get("Port", ""),
"cvss_score": cvss,
"cves": cve_list,
"solution": row.get("Solution", ""),
"synopsis": row.get("Synopsis", ""),
"description": row.get("Description", "")[:200],
}
vulns.append(vuln)
return vulns
def fetch_cisa_kev() -> set:
"""Fetch CISA Known Exploited Vulnerabilities catalog."""
try:
with urlopen(CISA_KEV_URL, timeout=15) as resp:
data = json.loads(resp.read())
return {v["cveID"] for v in data.get("vulnerabilities", [])}
except (URLError, json.JSONDecodeError, KeyError) as e:
print(f"Warning: Could not fetch CISA KEV catalog: {e}")
return set()
def fetch_epss_scores(cves: list) -> dict:
"""Fetch EPSS scores for a list of CVEs."""
epss_scores = {}
batch_size = 100
for i in range(0, len(cves), batch_size):
batch = cves[i:i + batch_size]
cve_param = ",".join(batch)
url = f"{EPSS_API_URL}?cve={cve_param}"
try:
with urlopen(url, timeout=15) as resp:
data = json.loads(resp.read())
for item in data.get("data", []):
epss_scores[item["cve"]] = float(item.get("epss", 0))
except (URLError, json.JSONDecodeError) as e:
print(f"Warning: EPSS lookup failed for batch: {e}")
return epss_scores
def prioritize_vulnerabilities(vulns: list, kev_cves: set, epss_scores: dict) -> list:
"""Assign remediation priority based on risk scoring."""
for vuln in vulns:
cvss = vuln["cvss_score"]
cves = vuln["cves"]
in_kev = any(cve in kev_cves for cve in cves)
max_epss = max((epss_scores.get(cve, 0) for cve in cves), default=0)
vuln["in_cisa_kev"] = in_kev
vuln["epss_score"] = round(max_epss, 4)
if cvss >= 9.0 or in_kev or (cvss >= 7.0 and max_epss > 0.7):
vuln["priority"] = "P1"
vuln["sla_days"] = 14
elif cvss >= 7.0 and max_epss > 0.5:
vuln["priority"] = "P2"
vuln["sla_days"] = 30
elif cvss >= 4.0:
vuln["priority"] = "P3"
vuln["sla_days"] = 60
else:
vuln["priority"] = "P4"
vuln["sla_days"] = 90
vuln["sla_deadline"] = (
datetime.utcnow() + timedelta(days=vuln["sla_days"])
).strftime("%Y-%m-%d")
vulns.sort(key=lambda v: (
{"P1": 0, "P2": 1, "P3": 2, "P4": 3}.get(v["priority"], 4),
-v["cvss_score"],
-v["epss_score"],
))
return vulns
def generate_remediation_plan(vulns: list, output_path: str) -> None:
"""Generate prioritized remediation plan in JSON."""
summary = defaultdict(int)
hosts_affected = defaultdict(set)
for vuln in vulns:
summary[vuln["priority"]] += 1
hosts_affected[vuln["priority"]].add(vuln["host"])
plan = {
"report_generated": datetime.utcnow().isoformat() + "Z",
"summary": {
"total_vulnerabilities": len(vulns),
"by_priority": dict(summary),
"unique_hosts": {p: len(h) for p, h in hosts_affected.items()},
},
"kev_vulnerabilities": [v for v in vulns if v.get("in_cisa_kev")],
"remediation_queue": [
{
"priority": v["priority"],
"sla_deadline": v["sla_deadline"],
"host": v["host"],
"plugin_name": v["plugin_name"],
"cvss": v["cvss_score"],
"epss": v["epss_score"],
"cisa_kev": v["in_cisa_kev"],
"cves": v["cves"],
"solution": v["solution"],
}
for v in vulns
],
}
with open(output_path, "w", encoding="utf-8") as f:
json.dump(plan, f, indent=2)
def export_remediation_csv(vulns: list, output_path: str) -> None:
"""Export remediation plan to CSV for team assignment."""
with open(output_path, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow([
"Priority", "SLA Deadline", "Host", "Vulnerability",
"CVSS", "EPSS", "CISA KEV", "CVEs", "Solution",
"Status", "Assigned To", "Notes",
])
for v in vulns:
writer.writerow([
v["priority"], v["sla_deadline"], v["host"],
v["plugin_name"], v["cvss_score"], v["epss_score"],
v["in_cisa_kev"], "; ".join(v["cves"]),
v["solution"][:200], "Open", "", "",
])
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python process.py <nessus_export.csv>")
print()
print("Parses Nessus CSV export, enriches with EPSS/CISA KEV data,")
print("and generates a prioritized remediation plan.")
sys.exit(1)
csv_path = sys.argv[1]
if not os.path.exists(csv_path):
print(f"Error: File not found: {csv_path}")
sys.exit(1)
print("Parsing vulnerability scan results...")
vulns = load_nessus_csv(csv_path)
print(f"Loaded {len(vulns)} vulnerability findings")
all_cves = list({cve for v in vulns for cve in v["cves"]})
print(f"Unique CVEs: {len(all_cves)}")
print("Fetching CISA KEV catalog...")
kev_cves = fetch_cisa_kev()
print(f"KEV catalog contains {len(kev_cves)} CVEs")
print("Fetching EPSS scores...")
epss_scores = fetch_epss_scores(all_cves) if all_cves else {}
print(f"Retrieved EPSS scores for {len(epss_scores)} CVEs")
print("Prioritizing vulnerabilities...")
vulns = prioritize_vulnerabilities(vulns, kev_cves, epss_scores)
base = os.path.splitext(os.path.basename(csv_path))[0]
out_dir = os.path.dirname(csv_path) or "."
plan_path = os.path.join(out_dir, f"{base}_remediation_plan.json")
generate_remediation_plan(vulns, plan_path)
print(f"Remediation plan: {plan_path}")
csv_out = os.path.join(out_dir, f"{base}_remediation.csv")
export_remediation_csv(vulns, csv_out)
print(f"Remediation CSV: {csv_out}")
print(f"\n--- Remediation Summary ---")
for p in ["P1", "P2", "P3", "P4"]:
count = sum(1 for v in vulns if v["priority"] == p)
if count:
print(f" {p}: {count} vulnerabilities")
kev_count = sum(1 for v in vulns if v.get("in_cisa_kev"))
if kev_count:
print(f"\n CISA KEV matches: {kev_count} (mandatory remediation)")