mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-13 06:34:57 +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
160 lines
6.5 KiB
Python
160 lines
6.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Trivy Container Security Agent - scans images for vulnerabilities, misconfigs, and secrets."""
|
|
|
|
import json
|
|
import argparse
|
|
import logging
|
|
import subprocess
|
|
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()
|