mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 21:54:56 +03:00
246 lines
9.1 KiB
Python
246 lines
9.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Rapid7 InsightVM vulnerability scanning agent.
|
|
|
|
Interfaces with the InsightVM (Nexpose) REST API to manage scan
|
|
configurations, launch scans, retrieve vulnerability results, and
|
|
generate remediation reports. Supports site management, asset
|
|
discovery, and vulnerability prioritization.
|
|
"""
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
from datetime import datetime, timezone
|
|
|
|
try:
|
|
import requests
|
|
from requests.auth import HTTPBasicAuth
|
|
except ImportError:
|
|
print("[!] 'requests' required: pip install requests", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
|
|
def get_insightvm_config():
|
|
"""Return InsightVM API connection config."""
|
|
host = os.environ.get("INSIGHTVM_HOST", "localhost")
|
|
port = os.environ.get("INSIGHTVM_PORT", "3780")
|
|
user = os.environ.get("INSIGHTVM_USER", "")
|
|
password = os.environ.get("INSIGHTVM_PASSWORD", "")
|
|
return f"https://{host}:{port}", user, password
|
|
|
|
|
|
def api_call(base_url, endpoint, user, password, method="GET",
|
|
data=None, params=None):
|
|
"""Make authenticated API call to InsightVM."""
|
|
url = f"{base_url}/api/3{endpoint}"
|
|
auth = HTTPBasicAuth(user, password)
|
|
headers = {"Content-Type": "application/json", "Accept": "application/json"}
|
|
if method == "POST":
|
|
resp = requests.post(url, auth=auth, headers=headers, json=data,
|
|
params=params,
|
|
verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", timeout=60) # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
|
|
elif method == "PUT":
|
|
resp = requests.put(url, auth=auth, headers=headers, json=data,
|
|
verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", timeout=60) # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
|
|
else:
|
|
resp = requests.get(url, auth=auth, headers=headers, params=params,
|
|
verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", timeout=60) # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
|
|
def list_sites(base_url, user, password):
|
|
"""List all scan sites."""
|
|
print("[*] Listing sites...")
|
|
data = api_call(base_url, "/sites", user, password, params={"size": 500})
|
|
sites = []
|
|
for site in data.get("resources", []):
|
|
sites.append({
|
|
"id": site.get("id"),
|
|
"name": site.get("name", ""),
|
|
"description": site.get("description", ""),
|
|
"type": site.get("type", ""),
|
|
"assets": site.get("assets", 0),
|
|
"last_scan_time": site.get("lastScanTime", ""),
|
|
"risk_score": site.get("riskScore", 0),
|
|
})
|
|
print(f"[+] Found {len(sites)} sites")
|
|
return sites
|
|
|
|
|
|
def get_site_vulnerabilities(base_url, user, password, site_id):
|
|
"""Get vulnerabilities for a specific site."""
|
|
print(f"[*] Fetching vulnerabilities for site {site_id}...")
|
|
vulns = []
|
|
page = 0
|
|
while True:
|
|
data = api_call(base_url, f"/sites/{site_id}/vulnerabilities",
|
|
user, password, params={"page": page, "size": 100})
|
|
resources = data.get("resources", [])
|
|
if not resources:
|
|
break
|
|
for v in resources:
|
|
vulns.append({
|
|
"id": v.get("id", ""),
|
|
"title": v.get("title", ""),
|
|
"severity": v.get("severity", ""),
|
|
"cvss_v3_score": v.get("cvss", {}).get("v3", {}).get("score", 0),
|
|
"risk_score": v.get("riskScore", 0),
|
|
"instances": v.get("instances", 0),
|
|
"status": v.get("status", ""),
|
|
})
|
|
page += 1
|
|
total_pages = data.get("page", {}).get("totalPages", 1)
|
|
if page >= total_pages:
|
|
break
|
|
|
|
print(f"[+] Retrieved {len(vulns)} vulnerabilities")
|
|
return vulns
|
|
|
|
|
|
def launch_scan(base_url, user, password, site_id, scan_name=None):
|
|
"""Launch a scan on a site."""
|
|
if not scan_name:
|
|
scan_name = f"agent-scan-{datetime.now(timezone.utc).strftime('%Y%m%d-%H%M%S')}"
|
|
print(f"[*] Launching scan '{scan_name}' on site {site_id}...")
|
|
data = api_call(base_url, f"/sites/{site_id}/scans", user, password,
|
|
method="POST", data={"name": scan_name})
|
|
scan_id = data.get("id")
|
|
print(f"[+] Scan started, ID: {scan_id}")
|
|
return scan_id
|
|
|
|
|
|
def poll_scan_status(base_url, user, password, scan_id, max_wait=1800):
|
|
"""Poll scan status until completion."""
|
|
print(f"[*] Waiting for scan {scan_id} to complete...")
|
|
elapsed = 0
|
|
interval = 30
|
|
while elapsed < max_wait:
|
|
data = api_call(base_url, f"/scans/{scan_id}", user, password)
|
|
status = data.get("status", "unknown")
|
|
if status in ("finished", "stopped", "error"):
|
|
print(f"[+] Scan {status}")
|
|
return status, data
|
|
print(f" Status: {status} ({elapsed}s)")
|
|
time.sleep(interval)
|
|
elapsed += interval
|
|
print("[!] Scan timed out")
|
|
return "timeout", {}
|
|
|
|
|
|
def get_scan_report(base_url, user, password, scan_id):
|
|
"""Get scan results summary."""
|
|
data = api_call(base_url, f"/scans/{scan_id}", user, password)
|
|
return {
|
|
"scan_id": scan_id,
|
|
"status": data.get("status", ""),
|
|
"start_time": data.get("startTime", ""),
|
|
"end_time": data.get("endTime", ""),
|
|
"duration": data.get("duration", ""),
|
|
"assets_discovered": data.get("assets", 0),
|
|
"vulnerabilities": data.get("vulnerabilities", {}),
|
|
}
|
|
|
|
|
|
def format_summary(sites, vulns=None, scan_report=None):
|
|
"""Print summary."""
|
|
print(f"\n{'='*60}")
|
|
print(f" Rapid7 InsightVM Report")
|
|
print(f"{'='*60}")
|
|
|
|
if sites:
|
|
print(f"\n Sites ({len(sites)}):")
|
|
for s in sites:
|
|
print(f" {s['name']:30s} | Assets: {s['assets']:5d} | "
|
|
f"Risk: {s['risk_score']:8.1f}")
|
|
|
|
if vulns:
|
|
severity_counts = {}
|
|
for v in vulns:
|
|
sev = v.get("severity", "unknown")
|
|
severity_counts[sev] = severity_counts.get(sev, 0) + 1
|
|
print(f"\n Vulnerabilities ({len(vulns)}):")
|
|
for sev in sorted(severity_counts.keys()):
|
|
print(f" {sev:15s}: {severity_counts[sev]}")
|
|
|
|
if scan_report:
|
|
print(f"\n Scan Report:")
|
|
print(f" Status : {scan_report.get('status', 'N/A')}")
|
|
print(f" Assets : {scan_report.get('assets_discovered', 0)}")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Rapid7 InsightVM scanning agent")
|
|
sub = parser.add_subparsers(dest="command")
|
|
|
|
sub.add_parser("list-sites", help="List scan sites")
|
|
|
|
p_vulns = sub.add_parser("get-vulns", help="Get site vulnerabilities")
|
|
p_vulns.add_argument("--site-id", required=True, type=int)
|
|
|
|
p_scan = sub.add_parser("scan", help="Launch a scan")
|
|
p_scan.add_argument("--site-id", required=True, type=int)
|
|
p_scan.add_argument("--wait", action="store_true", help="Wait for completion")
|
|
p_scan.add_argument("--max-wait", type=int, default=1800)
|
|
|
|
parser.add_argument("--host", help="InsightVM host (or INSIGHTVM_HOST env)")
|
|
parser.add_argument("--user", help="Username (or INSIGHTVM_USER env)")
|
|
parser.add_argument("--password", help="Password (or INSIGHTVM_PASSWORD env)")
|
|
parser.add_argument("--output", "-o", help="Output JSON report")
|
|
parser.add_argument("--verbose", "-v", action="store_true")
|
|
args = parser.parse_args()
|
|
|
|
if not args.command:
|
|
parser.print_help()
|
|
sys.exit(1)
|
|
|
|
if args.host:
|
|
os.environ["INSIGHTVM_HOST"] = args.host
|
|
if args.user:
|
|
os.environ["INSIGHTVM_USER"] = args.user
|
|
if args.password:
|
|
os.environ["INSIGHTVM_PASSWORD"] = args.password
|
|
|
|
base_url, user, password = get_insightvm_config()
|
|
if not user or not password:
|
|
print("[!] Set INSIGHTVM_USER and INSIGHTVM_PASSWORD", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
result = {}
|
|
if args.command == "list-sites":
|
|
sites = list_sites(base_url, user, password)
|
|
format_summary(sites)
|
|
result = {"sites": sites}
|
|
elif args.command == "get-vulns":
|
|
vulns = get_site_vulnerabilities(base_url, user, password, args.site_id)
|
|
format_summary([], vulns)
|
|
result = {"site_id": args.site_id, "vulnerabilities": vulns}
|
|
elif args.command == "scan":
|
|
scan_id = launch_scan(base_url, user, password, args.site_id)
|
|
if args.wait:
|
|
status, data = poll_scan_status(base_url, user, password, scan_id, args.max_wait)
|
|
scan_report = get_scan_report(base_url, user, password, scan_id)
|
|
format_summary([], scan_report=scan_report)
|
|
result = {"scan_id": scan_id, "report": scan_report}
|
|
else:
|
|
result = {"scan_id": scan_id, "status": "launched"}
|
|
|
|
report = {
|
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
"tool": "Rapid7 InsightVM",
|
|
"command": args.command,
|
|
"result": result,
|
|
}
|
|
|
|
if args.output:
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
print(f"\n[+] Report saved to {args.output}")
|
|
elif args.verbose:
|
|
print(json.dumps(report, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|