#!/usr/bin/env python3 """Agent for performing endpoint vulnerability remediation tracking and validation.""" import json import argparse import subprocess import csv from datetime import datetime def parse_scan_report(csv_file): """Parse a vulnerability scan CSV report and prioritize remediation.""" with open(csv_file, "r", encoding="utf-8", errors="replace") as f: reader = csv.DictReader(f) rows = list(reader) vulns = [] for row in rows: severity = row.get("Severity", row.get("severity", row.get("Risk", ""))).lower() vulns.append({ "host": row.get("Host", row.get("IP", row.get("ip", ""))), "port": row.get("Port", row.get("port", "")), "cve": row.get("CVE", row.get("cve", row.get("Plugin ID", ""))), "title": row.get("Name", row.get("title", row.get("Summary", "")))[:200], "severity": severity, "solution": row.get("Solution", row.get("Fix", ""))[:300], }) by_severity = {} for v in vulns: s = v["severity"] by_severity[s] = by_severity.get(s, 0) + 1 critical_high = [v for v in vulns if v["severity"] in ("critical", "high")] return { "total_vulns": len(vulns), "by_severity": by_severity, "critical_high_count": len(critical_high), "remediation_queue": sorted(critical_high, key=lambda x: 0 if x["severity"] == "critical" else 1), } def check_windows_patches(): """Check installed Windows patches and identify missing ones.""" try: result = subprocess.run( ["wmic", "qfe", "get", "HotFixID,InstalledOn,Description", "/format:csv"], capture_output=True, text=True, timeout=30 ) from io import StringIO reader = csv.DictReader(StringIO(result.stdout)) patches = [{"id": r.get("HotFixID"), "date": r.get("InstalledOn"), "desc": r.get("Description")} for r in reader if r.get("HotFixID")] return {"installed_patches": len(patches), "patches": patches} except Exception as e: return {"error": str(e)} def validate_remediation(host, port, check_type="port_open"): """Validate that a vulnerability has been remediated.""" import socket result = {"host": host, "port": int(port), "check_type": check_type} if check_type == "port_open": sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) try: sock.connect((host, int(port))) result["status"] = "STILL_OPEN" result["remediated"] = False except (socket.timeout, ConnectionRefusedError, OSError): result["status"] = "CLOSED" result["remediated"] = True finally: sock.close() return result def generate_remediation_report(scan_file, output=None): """Generate a remediation plan from scan results.""" parsed = parse_scan_report(scan_file) plan = {"generated": datetime.utcnow().isoformat(), "source": scan_file} plan["summary"] = parsed["by_severity"] plan["total"] = parsed["total_vulns"] hosts = {} for v in parsed["remediation_queue"]: h = v["host"] if h not in hosts: hosts[h] = [] hosts[h].append(v) plan["by_host"] = {h: {"count": len(vs), "vulns": vs} for h, vs in hosts.items()} if output: with open(output, "w") as f: json.dump(plan, f, indent=2) return plan def main(): parser = argparse.ArgumentParser(description="Endpoint Vulnerability Remediation Agent") sub = parser.add_subparsers(dest="command") p = sub.add_parser("parse", help="Parse vulnerability scan report") p.add_argument("--scan-file", required=True) sub.add_parser("patches", help="Check installed Windows patches") v = sub.add_parser("validate", help="Validate remediation") v.add_argument("--host", required=True) v.add_argument("--port", required=True) r = sub.add_parser("report", help="Generate remediation report") r.add_argument("--scan-file", required=True) r.add_argument("--output", help="Output JSON file") args = parser.parse_args() if args.command == "parse": result = parse_scan_report(args.scan_file) elif args.command == "patches": result = check_windows_patches() elif args.command == "validate": result = validate_remediation(args.host, args.port) elif args.command == "report": result = generate_remediation_report(args.scan_file, args.output) else: parser.print_help() return print(json.dumps(result, indent=2, default=str)) if __name__ == "__main__": main()