mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-16 04:35:18 +03:00
224 lines
8.1 KiB
Python
224 lines
8.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
ScoutSuite AWS Security Assessment Automation Script
|
|
|
|
Automates ScoutSuite scanning, parses results, and generates
|
|
summary reports for AWS security posture assessment.
|
|
"""
|
|
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
import os
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from collections import defaultdict
|
|
|
|
|
|
def run_scoutsuite_scan(
|
|
services=None,
|
|
regions=None,
|
|
profile=None,
|
|
report_dir=None
|
|
):
|
|
"""Execute ScoutSuite scan against AWS account."""
|
|
cmd = ["scout", "aws", "--no-browser"]
|
|
|
|
if services:
|
|
cmd.extend(["--services"] + services)
|
|
if regions:
|
|
cmd.extend(["--regions"] + regions)
|
|
if profile:
|
|
cmd.extend(["--profile", profile])
|
|
if report_dir:
|
|
cmd.extend(["--report-dir", report_dir])
|
|
|
|
print(f"[*] Running ScoutSuite scan: {' '.join(cmd)}")
|
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
|
|
if result.returncode != 0:
|
|
print(f"[!] ScoutSuite scan failed: {result.stderr}")
|
|
return False
|
|
|
|
print("[+] ScoutSuite scan completed successfully")
|
|
return True
|
|
|
|
|
|
def parse_scoutsuite_results(report_dir):
|
|
"""Parse ScoutSuite JSON results and extract findings."""
|
|
results_file = Path(report_dir) / "scoutsuite-results" / "scoutsuite_results.json"
|
|
|
|
if not results_file.exists():
|
|
# Try alternative path structure
|
|
for path in Path(report_dir).rglob("scoutsuite_results*.json"):
|
|
results_file = path
|
|
break
|
|
|
|
if not results_file.exists():
|
|
print(f"[!] Results file not found in {report_dir}")
|
|
return None
|
|
|
|
with open(results_file, "r") as f:
|
|
return json.load(f)
|
|
|
|
|
|
def extract_findings(results):
|
|
"""Extract and categorize findings from ScoutSuite results."""
|
|
findings_summary = {
|
|
"danger": [],
|
|
"warning": [],
|
|
"good": []
|
|
}
|
|
service_summary = defaultdict(lambda: {"danger": 0, "warning": 0, "good": 0})
|
|
|
|
services = results.get("services", {})
|
|
for service_name, service_data in services.items():
|
|
findings = service_data.get("findings", {})
|
|
for finding_id, finding in findings.items():
|
|
level = finding.get("level", "warning")
|
|
flagged = finding.get("flagged_items", 0)
|
|
total = finding.get("checked_items", 0)
|
|
description = finding.get("description", "No description")
|
|
rationale = finding.get("rationale", "")
|
|
|
|
entry = {
|
|
"id": finding_id,
|
|
"service": service_name,
|
|
"description": description,
|
|
"rationale": rationale,
|
|
"flagged_items": flagged,
|
|
"checked_items": total,
|
|
"level": level
|
|
}
|
|
|
|
if flagged > 0:
|
|
if level == "danger":
|
|
findings_summary["danger"].append(entry)
|
|
service_summary[service_name]["danger"] += flagged
|
|
elif level == "warning":
|
|
findings_summary["warning"].append(entry)
|
|
service_summary[service_name]["warning"] += flagged
|
|
else:
|
|
findings_summary["good"].append(entry)
|
|
service_summary[service_name]["good"] += 1
|
|
|
|
return findings_summary, dict(service_summary)
|
|
|
|
|
|
def generate_report(findings_summary, service_summary, output_file=None):
|
|
"""Generate a text-based summary report."""
|
|
report_lines = []
|
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
report_lines.append("=" * 70)
|
|
report_lines.append("ScoutSuite AWS Security Assessment Report")
|
|
report_lines.append(f"Generated: {timestamp}")
|
|
report_lines.append("=" * 70)
|
|
|
|
# Executive summary
|
|
danger_count = len(findings_summary["danger"])
|
|
warning_count = len(findings_summary["warning"])
|
|
good_count = len(findings_summary["good"])
|
|
|
|
report_lines.append(f"\n## Executive Summary")
|
|
report_lines.append(f" Critical Findings : {danger_count}")
|
|
report_lines.append(f" Warning Findings : {warning_count}")
|
|
report_lines.append(f" Passing Checks : {good_count}")
|
|
|
|
# Service breakdown
|
|
report_lines.append(f"\n## Service Breakdown")
|
|
report_lines.append(f"{'Service':<20} {'Danger':<10} {'Warning':<10} {'Good':<10}")
|
|
report_lines.append("-" * 50)
|
|
for service, counts in sorted(service_summary.items()):
|
|
report_lines.append(
|
|
f"{service:<20} {counts['danger']:<10} {counts['warning']:<10} {counts['good']:<10}"
|
|
)
|
|
|
|
# Critical findings detail
|
|
if findings_summary["danger"]:
|
|
report_lines.append(f"\n## Critical Findings (Requires Immediate Action)")
|
|
report_lines.append("-" * 50)
|
|
for finding in sorted(findings_summary["danger"], key=lambda x: x["flagged_items"], reverse=True):
|
|
report_lines.append(f"\n [{finding['service'].upper()}] {finding['id']}")
|
|
report_lines.append(f" Description: {finding['description']}")
|
|
report_lines.append(f" Flagged Items: {finding['flagged_items']}/{finding['checked_items']}")
|
|
if finding["rationale"]:
|
|
report_lines.append(f" Rationale: {finding['rationale']}")
|
|
|
|
# Warning findings
|
|
if findings_summary["warning"]:
|
|
report_lines.append(f"\n## Warning Findings")
|
|
report_lines.append("-" * 50)
|
|
for finding in sorted(findings_summary["warning"], key=lambda x: x["flagged_items"], reverse=True)[:20]:
|
|
report_lines.append(f"\n [{finding['service'].upper()}] {finding['id']}")
|
|
report_lines.append(f" Description: {finding['description']}")
|
|
report_lines.append(f" Flagged Items: {finding['flagged_items']}/{finding['checked_items']}")
|
|
|
|
report = "\n".join(report_lines)
|
|
|
|
if output_file:
|
|
with open(output_file, "w") as f:
|
|
f.write(report)
|
|
print(f"[+] Report saved to {output_file}")
|
|
else:
|
|
print(report)
|
|
|
|
return report
|
|
|
|
|
|
def check_compliance_gate(findings_summary, max_danger=0, max_warning=10):
|
|
"""Check if findings meet compliance gate thresholds for CI/CD."""
|
|
danger_count = len(findings_summary["danger"])
|
|
warning_count = len(findings_summary["warning"])
|
|
|
|
passed = True
|
|
|
|
if danger_count > max_danger:
|
|
print(f"[FAIL] {danger_count} critical findings exceed threshold of {max_danger}")
|
|
passed = False
|
|
else:
|
|
print(f"[PASS] Critical findings ({danger_count}) within threshold ({max_danger})")
|
|
|
|
if warning_count > max_warning:
|
|
print(f"[WARN] {warning_count} warning findings exceed threshold of {max_warning}")
|
|
|
|
return passed
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description="ScoutSuite AWS Security Assessment")
|
|
parser.add_argument("--scan", action="store_true", help="Run ScoutSuite scan")
|
|
parser.add_argument("--parse", type=str, help="Parse existing ScoutSuite results directory")
|
|
parser.add_argument("--services", nargs="+", default=None, help="AWS services to scan")
|
|
parser.add_argument("--regions", nargs="+", default=None, help="AWS regions to scan")
|
|
parser.add_argument("--profile", type=str, default=None, help="AWS profile name")
|
|
parser.add_argument("--report-dir", type=str, default="./scoutsuite-report", help="Report output directory")
|
|
parser.add_argument("--output", type=str, default=None, help="Output file for summary report")
|
|
parser.add_argument("--gate", action="store_true", help="Run compliance gate check")
|
|
parser.add_argument("--max-danger", type=int, default=0, help="Max allowed danger findings")
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.scan:
|
|
success = run_scoutsuite_scan(
|
|
services=args.services,
|
|
regions=args.regions,
|
|
profile=args.profile,
|
|
report_dir=args.report_dir
|
|
)
|
|
if not success:
|
|
sys.exit(1)
|
|
|
|
report_dir = args.parse or args.report_dir
|
|
results = parse_scoutsuite_results(report_dir)
|
|
|
|
if results:
|
|
findings_summary, service_summary = extract_findings(results)
|
|
generate_report(findings_summary, service_summary, args.output)
|
|
|
|
if args.gate:
|
|
passed = check_compliance_gate(findings_summary, max_danger=args.max_danger)
|
|
sys.exit(0 if passed else 1)
|