"""AP010: Injection Vector Detection."""

from collections.abc import Iterator

from apiposture.core.models.endpoint import Endpoint
from apiposture.core.models.enums import Severity
from apiposture.core.models.finding import Finding

from apiposture_pro.rules.base import ProSecurityRule


class AP010InjectionVectors(ProSecurityRule):
    """
    AP010: Injection Vector Detection.

    Detects potential injection vulnerabilities including:
    - Path parameters that could enable path traversal
    - Query/file operation endpoints without validation markers
    - Database query endpoints without ORM protection markers
    """

    @property
    def rule_id(self) -> str:
        return "AP010"

    @property
    def name(self) -> str:
        return "Injection Vector Detection"

    @property
    def severity(self) -> Severity:
        return Severity.HIGH

    @property
    def description(self) -> str:
        return (
            "Detects potential injection vulnerabilities including SQL injection, "
            "command injection, and path traversal risks (OWASP A03:2021)"
        )

    def evaluate(self, endpoint: Endpoint) -> Iterator[Finding]:
        """Evaluate endpoint for injection vulnerabilities."""
        route = endpoint.route
        func_name = endpoint.function_name.lower()
        route_lower = route.lower()

        # Check for file path parameters (potential path traversal)
        file_keywords = ["file", "path", "filename", "filepath", "document", "attachment"]
        has_path_param = "{" in route or "<" in route

        if has_path_param and any(keyword in route_lower for keyword in file_keywords):
            yield self.create_finding(
                endpoint,
                message=(
                    f"Endpoint '{endpoint.full_route}' accepts file/path parameters "
                    "which may be vulnerable to path traversal"
                ),
                recommendation=(
                    "Validate and sanitize file paths. Use allowlists for allowed directories, "
                    "reject '..' and absolute paths, and use pathlib.resolve() to normalize paths."
                ),
                severity=Severity.HIGH,
            )

        # Check for query/search endpoints (potential SQL injection)
        query_keywords = ["query", "search", "filter", "sql", "where"]
        is_query_endpoint = any(keyword in func_name or keyword in route_lower for keyword in query_keywords)

        if is_query_endpoint:
            yield self.create_finding(
                endpoint,
                message=(
                    f"Query endpoint '{endpoint.full_route}' may be vulnerable to injection"
                ),
                recommendation=(
                    "Use parameterized queries or ORM methods. Never construct SQL from "
                    "user input using string concatenation or f-strings. "
                    "For Django: use ORM querysets. For SQLAlchemy: use bound parameters. "
                    "For raw SQL: use cursor.execute(sql, params)."
                ),
                severity=Severity.HIGH,
            )

        # Check for command execution patterns
        command_keywords = ["exec", "execute", "run", "cmd", "command", "shell", "process"]
        is_command_endpoint = any(keyword in func_name for keyword in command_keywords)

        if is_command_endpoint and endpoint.is_write_endpoint:
            yield self.create_finding(
                endpoint,
                message=(
                    f"Command execution endpoint '{endpoint.full_route}' "
                    "may be vulnerable to command injection"
                ),
                recommendation=(
                    "Avoid executing system commands from user input. If required, "
                    "use subprocess.run() with a list of arguments (not shell=True), "
                    "validate inputs against a strict allowlist, and run with minimal privileges."
                ),
                severity=Severity.CRITICAL,
            )

        # Check for template/code endpoints (potential template injection)
        template_keywords = ["template", "render", "eval", "compile"]
        is_template_endpoint = any(keyword in func_name for keyword in template_keywords)

        if is_template_endpoint:
            yield self.create_finding(
                endpoint,
                message=(
                    f"Template/code endpoint '{endpoint.full_route}' "
                    "may be vulnerable to template injection"
                ),
                recommendation=(
                    "Never use eval(), exec(), or compile() with user input. "
                    "Use sandboxed template engines and disable unsafe template features. "
                    "For Jinja2: use autoescaping and disable unsafe extensions."
                ),
                severity=Severity.CRITICAL,
            )

        # Check for redirect/URL parameters (open redirect)
        redirect_keywords = ["redirect", "return", "callback", "continue", "next"]
        has_redirect_pattern = any(keyword in func_name for keyword in redirect_keywords)

        if has_redirect_pattern and has_path_param:
            yield self.create_finding(
                endpoint,
                message=(
                    f"Redirect endpoint '{endpoint.full_route}' accepts URL parameters "
                    "which may enable open redirect attacks"
                ),
                recommendation=(
                    "Validate redirect URLs against an allowlist of trusted domains. "
                    "Never redirect to arbitrary user-provided URLs. "
                    "Use relative URLs or validate against a list of allowed destinations."
                ),
                severity=Severity.MEDIUM,
            )
