mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-19 14:09:40 +03:00
chore: fix license, add disclaimer, quick start, GitHub topics, issue templates
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Trivy Container Security Agent - scans images for vulnerabilities, misconfigs, and secrets."""
|
||||
|
||||
import json
|
||||
import argparse
|
||||
import logging
|
||||
import subprocess
|
||||
import os
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def run_trivy_scan(image, scanners="vuln,secret", severity="CRITICAL,HIGH,MEDIUM"):
|
||||
"""Run Trivy image scan and return JSON results."""
|
||||
cmd = [
|
||||
"trivy", "image", "--format", "json", "--scanners", scanners,
|
||||
"--severity", severity, "--quiet", image,
|
||||
]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
||||
if result.returncode != 0 and not result.stdout:
|
||||
logger.error("Trivy scan failed: %s", result.stderr)
|
||||
return {}
|
||||
return json.loads(result.stdout) if result.stdout else {}
|
||||
|
||||
|
||||
def run_trivy_misconfig(target_path):
|
||||
"""Run Trivy misconfiguration scan on Dockerfile/K8s manifests."""
|
||||
cmd = [
|
||||
"trivy", "config", "--format", "json", "--severity", "CRITICAL,HIGH,MEDIUM",
|
||||
"--quiet", target_path,
|
||||
]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
||||
return json.loads(result.stdout) if result.stdout else {}
|
||||
|
||||
|
||||
def generate_sbom(image, sbom_format="cyclonedx"):
|
||||
"""Generate SBOM from container image."""
|
||||
cmd = ["trivy", "image", "--format", sbom_format, "--quiet", image]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
||||
return result.stdout if result.returncode == 0 else ""
|
||||
|
||||
|
||||
def analyze_vulnerabilities(scan_results):
|
||||
"""Analyze vulnerability scan results and produce summary."""
|
||||
by_severity = defaultdict(int)
|
||||
by_pkg_type = defaultdict(int)
|
||||
fixable = 0
|
||||
total = 0
|
||||
cve_list = []
|
||||
for target in scan_results.get("Results", []):
|
||||
pkg_type = target.get("Type", "unknown")
|
||||
for vuln in target.get("Vulnerabilities", []):
|
||||
severity = vuln.get("Severity", "UNKNOWN")
|
||||
by_severity[severity] += 1
|
||||
by_pkg_type[pkg_type] += 1
|
||||
total += 1
|
||||
if vuln.get("FixedVersion"):
|
||||
fixable += 1
|
||||
if severity in ("CRITICAL", "HIGH"):
|
||||
cve_list.append({
|
||||
"cve_id": vuln.get("VulnerabilityID", ""),
|
||||
"package": vuln.get("PkgName", ""),
|
||||
"installed": vuln.get("InstalledVersion", ""),
|
||||
"fixed": vuln.get("FixedVersion", ""),
|
||||
"severity": severity,
|
||||
"cvss_score": vuln.get("CVSS", {}).get("nvd", {}).get("V3Score", 0),
|
||||
"title": vuln.get("Title", "")[:100],
|
||||
})
|
||||
return {
|
||||
"total_vulnerabilities": total,
|
||||
"by_severity": dict(by_severity),
|
||||
"by_package_type": dict(by_pkg_type),
|
||||
"fixable_count": fixable,
|
||||
"fix_rate": round(fixable / max(total, 1) * 100, 1),
|
||||
"critical_high_cves": sorted(cve_list, key=lambda x: x.get("cvss_score", 0), reverse=True)[:20],
|
||||
}
|
||||
|
||||
|
||||
def analyze_secrets(scan_results):
|
||||
"""Analyze secret detection results."""
|
||||
secrets = []
|
||||
for target in scan_results.get("Results", []):
|
||||
for secret in target.get("Secrets", []):
|
||||
secrets.append({
|
||||
"rule_id": secret.get("RuleID", ""),
|
||||
"category": secret.get("Category", ""),
|
||||
"title": secret.get("Title", ""),
|
||||
"severity": secret.get("Severity", ""),
|
||||
"target_file": target.get("Target", ""),
|
||||
})
|
||||
return {"total_secrets": len(secrets), "secrets": secrets[:15]}
|
||||
|
||||
|
||||
def analyze_misconfigs(misconfig_results):
|
||||
"""Analyze misconfiguration scan results."""
|
||||
findings = []
|
||||
for target in misconfig_results.get("Results", []):
|
||||
for mc in target.get("Misconfigurations", []):
|
||||
findings.append({
|
||||
"avd_id": mc.get("AVDID", ""),
|
||||
"title": mc.get("Title", ""),
|
||||
"severity": mc.get("Severity", ""),
|
||||
"target": target.get("Target", ""),
|
||||
"resolution": mc.get("Resolution", "")[:150],
|
||||
})
|
||||
by_sev = defaultdict(int)
|
||||
for f in findings:
|
||||
by_sev[f["severity"]] += 1
|
||||
return {"total_misconfigs": len(findings), "by_severity": dict(by_sev), "findings": findings[:15]}
|
||||
|
||||
|
||||
def generate_report(image, vuln_analysis, secret_analysis, misconfig_analysis):
|
||||
critical = vuln_analysis["by_severity"].get("CRITICAL", 0)
|
||||
high = vuln_analysis["by_severity"].get("HIGH", 0)
|
||||
return {
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"image": image,
|
||||
"vulnerability_summary": vuln_analysis,
|
||||
"secret_summary": secret_analysis,
|
||||
"misconfiguration_summary": misconfig_analysis,
|
||||
"gate_result": "FAIL" if critical > 0 else "WARN" if high > 0 else "PASS",
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Trivy Container Security Scanning Agent")
|
||||
parser.add_argument("--image", required=True, help="Container image to scan (e.g., nginx:latest)")
|
||||
parser.add_argument("--config-path", help="Path to Dockerfile/K8s manifests for misconfig scan")
|
||||
parser.add_argument("--severity", default="CRITICAL,HIGH,MEDIUM", help="Severity filter")
|
||||
parser.add_argument("--sbom", action="store_true", help="Generate CycloneDX SBOM")
|
||||
parser.add_argument("--output", default="trivy_scan_report.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
scan_results = run_trivy_scan(args.image, severity=args.severity)
|
||||
vuln_analysis = analyze_vulnerabilities(scan_results)
|
||||
secret_analysis = analyze_secrets(scan_results)
|
||||
misconfig_analysis = {}
|
||||
if args.config_path:
|
||||
misconfig_results = run_trivy_misconfig(args.config_path)
|
||||
misconfig_analysis = analyze_misconfigs(misconfig_results)
|
||||
if args.sbom:
|
||||
sbom_data = generate_sbom(args.image)
|
||||
if sbom_data:
|
||||
with open(args.output.replace(".json", "_sbom.json"), "w") as f:
|
||||
f.write(sbom_data)
|
||||
report = generate_report(args.image, vuln_analysis, secret_analysis, misconfig_analysis)
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2, default=str)
|
||||
logger.info("Trivy: %s - %d vulns (%d critical), %d secrets, gate: %s",
|
||||
args.image, vuln_analysis["total_vulnerabilities"],
|
||||
vuln_analysis["by_severity"].get("CRITICAL", 0),
|
||||
secret_analysis["total_secrets"], report["gate_result"])
|
||||
print(json.dumps(report, indent=2, default=str))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user