Files
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

198 lines
7.0 KiB
Python

#!/usr/bin/env python3
"""Vulnerability scanning agent using the Nessus REST API."""
import json
import sys
import time
import os
import urllib3
try:
import requests
except ImportError:
print("Install: pip install requests")
sys.exit(1)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class NessusAPI:
def __init__(self, url=None, access_key=None, secret_key=None):
url = url or os.environ.get("NESSUS_URL", "https://localhost:8834")
self.url = url.rstrip("/")
self.session = requests.Session()
self.session.verify = False
if access_key and secret_key:
self.session.headers.update({
"X-ApiKeys": f"accessKey={access_key}; secretKey={secret_key}"
})
def _get(self, endpoint):
resp = self.session.get(f"{self.url}{endpoint}", timeout=30)
resp.raise_for_status()
return resp.json()
def _post(self, endpoint, data=None):
resp = self.session.post(f"{self.url}{endpoint}", json=data, timeout=30)
resp.raise_for_status()
return resp.json()
def _put(self, endpoint, data=None):
resp = self.session.put(f"{self.url}{endpoint}", json=data, timeout=30)
resp.raise_for_status()
return resp.json()
def get_server_status(self):
return self._get("/server/status")
def list_scans(self):
data = self._get("/scans")
scans = []
for scan in data.get("scans", []):
scans.append({
"id": scan["id"], "name": scan["name"],
"status": scan["status"],
"folder_id": scan.get("folder_id"),
})
return scans
def get_scan_details(self, scan_id):
data = self._get(f"/scans/{scan_id}")
info = data.get("info", {})
hosts = data.get("hosts", [])
vulns = data.get("vulnerabilities", [])
return {
"scan_id": scan_id,
"name": info.get("name"),
"status": info.get("status"),
"host_count": info.get("hostcount", len(hosts)),
"targets": info.get("targets"),
"start_time": info.get("scanner_start"),
"end_time": info.get("scanner_end"),
"policy": info.get("policy"),
"severity_counts": {
"critical": sum(1 for v in vulns if v.get("severity") == 4),
"high": sum(1 for v in vulns if v.get("severity") == 3),
"medium": sum(1 for v in vulns if v.get("severity") == 2),
"low": sum(1 for v in vulns if v.get("severity") == 1),
"info": sum(1 for v in vulns if v.get("severity") == 0),
},
"vulnerabilities": [
{
"plugin_id": v["plugin_id"],
"name": v["plugin_name"],
"severity": v["severity"],
"count": v["count"],
"family": v.get("plugin_family"),
}
for v in sorted(vulns, key=lambda x: -x.get("severity", 0))[:50]
],
}
def create_scan(self, name, targets, policy_id=None, template="advanced"):
templates = self._get("/editor/scan/templates")
template_uuid = None
for t in templates.get("templates", []):
if t["name"] == template:
template_uuid = t["uuid"]
break
if not template_uuid:
template_uuid = templates["templates"][0]["uuid"]
scan_config = {
"uuid": template_uuid,
"settings": {
"name": name,
"text_targets": targets,
"launch_now": False,
},
}
if policy_id:
scan_config["settings"]["policy_id"] = policy_id
return self._post("/scans", scan_config)
def launch_scan(self, scan_id):
return self._post(f"/scans/{scan_id}/launch")
def get_scan_status(self, scan_id):
data = self._get(f"/scans/{scan_id}")
return data.get("info", {}).get("status", "unknown")
def wait_for_scan(self, scan_id, poll_interval=30, timeout=7200):
elapsed = 0
while elapsed < timeout:
status = self.get_scan_status(scan_id)
if status == "completed":
return True
if status in ("canceled", "aborted"):
return False
time.sleep(poll_interval)
elapsed += poll_interval
return False
def export_scan(self, scan_id, fmt="csv"):
data = self._post(f"/scans/{scan_id}/export", {"format": fmt})
file_id = data.get("file")
if not file_id:
return None
while True:
status = self._get(f"/scans/{scan_id}/export/{file_id}/status")
if status.get("status") == "ready":
break
time.sleep(5)
resp = self.session.get(f"{self.url}/scans/{scan_id}/export/{file_id}/download", timeout=30)
return resp.content
def check_auth_status(self, scan_id):
"""Check if authenticated scanning succeeded per host."""
data = self._get(f"/scans/{scan_id}")
auth_results = []
for host in data.get("hosts", []):
host_id = host["host_id"]
host_detail = self._get(f"/scans/{scan_id}/hosts/{host_id}")
auth_info = None
for vuln in host_detail.get("vulnerabilities", []):
if vuln["plugin_id"] == 19506:
auth_info = vuln
break
auth_results.append({
"hostname": host.get("hostname"),
"host_id": host_id,
"critical": host.get("critical", 0),
"high": host.get("high", 0),
"authenticated": auth_info is not None,
})
return auth_results
def print_scan_report(details):
print("Vulnerability Scan Report")
print("=" * 50)
print(f"Scan: {details['name']}")
print(f"Status: {details['status']}")
print(f"Hosts: {details['host_count']}")
print(f"Targets: {details['targets']}")
sev = details["severity_counts"]
print(f"\nSeverity Summary:")
print(f" Critical: {sev['critical']}")
print(f" High: {sev['high']}")
print(f" Medium: {sev['medium']}")
print(f" Low: {sev['low']}")
print(f" Info: {sev['info']}")
print(f"\nTop Vulnerabilities:")
for v in details["vulnerabilities"][:15]:
sev_label = {4: "CRIT", 3: "HIGH", 2: "MED", 1: "LOW", 0: "INFO"}.get(v["severity"], "?")
print(f" [{sev_label}] {v['name']} (plugin {v['plugin_id']}, count: {v['count']})")
if __name__ == "__main__":
nessus_url = os.environ.get("NESSUS_URL", "https://localhost:8834")
access_key = os.environ.get("NESSUS_ACCESS_KEY", "")
secret_key = os.environ.get("NESSUS_SECRET_KEY", "")
api = NessusAPI(nessus_url, access_key, secret_key)
scans = api.list_scans()
if scans:
details = api.get_scan_details(scans[0]["id"])
print_scan_report(details)
else:
print("No scans found. Create one with api.create_scan()")