#!/usr/bin/env python3
"""RASP Agent - audits runtime application protection config, attack logs, and coverage."""

import json
import argparse
import logging
from collections import defaultdict
from datetime import datetime

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)

OWASP_TOP10_CHECKS = {
    "sqli": {"name": "SQL Injection", "owasp": "A03:2021", "hook": "sql_query"},
    "cmdi": {"name": "Command Injection", "owasp": "A03:2021", "hook": "command_exec"},
    "ssrf": {"name": "Server-Side Request Forgery", "owasp": "A10:2021", "hook": "http_request"},
    "path_traversal": {"name": "Path Traversal", "owasp": "A01:2021", "hook": "file_open"},
    "xxe": {"name": "XML External Entity", "owasp": "A05:2021", "hook": "xml_parse"},
    "deserialization": {"name": "Insecure Deserialization", "owasp": "A08:2021", "hook": "deserialize"},
    "xss": {"name": "Cross-Site Scripting", "owasp": "A03:2021", "hook": "response_write"},
    "ldap_injection": {"name": "LDAP Injection", "owasp": "A03:2021", "hook": "ldap_query"},
    "rce": {"name": "Remote Code Execution", "owasp": "A03:2021", "hook": "code_eval"},
}


def parse_rasp_config(config_file):
    """Parse OpenRASP or RASP agent configuration."""
    with open(config_file) as f:
        return json.load(f)


def audit_rasp_policy(config):
    """Audit RASP detection policies against OWASP Top 10 coverage."""
    findings = []
    enabled_checks = config.get("detection_plugins", {})
    for check_id, check_info in OWASP_TOP10_CHECKS.items():
        plugin = enabled_checks.get(check_id, {})
        if not plugin.get("enabled", False):
            findings.append({
                "check": check_id, "name": check_info["name"],
                "owasp": check_info["owasp"], "status": "disabled",
                "severity": "high",
                "recommendation": f"Enable {check_info['name']} detection plugin",
            })
        elif plugin.get("action", "log") != "block":
            findings.append({
                "check": check_id, "name": check_info["name"],
                "owasp": check_info["owasp"], "status": "monitor_only",
                "severity": "medium",
                "recommendation": f"Switch {check_info['name']} from monitor to block mode",
            })
    block_mode = config.get("global_action", "log")
    if block_mode == "log":
        findings.append({
            "check": "global_mode", "status": "monitor_only",
            "severity": "high",
            "recommendation": "Switch RASP from monitor to block mode after baseline period",
        })
    return findings


def analyze_attack_logs(log_file, limit=10000):
    """Analyze RASP attack detection logs."""
    attacks = []
    by_type = defaultdict(int)
    by_source = defaultdict(int)
    blocked = 0
    try:
        with open(log_file) as f:
            for i, line in enumerate(f):
                if i >= limit:
                    break
                try:
                    event = json.loads(line)
                    attack_type = event.get("attack_type", "unknown")
                    by_type[attack_type] += 1
                    by_source[event.get("client_ip", "")] += 1
                    if event.get("action") == "block":
                        blocked += 1
                    attacks.append(event)
                except json.JSONDecodeError:
                    continue
    except FileNotFoundError:
        logger.warning("RASP log file not found: %s", log_file)
    return {
        "total_attacks": len(attacks),
        "blocked": blocked,
        "block_rate": round(blocked / max(len(attacks), 1) * 100, 1),
        "by_attack_type": dict(sorted(by_type.items(), key=lambda x: x[1], reverse=True)),
        "top_sources": dict(sorted(by_source.items(), key=lambda x: x[1], reverse=True)[:10]),
    }


def check_agent_health(config):
    """Verify RASP agent deployment health."""
    findings = []
    if not config.get("agent_version"):
        findings.append({"issue": "Agent version unknown", "severity": "medium"})
    if config.get("cpu_limit_percent", 100) > 10:
        findings.append({
            "issue": f"CPU limit set to {config.get('cpu_limit_percent')}% (recommend < 5%)",
            "severity": "low",
        })
    if not config.get("siem_integration", {}).get("enabled"):
        findings.append({
            "issue": "SIEM log forwarding not configured",
            "severity": "high",
            "recommendation": "Enable syslog or webhook forwarding to SIEM",
        })
    return findings


def calculate_coverage(config):
    """Calculate OWASP Top 10 coverage percentage."""
    enabled_checks = config.get("detection_plugins", {})
    covered = sum(1 for check_id in OWASP_TOP10_CHECKS if enabled_checks.get(check_id, {}).get("enabled"))
    return {
        "total_checks": len(OWASP_TOP10_CHECKS),
        "enabled": covered,
        "coverage_percent": round(covered / len(OWASP_TOP10_CHECKS) * 100, 1),
    }


def generate_report(policy_findings, attack_analysis, health_findings, coverage, config):
    all_findings = policy_findings + health_findings
    return {
        "timestamp": datetime.utcnow().isoformat(),
        "rasp_agent": config.get("agent_type", "openrasp"),
        "agent_version": config.get("agent_version", "unknown"),
        "global_mode": config.get("global_action", "log"),
        "owasp_coverage": coverage,
        "policy_findings": policy_findings,
        "attack_analysis": attack_analysis,
        "health_findings": health_findings,
        "total_findings": len(all_findings),
        "critical_findings": sum(1 for f in all_findings if f.get("severity") == "critical"),
    }


def main():
    parser = argparse.ArgumentParser(description="RASP Security Audit Agent")
    parser.add_argument("--config", required=True, help="RASP agent config JSON file")
    parser.add_argument("--attack-log", help="RASP attack log file (JSON lines)")
    parser.add_argument("--output", default="rasp_audit_report.json")
    args = parser.parse_args()

    config = parse_rasp_config(args.config)
    policy_findings = audit_rasp_policy(config)
    attack_analysis = analyze_attack_logs(args.attack_log) if args.attack_log else {}
    health_findings = check_agent_health(config)
    coverage = calculate_coverage(config)
    report = generate_report(policy_findings, attack_analysis, health_findings, coverage, config)
    with open(args.output, "w") as f:
        json.dump(report, f, indent=2, default=str)
    logger.info("RASP audit: %.1f%% OWASP coverage, %d findings, mode: %s",
                coverage["coverage_percent"], report["total_findings"], config.get("global_action", "log"))
    print(json.dumps(report, indent=2, default=str))


if __name__ == "__main__":
    main()
