mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-14 03:15:16 +03:00
181 lines
6.0 KiB
Python
181 lines
6.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
kube-bench CIS Benchmark Reporter - Parse kube-bench JSON output
|
|
and generate compliance reports with trend tracking.
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
import argparse
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
from collections import Counter
|
|
|
|
|
|
def parse_kube_bench_json(filepath: str) -> dict:
|
|
"""Parse kube-bench JSON output file."""
|
|
path = Path(filepath)
|
|
if not path.exists():
|
|
print(f"File not found: {filepath}", file=sys.stderr)
|
|
sys.exit(1)
|
|
return json.loads(path.read_text())
|
|
|
|
|
|
def extract_checks(data: dict) -> list:
|
|
"""Extract all individual checks from kube-bench results."""
|
|
checks = []
|
|
for control in data.get("Controls", []):
|
|
section = control.get("id", "")
|
|
section_text = control.get("text", "")
|
|
for group in control.get("tests", []):
|
|
group_id = group.get("section", "")
|
|
for result in group.get("results", []):
|
|
checks.append({
|
|
"section": section,
|
|
"section_text": section_text,
|
|
"group": group_id,
|
|
"id": result.get("test_number", ""),
|
|
"description": result.get("test_desc", ""),
|
|
"status": result.get("status", ""),
|
|
"scored": result.get("scored", False),
|
|
"remediation": result.get("remediation", ""),
|
|
"reason": result.get("reason", ""),
|
|
})
|
|
return checks
|
|
|
|
|
|
def generate_summary(checks: list) -> dict:
|
|
"""Generate summary statistics."""
|
|
status_counts = Counter(c["status"] for c in checks)
|
|
section_counts = {}
|
|
for c in checks:
|
|
sec = c["section"]
|
|
if sec not in section_counts:
|
|
section_counts[sec] = {"section_text": c["section_text"], "PASS": 0, "FAIL": 0, "WARN": 0, "INFO": 0}
|
|
status = c["status"]
|
|
if status in section_counts[sec]:
|
|
section_counts[sec][status] += 1
|
|
|
|
total = len(checks)
|
|
passed = status_counts.get("PASS", 0)
|
|
score = (passed / total * 100) if total > 0 else 0
|
|
|
|
return {
|
|
"total_checks": total,
|
|
"pass": status_counts.get("PASS", 0),
|
|
"fail": status_counts.get("FAIL", 0),
|
|
"warn": status_counts.get("WARN", 0),
|
|
"info": status_counts.get("INFO", 0),
|
|
"score_percent": round(score, 1),
|
|
"by_section": section_counts,
|
|
}
|
|
|
|
|
|
def generate_report(data: dict, output_path: str = None) -> str:
|
|
"""Generate markdown CIS benchmark report."""
|
|
checks = extract_checks(data)
|
|
summary = generate_summary(checks)
|
|
timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
|
|
|
|
report = f"""# CIS Kubernetes Benchmark Report
|
|
|
|
**Date:** {timestamp}
|
|
**Total Checks:** {summary['total_checks']}
|
|
**Score:** {summary['score_percent']}%
|
|
|
|
## Summary
|
|
|
|
| Status | Count |
|
|
|--------|-------|
|
|
| PASS | {summary['pass']} |
|
|
| FAIL | {summary['fail']} |
|
|
| WARN | {summary['warn']} |
|
|
| INFO | {summary['info']} |
|
|
|
|
## Results by Section
|
|
|
|
| Section | Description | Pass | Fail | Warn |
|
|
|---------|-------------|------|------|------|
|
|
"""
|
|
for sec_id, sec in sorted(summary["by_section"].items()):
|
|
report += f"| {sec_id} | {sec['section_text']} | {sec['PASS']} | {sec['FAIL']} | {sec['WARN']} |\n"
|
|
|
|
failed = [c for c in checks if c["status"] == "FAIL"]
|
|
if failed:
|
|
report += "\n## Failed Checks (Requires Remediation)\n\n"
|
|
for c in failed:
|
|
report += f"### {c['id']} - {c['description']}\n"
|
|
report += f"- **Status:** FAIL\n"
|
|
report += f"- **Scored:** {c['scored']}\n"
|
|
if c['remediation']:
|
|
report += f"- **Remediation:** {c['remediation']}\n"
|
|
report += "\n"
|
|
|
|
if output_path:
|
|
Path(output_path).write_text(report)
|
|
print(f"Report written to {output_path}")
|
|
|
|
return report
|
|
|
|
|
|
def compare_scans(current_file: str, previous_file: str):
|
|
"""Compare two kube-bench scan results."""
|
|
current = extract_checks(parse_kube_bench_json(current_file))
|
|
previous = extract_checks(parse_kube_bench_json(previous_file))
|
|
|
|
curr_summary = generate_summary(current)
|
|
prev_summary = generate_summary(previous)
|
|
|
|
print("\n=== CIS Benchmark Comparison ===\n")
|
|
print(f"{'Metric':<20} {'Previous':<12} {'Current':<12} {'Change'}")
|
|
print("-" * 55)
|
|
|
|
for metric in ["pass", "fail", "warn"]:
|
|
prev_val = prev_summary[metric]
|
|
curr_val = curr_summary[metric]
|
|
change = curr_val - prev_val
|
|
sign = "+" if change > 0 else ""
|
|
print(f"{metric.upper():<20} {prev_val:<12} {curr_val:<12} {sign}{change}")
|
|
|
|
print(f"{'SCORE':<20} {prev_summary['score_percent']}%{'':<7} {curr_summary['score_percent']}%")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="kube-bench CIS Benchmark Reporter")
|
|
subparsers = parser.add_subparsers(dest="command")
|
|
|
|
report_cmd = subparsers.add_parser("report", help="Generate report from kube-bench JSON")
|
|
report_cmd.add_argument("input", help="kube-bench JSON output file")
|
|
report_cmd.add_argument("--output", "-o", help="Output markdown report path")
|
|
|
|
compare_cmd = subparsers.add_parser("compare", help="Compare two scan results")
|
|
compare_cmd.add_argument("current", help="Current scan JSON")
|
|
compare_cmd.add_argument("previous", help="Previous scan JSON")
|
|
|
|
summary_cmd = subparsers.add_parser("summary", help="Print summary from JSON")
|
|
summary_cmd.add_argument("input", help="kube-bench JSON output file")
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.command == "report":
|
|
data = parse_kube_bench_json(args.input)
|
|
report = generate_report(data, args.output)
|
|
if not args.output:
|
|
print(report)
|
|
|
|
elif args.command == "compare":
|
|
compare_scans(args.current, args.previous)
|
|
|
|
elif args.command == "summary":
|
|
data = parse_kube_bench_json(args.input)
|
|
checks = extract_checks(data)
|
|
summary = generate_summary(checks)
|
|
print(json.dumps(summary, indent=2))
|
|
|
|
else:
|
|
parser.print_help()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|