"""TS-SEC-001: Hardcoded Secrets detection for TypeScript/JavaScript.

Detects API keys, tokens, passwords, and other secrets hardcoded
in TypeScript/JavaScript MCP server source code.
"""

from __future__ import annotations

import re

from . import TSRule
from ..models import Finding, Severity

# Known API key prefixes
API_KEY_PREFIXES = re.compile(
    r'''['"](?:sk-[a-zA-Z0-9]{20,}|'''
    r'''ghp_[a-zA-Z0-9]{36}|'''
    r'''gho_[a-zA-Z0-9]{36}|'''
    r'''github_pat_[a-zA-Z0-9_]{20,}|'''
    r'''xoxb-[a-zA-Z0-9\-]{20,}|'''
    r'''xoxp-[a-zA-Z0-9\-]{20,}|'''
    r'''AKIA[A-Z0-9]{16}|'''
    r'''AIza[a-zA-Z0-9\-_]{35}|'''
    r'''glpat-[a-zA-Z0-9\-]{20,}|'''
    r'''npm_[a-zA-Z0-9]{36})['"]'''
)

# Generic assignment patterns
SECRET_ASSIGN = re.compile(
    r'''(?i)(?:api[_-]?key|secret|password|passwd|token|auth|'''
    r'''credential|private[_-]?key)\s*[:=]\s*['"][^'"]{8,}['"]'''
)

# Placeholder/example values to ignore
SAFE_VALUES = re.compile(
    r'''(?i)['"](?:your[_-]|example|test|placeholder|dummy|'''
    r'''changeme|xxx|TODO|REPLACE|<[^>]+>|process\.env|'''
    r'''env\.|config\.|settings\.)'''
)

# Environment variable access patterns (safe)
ENV_ACCESS = re.compile(
    r'process\.env\[|process\.env\.'
)


class TSHardcodedSecretsRule(TSRule):
    """Detects hardcoded secrets in TypeScript/JavaScript."""

    @property
    def rule_id(self) -> str:
        return "TS-SEC-001"

    @property
    def name(self) -> str:
        return "Hardcoded Secrets"

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

    @property
    def cwe(self) -> str:
        return "CWE-798"

    @property
    def description(self) -> str:
        return "Hardcoded API keys, tokens, or passwords in source code."

    @property
    def detailed_description(self) -> str:
        return (
            "Hardcoded API keys, tokens, or passwords in source code.\n\n"
            "Secrets committed to source control can be exposed through\n"
            "repository access, logs, or error messages.\n\n"
            "Examples of vulnerable code:\n"
            "  const apiKey = 'sk-abc123...';\n"
            "  const token = 'ghp_ABCdef...';\n\n"
            "How to fix:\n"
            "  - Use environment variables: process.env.API_KEY\n"
            "  - Use a secrets manager (AWS Secrets Manager, Vault)\n"
            "  - Use .env files (never commit to git)"
        )

    def check(self, source: str) -> list[Finding]:
        """Check for hardcoded secrets."""
        findings: list[Finding] = []
        lines = self._get_lines(source)

        for i, line in enumerate(lines, 1):
            if self._is_comment_line(line):
                continue

            # Skip env access lines
            if ENV_ACCESS.search(line):
                continue

            # Known API key prefixes
            if API_KEY_PREFIXES.search(line):
                if not SAFE_VALUES.search(line):
                    findings.append(self._make_finding(
                        i, line, "Known API key/token pattern detected."
                    ))
                continue

            # Generic secret assignment
            if SECRET_ASSIGN.search(line):
                if not SAFE_VALUES.search(line):
                    findings.append(Finding(
                        rule_id=self.rule_id,
                        severity=Severity.HIGH,
                        title="Potential Hardcoded Secret",
                        description=(
                            "Variable with secret-like name assigned "
                            "a string literal."
                        ),
                        file="",
                        line=i,
                        code_snippet=line.strip()[:80],
                        recommendation=(
                            "Use process.env or a secrets manager."
                        ),
                        cwe=self.cwe,
                        confidence="MEDIUM",
                    ))

        return findings

    def _make_finding(
        self, lineno: int, line: str, desc: str
    ) -> Finding:
        """Create a hardcoded secret finding."""
        return Finding(
            rule_id=self.rule_id,
            severity=self.severity,
            title="Hardcoded Secret",
            description=desc,
            file="",
            line=lineno,
            code_snippet=line.strip()[:80],
            recommendation="Use environment variables or a secrets manager.",
            cwe=self.cwe,
        )
