#!/usr/bin/env python3 """Greenbone/OpenVAS Vulnerability Management agent - creates scan targets, executes scans, and parses reports via python-gvm GMP protocol""" import argparse import json from collections import Counter, defaultdict from datetime import datetime from pathlib import Path try: from gvm.connections import UnixSocketConnection, TLSConnection from gvm.protocols.gmp import Gmp from gvm.transforms import EtreeTransform HAS_GVM = True except ImportError: HAS_GVM = False def load_data(path): return json.loads(Path(path).read_text(encoding="utf-8")) def connect_gvm(host, port=9390, socket_path="/run/gvmd/gvmd.sock", use_tls=False, username="admin", password="admin"): """Connect to GVM daemon and authenticate via GMP.""" if not HAS_GVM: return None, "python-gvm not installed" transform = EtreeTransform() if use_tls: conn = TLSConnection(hostname=host, port=port) else: conn = UnixSocketConnection(path=socket_path) gmp = Gmp(connection=conn, transform=transform) gmp.authenticate(username, password) version_resp = gmp.get_version() version = version_resp.xpath("version/text()")[0] if version_resp.xpath("version/text()") else "unknown" return gmp, version def create_target(gmp, name, hosts, port_list_id="33d0cd82-57c6-11e1-8ed1-406186ea4fc5"): """Create a scan target. Default port list is 'All IANA assigned TCP'.""" resp = gmp.create_target(name=name, hosts=hosts, port_list_id=port_list_id) target_id = resp.get("id", "") return target_id def create_and_start_task(gmp, task_name, target_id, config_id="daba56c8-73ec-11df-a475-002264764cea", scanner_id="08b69003-5fc2-4037-a479-93b440211c73"): """Create scan task and start it. Default config is 'Full and fast', default scanner is 'OpenVAS Default'.""" resp = gmp.create_task(name=task_name, config_id=config_id, target_id=target_id, scanner_id=scanner_id) task_id = resp.get("id", "") gmp.start_task(task_id) return task_id def parse_report_xml(report_xml): """Parse GMP report XML into structured findings.""" findings = [] results = report_xml.findall(".//result") if report_xml is not None else [] for result in results: host_el = result.find("host") host = host_el.text if host_el is not None else "" name_el = result.find("name") name = name_el.text if name_el is not None else "" threat_el = result.find("threat") threat = threat_el.text if threat_el is not None else "Log" severity_el = result.find("severity") cvss = float(severity_el.text) if severity_el is not None and severity_el.text else 0.0 nvt = result.find("nvt") oid = nvt.get("oid", "") if nvt is not None else "" cve_el = nvt.find("cve") if nvt is not None else None cve = cve_el.text if cve_el is not None and cve_el.text != "NOCVE" else "" desc_el = result.find("description") desc = (desc_el.text or "")[:200] if desc_el is not None else "" sev_label = "critical" if cvss >= 9.0 else "high" if cvss >= 7.0 else "medium" if cvss >= 4.0 else "low" findings.append({ "host": host, "vulnerability": name, "severity": sev_label, "cvss": cvss, "cve": cve, "nvt_oid": oid, "description": desc, }) return findings def analyze_offline_report(data): """Analyze pre-exported GVM report data (JSON format).""" findings = [] results = data.get("results", data.get("vulnerabilities", [])) if isinstance(data, list): results = data for r in results: cvss = r.get("cvss", r.get("severity_score", 0.0)) if isinstance(cvss, str): try: cvss = float(cvss) except ValueError: cvss = 0.0 sev_label = "critical" if cvss >= 9.0 else "high" if cvss >= 7.0 else "medium" if cvss >= 4.0 else "low" findings.append({ "host": r.get("host", r.get("ip", "")), "vulnerability": r.get("name", r.get("vulnerability", "")), "severity": sev_label, "cvss": cvss, "cve": r.get("cve", r.get("cves", "")), "nvt_oid": r.get("nvt_oid", r.get("oid", "")), "description": (r.get("description", r.get("summary", "")) or "")[:200], }) return findings def generate_report(input_path): data = load_data(input_path) findings = analyze_offline_report(data) sev = Counter(f["severity"] for f in findings) host_vulns = defaultdict(int) for f in findings: host_vulns[f["host"]] += 1 cve_list = [f["cve"] for f in findings if f["cve"]] findings.sort(key=lambda x: x["cvss"], reverse=True) return { "report": "greenbone_vulnerability_management", "generated_at": datetime.utcnow().isoformat() + "Z", "total_vulnerabilities": len(findings), "severity_summary": dict(sev), "hosts_scanned": len(host_vulns), "host_vulnerability_counts": dict(host_vulns), "unique_cves": len(set(cve_list)), "top_10_findings": findings[:10], "findings": findings, } def main(): ap = argparse.ArgumentParser(description="Greenbone Vulnerability Management Agent") ap.add_argument("--input", required=True, help="Input JSON with GVM scan results") ap.add_argument("--output", help="Output JSON report path") ap.add_argument("--host", help="GVM host for live scan (requires python-gvm)") ap.add_argument("--username", default="admin", help="GMP username") ap.add_argument("--password", default="admin", help="GMP password") args = ap.parse_args() report = generate_report(args.input) out = json.dumps(report, indent=2) if args.output: Path(args.output).write_text(out, encoding="utf-8") print(f"Report written to {args.output}") else: print(out) if __name__ == "__main__": main()