mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 06:04:56 +03:00
c47eed6a64
- 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
328 lines
12 KiB
Python
328 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Rapid7 InsightVM Scan Automation and Reporting Tool
|
|
|
|
Automates scan operations, asset queries, and vulnerability reporting
|
|
via the InsightVM API v3.
|
|
|
|
Requirements:
|
|
pip install requests pandas tabulate
|
|
|
|
Usage:
|
|
python process.py sites # List all sites
|
|
python process.py scan --site-id 1 # Start scan for site
|
|
python process.py status --scan-id 12345 # Check scan status
|
|
python process.py vulns --asset-id 42 # Get asset vulnerabilities
|
|
python process.py report --site-id 1 --output report.csv # Export report
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
import urllib3
|
|
from datetime import datetime
|
|
|
|
import pandas as pd
|
|
import requests
|
|
|
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
|
|
|
|
class InsightVMAPI:
|
|
"""Rapid7 InsightVM API v3 client."""
|
|
|
|
def __init__(self, console_url, username=None, password=None, api_key=None):
|
|
self.base_url = f"{console_url.rstrip('/')}/api/3"
|
|
self.session = requests.Session()
|
|
self.session.verify = not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true" # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
|
|
|
|
if api_key:
|
|
self.session.headers.update({
|
|
"Authorization": f"Bearer {api_key}",
|
|
"Content-Type": "application/json"
|
|
})
|
|
elif username and password:
|
|
self.session.auth = (username, password)
|
|
self.session.headers.update({"Content-Type": "application/json"})
|
|
else:
|
|
raise ValueError("Provide either api_key or username/password")
|
|
|
|
def _get_paginated(self, endpoint, params=None):
|
|
"""Fetch all pages from a paginated endpoint."""
|
|
all_resources = []
|
|
page = 0
|
|
while True:
|
|
p = params.copy() if params else {}
|
|
p["page"] = page
|
|
p["size"] = 100
|
|
response = self.session.get(
|
|
f"{self.base_url}/{endpoint}", params=p, timeout=60
|
|
)
|
|
response.raise_for_status()
|
|
data = response.json()
|
|
resources = data.get("resources", [])
|
|
all_resources.extend(resources)
|
|
total_pages = data.get("page", {}).get("totalPages", 1)
|
|
page += 1
|
|
if page >= total_pages:
|
|
break
|
|
return all_resources
|
|
|
|
def list_sites(self):
|
|
"""List all scan sites."""
|
|
return self._get_paginated("sites")
|
|
|
|
def get_site(self, site_id):
|
|
"""Get details for a specific site."""
|
|
response = self.session.get(
|
|
f"{self.base_url}/sites/{site_id}", timeout=30
|
|
)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|
|
def start_scan(self, site_id, engine_id=None, template_id=None, hosts=None):
|
|
"""Start a scan for a site."""
|
|
payload = {}
|
|
if engine_id:
|
|
payload["engineId"] = engine_id
|
|
if template_id:
|
|
payload["templateId"] = template_id
|
|
if hosts:
|
|
payload["hosts"] = hosts
|
|
|
|
response = self.session.post(
|
|
f"{self.base_url}/sites/{site_id}/scans",
|
|
json=payload, timeout=30
|
|
)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|
|
def get_scan_status(self, scan_id):
|
|
"""Get the status of a scan."""
|
|
response = self.session.get(
|
|
f"{self.base_url}/scans/{scan_id}", timeout=30
|
|
)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|
|
def wait_for_scan(self, scan_id, poll_interval=30, timeout=3600):
|
|
"""Wait for a scan to complete."""
|
|
start_time = time.time()
|
|
while time.time() - start_time < timeout:
|
|
status = self.get_scan_status(scan_id)
|
|
state = status.get("status", "unknown")
|
|
print(f" Scan {scan_id}: {state} "
|
|
f"({status.get('assets', 0)} assets, "
|
|
f"{status.get('vulnerabilities', {}).get('total', 0)} vulns)")
|
|
if state in ("finished", "stopped", "error", "aborted"):
|
|
return status
|
|
time.sleep(poll_interval)
|
|
print(f" [!] Scan timeout after {timeout}s")
|
|
return None
|
|
|
|
def get_site_assets(self, site_id):
|
|
"""Get all assets for a site."""
|
|
return self._get_paginated(f"sites/{site_id}/assets")
|
|
|
|
def get_asset_vulnerabilities(self, asset_id):
|
|
"""Get vulnerabilities for a specific asset."""
|
|
return self._get_paginated(f"assets/{asset_id}/vulnerabilities")
|
|
|
|
def get_vulnerability_details(self, vuln_id):
|
|
"""Get details for a specific vulnerability."""
|
|
response = self.session.get(
|
|
f"{self.base_url}/vulnerabilities/{vuln_id}", timeout=30
|
|
)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|
|
def list_scan_engines(self):
|
|
"""List all scan engines."""
|
|
return self._get_paginated("scan_engines")
|
|
|
|
def list_scan_templates(self):
|
|
"""List available scan templates."""
|
|
return self._get_paginated("scan_templates")
|
|
|
|
|
|
def cmd_list_sites(api):
|
|
"""List all configured sites."""
|
|
sites = api.list_sites()
|
|
if not sites:
|
|
print("No sites configured.")
|
|
return
|
|
|
|
print(f"\n{'ID':<6} {'Name':<35} {'Assets':<10} {'Last Scan':<20}")
|
|
print("-" * 75)
|
|
for site in sites:
|
|
last_scan = site.get("lastScanTime", "Never")
|
|
if last_scan != "Never":
|
|
last_scan = last_scan[:19]
|
|
print(f"{site['id']:<6} {site['name'][:34]:<35} "
|
|
f"{site.get('assets', 0):<10} {last_scan:<20}")
|
|
|
|
|
|
def cmd_start_scan(api, site_id, engine_id=None, template_id=None, wait=False):
|
|
"""Start a scan for a site."""
|
|
print(f"[*] Starting scan for site {site_id}...")
|
|
result = api.start_scan(site_id, engine_id, template_id)
|
|
scan_id = result.get("id")
|
|
print(f"[+] Scan started: ID={scan_id}")
|
|
|
|
if wait and scan_id:
|
|
print("[*] Waiting for scan to complete...")
|
|
final_status = api.wait_for_scan(scan_id)
|
|
if final_status:
|
|
print(f"\n[+] Scan completed: {final_status.get('status')}")
|
|
vulns = final_status.get("vulnerabilities", {})
|
|
print(f" Total vulnerabilities: {vulns.get('total', 0)}")
|
|
print(f" Critical: {vulns.get('critical', 0)}")
|
|
print(f" Severe: {vulns.get('severe', 0)}")
|
|
print(f" Moderate: {vulns.get('moderate', 0)}")
|
|
|
|
|
|
def cmd_scan_status(api, scan_id):
|
|
"""Check scan status."""
|
|
status = api.get_scan_status(scan_id)
|
|
print(f"\nScan ID: {status.get('id')}")
|
|
print(f"Status: {status.get('status')}")
|
|
print(f"Start Time: {status.get('startTime', 'N/A')}")
|
|
print(f"End Time: {status.get('endTime', 'N/A')}")
|
|
print(f"Assets: {status.get('assets', 0)}")
|
|
vulns = status.get("vulnerabilities", {})
|
|
print(f"Vulns Total: {vulns.get('total', 0)}")
|
|
print(f" Critical: {vulns.get('critical', 0)}")
|
|
print(f" Severe: {vulns.get('severe', 0)}")
|
|
print(f" Moderate: {vulns.get('moderate', 0)}")
|
|
|
|
|
|
def cmd_asset_vulns(api, asset_id):
|
|
"""List vulnerabilities for an asset."""
|
|
vulns = api.get_asset_vulnerabilities(asset_id)
|
|
if not vulns:
|
|
print(f"No vulnerabilities found for asset {asset_id}.")
|
|
return
|
|
|
|
print(f"\nVulnerabilities for asset {asset_id}: {len(vulns)} total\n")
|
|
print(f"{'Vuln ID':<40} {'Severity':<10} {'CVSS':<8} {'Status':<12}")
|
|
print("-" * 72)
|
|
for v in sorted(vulns, key=lambda x: x.get("severity", ""), reverse=True):
|
|
print(f"{v.get('id', 'N/A')[:39]:<40} "
|
|
f"{v.get('severity', 'N/A'):<10} "
|
|
f"{v.get('cvssV3Score', 'N/A'):<8} "
|
|
f"{v.get('status', 'N/A'):<12}")
|
|
|
|
|
|
def cmd_export_report(api, site_id, output_file):
|
|
"""Export vulnerability report for a site to CSV."""
|
|
print(f"[*] Fetching assets for site {site_id}...")
|
|
assets = api.get_site_assets(site_id)
|
|
print(f"[+] Found {len(assets)} assets")
|
|
|
|
all_findings = []
|
|
for asset in assets:
|
|
asset_id = asset.get("id")
|
|
hostname = asset.get("hostName", asset.get("ip", "unknown"))
|
|
ip = asset.get("ip", "N/A")
|
|
os_name = asset.get("os", {}).get("description", "Unknown")
|
|
|
|
vulns = api.get_asset_vulnerabilities(asset_id)
|
|
for v in vulns:
|
|
all_findings.append({
|
|
"asset_id": asset_id,
|
|
"hostname": hostname,
|
|
"ip_address": ip,
|
|
"os": os_name,
|
|
"vulnerability_id": v.get("id", ""),
|
|
"severity": v.get("severity", ""),
|
|
"cvss_v3_score": v.get("cvssV3Score", ""),
|
|
"status": v.get("status", ""),
|
|
"first_found": v.get("since", ""),
|
|
})
|
|
|
|
print(f" Processed {hostname}: {len(vulns)} vulnerabilities")
|
|
|
|
if all_findings:
|
|
df = pd.DataFrame(all_findings)
|
|
df = df.sort_values(["cvss_v3_score", "severity"], ascending=[False, False])
|
|
df.to_csv(output_file, index=False)
|
|
print(f"\n[+] Report exported to {output_file}")
|
|
print(f" Total findings: {len(all_findings)}")
|
|
print(f"\n Severity Distribution:")
|
|
print(df["severity"].value_counts().to_string())
|
|
else:
|
|
print("[!] No findings to export.")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Rapid7 InsightVM Scan Automation Tool"
|
|
)
|
|
parser.add_argument("--console", default="https://localhost:3780",
|
|
help="InsightVM console URL")
|
|
parser.add_argument("--username", help="Console username")
|
|
parser.add_argument("--password", help="Console password")
|
|
parser.add_argument("--api-key", help="API key (alternative to user/pass)")
|
|
|
|
subparsers = parser.add_subparsers(dest="command")
|
|
|
|
subparsers.add_parser("sites", help="List all scan sites")
|
|
|
|
scan_p = subparsers.add_parser("scan", help="Start a scan")
|
|
scan_p.add_argument("--site-id", type=int, required=True)
|
|
scan_p.add_argument("--engine-id", type=int)
|
|
scan_p.add_argument("--template-id", type=str)
|
|
scan_p.add_argument("--wait", action="store_true")
|
|
|
|
status_p = subparsers.add_parser("status", help="Check scan status")
|
|
status_p.add_argument("--scan-id", type=int, required=True)
|
|
|
|
vuln_p = subparsers.add_parser("vulns", help="List asset vulnerabilities")
|
|
vuln_p.add_argument("--asset-id", type=int, required=True)
|
|
|
|
report_p = subparsers.add_parser("report", help="Export vulnerability report")
|
|
report_p.add_argument("--site-id", type=int, required=True)
|
|
report_p.add_argument("--output", default="insightvm_report.csv")
|
|
|
|
subparsers.add_parser("engines", help="List scan engines")
|
|
subparsers.add_parser("templates", help="List scan templates")
|
|
|
|
args = parser.parse_args()
|
|
|
|
if not args.command:
|
|
parser.print_help()
|
|
sys.exit(1)
|
|
|
|
api = InsightVMAPI(
|
|
args.console,
|
|
username=args.username,
|
|
password=args.password,
|
|
api_key=args.api_key
|
|
)
|
|
|
|
if args.command == "sites":
|
|
cmd_list_sites(api)
|
|
elif args.command == "scan":
|
|
cmd_start_scan(api, args.site_id, args.engine_id,
|
|
args.template_id, args.wait)
|
|
elif args.command == "status":
|
|
cmd_scan_status(api, args.scan_id)
|
|
elif args.command == "vulns":
|
|
cmd_asset_vulns(api, args.asset_id)
|
|
elif args.command == "report":
|
|
cmd_export_report(api, args.site_id, args.output)
|
|
elif args.command == "engines":
|
|
engines = api.list_scan_engines()
|
|
for e in engines:
|
|
print(f" Engine {e['id']}: {e['name']} - {e.get('address', 'N/A')}")
|
|
elif args.command == "templates":
|
|
templates = api.list_scan_templates()
|
|
for t in templates:
|
|
print(f" {t['id']}: {t['name']}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|