Files
T
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

172 lines
6.1 KiB
Python

#!/usr/bin/env python3
"""Vulnerability Scanning Workflow Agent - Automates scan orchestration and prioritization."""
import json
import logging
import os
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=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", timeout=30 # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
)
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=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", timeout=30 # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
)
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()