Files
mukul975 c21af3347e Complete folder anatomy for all 649 cybersecurity skills + update LICENSE to Mahipal
- Add scripts/agent.py and references/api-reference.md to all remaining skills
- Update all 648 LICENSE files: copyright now reads 'Mahipal'
- Add implementing-security-monitoring-with-datadog (new skill with full anatomy)
- All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
2026-03-11 00:22:12 +01:00

179 lines
6.5 KiB
Python

#!/usr/bin/env python3
"""Authenticated vulnerability scan orchestration agent using Nessus API."""
import json
import sys
import argparse
import time
from datetime import datetime
try:
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
except ImportError:
print("Install: pip install requests")
sys.exit(1)
class NessusClient:
"""Client for Tenable Nessus REST API."""
def __init__(self, url, access_key, secret_key):
self.base_url = url.rstrip("/")
self.headers = {
"X-ApiKeys": f"accessKey={access_key}; secretKey={secret_key}",
"Content-Type": "application/json",
}
def _get(self, path, params=None):
resp = requests.get(f"{self.base_url}{path}", headers=self.headers,
params=params, verify=False, timeout=30)
resp.raise_for_status()
return resp.json()
def _post(self, path, data=None):
resp = requests.post(f"{self.base_url}{path}", headers=self.headers,
json=data, verify=False, timeout=30)
resp.raise_for_status()
return resp.json()
def list_scans(self):
return self._get("/scans").get("scans", [])
def get_scan_details(self, scan_id):
return self._get(f"/scans/{scan_id}")
def list_policies(self):
return self._get("/policies").get("policies", [])
def list_credentials(self):
return self._get("/credentials").get("credentials", [])
def get_scan_results(self, scan_id):
details = self.get_scan_details(scan_id)
hosts = details.get("hosts", [])
vulns = details.get("vulnerabilities", [])
return {"hosts": hosts, "vulnerabilities": vulns, "info": details.get("info", {})}
def get_host_vulnerabilities(self, scan_id, host_id):
return self._get(f"/scans/{scan_id}/hosts/{host_id}")
def export_scan(self, scan_id, fmt="nessus"):
data = {"format": fmt}
resp = self._post(f"/scans/{scan_id}/export", data)
return resp.get("file", 0)
def analyze_scan_results(results):
"""Analyze scan results and categorize findings."""
vulns = results.get("vulnerabilities", [])
severity_map = {0: "Info", 1: "Low", 2: "Medium", 3: "High", 4: "Critical"}
severity_counts = {"Critical": 0, "High": 0, "Medium": 0, "Low": 0, "Info": 0}
categorized = []
for v in vulns:
sev = severity_map.get(v.get("severity", 0), "Info")
severity_counts[sev] += 1
categorized.append({
"plugin_id": v.get("plugin_id", 0),
"plugin_name": v.get("plugin_name", ""),
"severity": sev,
"severity_index": v.get("severity", 0),
"count": v.get("count", 0),
"family": v.get("plugin_family", ""),
})
categorized.sort(key=lambda x: x["severity_index"], reverse=True)
return {"severity_counts": severity_counts, "vulnerabilities": categorized}
def audit_credential_coverage(results):
"""Check if authenticated scan achieved credential coverage."""
findings = []
hosts = results.get("hosts", [])
for host in hosts:
host_info = host.get("info", {})
credentialed = host.get("credentialed_checks_running", "")
if not credentialed or credentialed == "no":
findings.append({
"host": host.get("hostname", host.get("host_id", "")),
"issue": "Credentialed checks not running — scan is unauthenticated",
"severity": "HIGH",
"detail": "Configure valid credentials for this host",
})
return findings
def compare_auth_vs_unauth(auth_results, unauth_results):
"""Compare authenticated vs unauthenticated scan finding counts."""
auth_vulns = len(auth_results.get("vulnerabilities", []))
unauth_vulns = len(unauth_results.get("vulnerabilities", []))
improvement = ((auth_vulns - unauth_vulns) / max(unauth_vulns, 1)) * 100
return {
"authenticated_findings": auth_vulns,
"unauthenticated_findings": unauth_vulns,
"improvement_pct": round(improvement, 1),
"additional_findings": auth_vulns - unauth_vulns,
}
def run_audit(args):
"""Execute authenticated vulnerability scan audit."""
print(f"\n{'='*60}")
print(f" AUTHENTICATED VULNERABILITY SCAN AUDIT")
print(f" Generated: {datetime.utcnow().isoformat()} UTC")
print(f"{'='*60}\n")
client = NessusClient(args.nessus_url, args.access_key, args.secret_key)
report = {}
scans = client.list_scans()
report["total_scans"] = len(scans) if scans else 0
print(f"--- AVAILABLE SCANS ({report['total_scans']}) ---")
for s in (scans or [])[:10]:
print(f" [{s.get('status','')}] {s.get('name','')}: {s.get('id','')}")
if args.scan_id:
results = client.get_scan_results(args.scan_id)
analysis = analyze_scan_results(results)
report["analysis"] = analysis
counts = analysis["severity_counts"]
print(f"\n--- SCAN RESULTS (ID: {args.scan_id}) ---")
print(f" Critical: {counts['Critical']} | High: {counts['High']} | "
f"Medium: {counts['Medium']} | Low: {counts['Low']}")
print(f"\n--- TOP VULNERABILITIES ---")
for v in analysis["vulnerabilities"][:15]:
print(f" [{v['severity']}] {v['plugin_name'][:70]} (x{v['count']})")
cred_check = audit_credential_coverage(results)
report["credential_coverage"] = cred_check
if cred_check:
print(f"\n--- CREDENTIAL COVERAGE ISSUES ({len(cred_check)}) ---")
for c in cred_check:
print(f" [{c['severity']}] {c['host']}: {c['issue']}")
else:
print(f"\n Credential coverage: All hosts authenticated")
return report
def main():
parser = argparse.ArgumentParser(description="Authenticated Vulnerability Scan Agent")
parser.add_argument("--nessus-url", required=True, help="Nessus server URL")
parser.add_argument("--access-key", required=True, help="Nessus API access key")
parser.add_argument("--secret-key", required=True, help="Nessus API secret key")
parser.add_argument("--scan-id", type=int, help="Scan ID to analyze results")
parser.add_argument("--output", help="Save report to JSON file")
args = parser.parse_args()
report = run_audit(args)
if args.output:
with open(args.output, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"\n[+] Report saved to {args.output}")
if __name__ == "__main__":
main()