#!/usr/bin/env python3
"""Authenticated vulnerability scan orchestration agent using Nessus API."""

import json
import os
import sys
import argparse
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=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()
        return resp.json()

    def _post(self, path, data=None):
        resp = requests.post(f"{self.base_url}{path}", headers=self.headers,
                             json=data,
                             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()
        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()
