Files
Anthropic-Cybersecurity-Skills/skills/scanning-infrastructure-with-nessus/scripts/agent.py
T

184 lines
6.1 KiB
Python

#!/usr/bin/env python3
"""Agent for scanning infrastructure with Tenable Nessus.
Interacts with the Nessus REST API to create scan policies,
launch scans, monitor progress, retrieve results, and generate
vulnerability reports with severity-based prioritization.
"""
import json
import os
import sys
import time
import urllib3
from pathlib import Path
from datetime import datetime
try:
import requests
except ImportError:
requests = None
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class NessusScanAgent:
"""Manages Nessus vulnerability scans via REST API."""
def __init__(self, host=None, username="admin",
password="", output_dir="./nessus_scan"):
self.base_url = (host or os.environ.get("NESSUS_URL", "https://localhost:8834")).rstrip("/")
self.username = username
self.password = password
self.token = None
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
def _req(self, method, path, data=None):
"""Make authenticated request to Nessus API."""
if not requests:
return {"error": "requests library required: pip install requests"}
headers = {"Content-Type": "application/json"}
if self.token:
headers["X-Cookie"] = f"token={self.token}"
url = f"{self.base_url}{path}"
resp = requests.request(method, url, json=data, 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
try:
return resp.json()
except (json.JSONDecodeError, ValueError):
return {"status_code": resp.status_code, "text": resp.text[:200]}
def authenticate(self):
"""Authenticate and obtain session token."""
result = self._req("POST", "/session",
{"username": self.username, "password": self.password})
self.token = result.get("token")
return bool(self.token)
def list_scans(self):
"""List all configured scans."""
result = self._req("GET", "/scans")
scans = []
for s in result.get("scans", []):
scans.append({
"id": s.get("id"),
"name": s.get("name"),
"status": s.get("status"),
"last_modification_date": s.get("last_modification_date"),
})
return scans
def create_scan(self, name, targets, template_uuid=None):
"""Create a new scan configuration."""
data = {
"uuid": template_uuid or "ab4bacd2-05f6-425c-9d79-3f5a0a1f28b0",
"settings": {
"name": name,
"text_targets": targets,
"enabled": True,
"launch": "ON_DEMAND",
},
}
return self._req("POST", "/scans", data)
def launch_scan(self, scan_id):
"""Launch a configured scan."""
return self._req("POST", f"/scans/{scan_id}/launch")
def get_scan_status(self, scan_id):
"""Get current scan status."""
result = self._req("GET", f"/scans/{scan_id}")
info = result.get("info", {})
return {
"status": info.get("status"),
"host_count": info.get("hostcount", 0),
"name": info.get("name"),
}
def get_scan_results(self, scan_id):
"""Retrieve scan results with vulnerability details."""
result = self._req("GET", f"/scans/{scan_id}")
hosts = []
for h in result.get("hosts", []):
hosts.append({
"hostname": h.get("hostname"),
"host_id": h.get("host_id"),
"critical": h.get("critical", 0),
"high": h.get("high", 0),
"medium": h.get("medium", 0),
"low": h.get("low", 0),
"info": h.get("info", 0),
})
vulns = []
for v in result.get("vulnerabilities", []):
vulns.append({
"plugin_id": v.get("plugin_id"),
"plugin_name": v.get("plugin_name"),
"severity": v.get("severity"),
"count": v.get("count", 0),
"family": v.get("plugin_family"),
})
return {
"scan_id": scan_id,
"hosts": hosts,
"vulnerabilities": sorted(vulns, key=lambda x: x.get("severity", 0), reverse=True),
"total_vulns": len(vulns),
}
def export_scan(self, scan_id, fmt="nessus"):
"""Export scan results in specified format."""
result = self._req("POST", f"/scans/{scan_id}/export", {"format": fmt})
file_id = result.get("file")
if not file_id:
return {"error": "Export failed"}
for _ in range(60):
status = self._req("GET", f"/scans/{scan_id}/export/{file_id}/status")
if status.get("status") == "ready":
break
time.sleep(5)
return {"file_id": file_id, "format": fmt, "status": "ready"}
def generate_report(self, scan_id):
results = self.get_scan_results(scan_id)
report = {
"report_date": datetime.utcnow().isoformat(),
"scan_id": scan_id,
"results": results,
}
out = self.output_dir / "nessus_report.json"
with open(out, "w") as f:
json.dump(report, f, indent=2)
print(json.dumps(report, indent=2))
return report
def main():
if len(sys.argv) < 3:
print("Usage: agent.py <nessus_url> <scan_id> [--user admin] [--pass password]")
sys.exit(1)
host = sys.argv[1]
scan_id = int(sys.argv[2])
user = "admin"
password = ""
if "--user" in sys.argv:
user = sys.argv[sys.argv.index("--user") + 1]
if "--pass" in sys.argv:
password = sys.argv[sys.argv.index("--pass") + 1]
agent = NessusScanAgent(host, user, password)
if agent.authenticate():
agent.generate_report(scan_id)
else:
print("Authentication failed")
sys.exit(1)
if __name__ == "__main__":
main()