#!/usr/bin/env python3 """Vulnerability Scanning Workflow Agent - Automates scan orchestration and prioritization.""" import json import logging import argparse from datetime import datetime import requests import nmap logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") logger = logging.getLogger(__name__) def run_nmap_vuln_scan(targets, ports="1-1024"): """Run Nmap vulnerability scan against target hosts.""" scanner = nmap.PortScanner() logger.info("Starting Nmap vuln scan on %s ports %s", targets, ports) scanner.scan(hosts=targets, ports=ports, arguments="-sV --script=vulners,vulscan") results = [] for host in scanner.all_hosts(): for proto in scanner[host].all_protocols(): for port in scanner[host][proto]: service = scanner[host][proto][port] results.append({ "host": host, "port": port, "protocol": proto, "state": service["state"], "service": service.get("name", ""), "version": service.get("version", ""), "scripts": service.get("script", {}), }) logger.info("Nmap scan complete: %d service entries", len(results)) return results def fetch_cisa_kev(): """Fetch the CISA Known Exploited Vulnerabilities catalog.""" url = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json" resp = requests.get(url, timeout=30) resp.raise_for_status() kev_data = resp.json() kev_set = {v["cveID"] for v in kev_data["vulnerabilities"]} logger.info("Loaded %d CISA KEV entries", len(kev_set)) return kev_set def launch_nessus_scan(nessus_url, api_keys, scan_name, targets): """Launch a vulnerability scan via Nessus REST API.""" headers = {"X-ApiKeys": api_keys, "Content-Type": "application/json"} scan_config = { "uuid": "advanced", "settings": { "name": scan_name, "text_targets": targets, "launch": "ON_DEMAND", "enabled": True, }, } resp = requests.post( f"{nessus_url}/scans", headers=headers, json=scan_config, verify=False, timeout=30 ) resp.raise_for_status() scan_id = resp.json()["scan"]["id"] logger.info("Nessus scan created: ID %d", scan_id) requests.post( f"{nessus_url}/scans/{scan_id}/launch", headers=headers, verify=False, timeout=30 ) logger.info("Nessus scan %d launched", scan_id) return scan_id def prioritize_vulnerabilities(vulns, kev_set, asset_criticality_map): """Apply risk-based prioritization to vulnerability findings.""" for vuln in vulns: cvss = vuln.get("cvss", 0.0) cve = vuln.get("cve", "") host = vuln.get("host", "") criticality = asset_criticality_map.get(host, 1.0) risk_score = cvss * criticality if cve in kev_set: risk_score *= 1.5 vuln["kev"] = True else: vuln["kev"] = False vuln["risk_score"] = round(risk_score, 1) vuln["priority"] = ( "P1" if risk_score >= 13.5 else "P2" if risk_score >= 7.0 else "P3" if risk_score >= 4.0 else "P4" ) vulns.sort(key=lambda x: x["risk_score"], reverse=True) return vulns def create_servicenow_ticket(snow_url, token, vuln): """Create a ServiceNow incident ticket for a high-priority vulnerability.""" headers = {"Content-Type": "application/json", "Authorization": f"Bearer {token}"} ticket = { "short_description": f"[VULN] {vuln.get('cve', 'N/A')} on {vuln['host']}", "description": ( f"CVE: {vuln.get('cve', 'N/A')}\n" f"CVSS: {vuln.get('cvss', 0)}\n" f"Host: {vuln['host']}\n" f"Risk Score: {vuln['risk_score']}\n" f"CISA KEV: {'YES' if vuln.get('kev') else 'NO'}" ), "urgency": "1" if vuln.get("kev") else "2", "category": "Vulnerability", } resp = requests.post( f"{snow_url}/api/now/table/incident", headers=headers, json=ticket, timeout=30 ) logger.info("ServiceNow ticket created: %s", resp.json().get("result", {}).get("number")) return resp.json() def generate_report(vulns): """Generate vulnerability scan summary report.""" p1 = [v for v in vulns if v.get("priority") == "P1"] p2 = [v for v in vulns if v.get("priority") == "P2"] lines = [ "VULNERABILITY SCAN REPORT", "=" * 40, f"Date: {datetime.utcnow().strftime('%Y-%m-%d %H:%M UTC')}", f"Total Findings: {len(vulns)}", f"P1 Critical: {len(p1)}", f"P2 High: {len(p2)}", "", "TOP PRIORITY FINDINGS:", ] for v in p1[:10]: lines.append(f" {v.get('cve', 'N/A'):20s} {v['host']:15s} Score: {v['risk_score']}") print("\n".join(lines)) return lines def main(): parser = argparse.ArgumentParser(description="Vulnerability Scanning Workflow Agent") parser.add_argument("--targets", required=True, help="Target hosts/CIDR") parser.add_argument("--ports", default="1-1024", help="Port range to scan") parser.add_argument("--nessus-url", help="Nessus API URL") parser.add_argument("--nessus-keys", help="Nessus API keys (accessKey;secretKey)") parser.add_argument("--output", default="vuln_report.json") args = parser.parse_args() kev_set = fetch_cisa_kev() results = run_nmap_vuln_scan(args.targets, args.ports) if args.nessus_url and args.nessus_keys: launch_nessus_scan(args.nessus_url, args.nessus_keys, "Agent Scan", args.targets) prioritized = prioritize_vulnerabilities(results, kev_set, {}) generate_report(prioritized) with open(args.output, "w") as f: json.dump(prioritized, f, indent=2) logger.info("Report saved to %s", args.output) if __name__ == "__main__": main()