"""Security scanning pattern definitions.

High-confidence patterns for secret detection and code anti-pattern
recognition, each tagged with a CWE ID and severity level.

NOTE: The regex patterns in this file are DETECTORS — they match
dangerous constructs in scanned code to flag them for review.
The patterns themselves do not execute any dangerous operations.
"""

from __future__ import annotations

import re
from dataclasses import dataclass, field
from enum import StrEnum

from attocode_intel._internal.types.severity import Severity


class Category(StrEnum):
    SECRET = "secret"
    ANTI_PATTERN = "anti_pattern"
    DEPENDENCY = "dependency"


@dataclass(slots=True)
class SecurityPattern:
    """A single security scanning pattern."""

    name: str
    pattern: re.Pattern[str]
    severity: Severity
    category: Category
    cwe_id: str
    message: str
    recommendation: str
    languages: list[str] = field(default_factory=list)  # empty = all languages
    scan_comments: bool = False  # if True, scan comment lines too (for stego payloads)


# ---------------------------------------------------------------------------
# Secret detection patterns
# ---------------------------------------------------------------------------

_SECRET_DEFS: list[tuple[str, str, str, str, str, str]] = [
    # (name, regex, severity, cwe, message, recommendation)
    ("generic_api_key",
     r"""(?:api[_-]?key|apikey)\s*[:=]\s*['"]([a-zA-Z0-9_\-]{20,})['"]""",
     "high", "CWE-798",
     "Hardcoded API key detected",
     "Use environment variables or a secrets manager"),
    ("openai_key",
     r"""sk-(?:proj-)?[a-zA-Z0-9_-]{20,}""",
     "critical", "CWE-798",
     "OpenAI API key (sk-...) detected",
     "Move to OPENAI_API_KEY environment variable"),
    ("anthropic_key",
     r"""sk-ant-[a-zA-Z0-9\-]{20,}""",
     "critical", "CWE-798",
     "Anthropic API key (sk-ant-...) detected",
     "Move to ANTHROPIC_API_KEY environment variable"),
    ("github_token",
     r"""ghp_[a-zA-Z0-9]{36}""",
     "critical", "CWE-798",
     "GitHub personal access token detected",
     "Use GITHUB_TOKEN environment variable"),
    ("github_oauth",
     r"""gho_[a-zA-Z0-9]{36}""",
     "high", "CWE-798",
     "GitHub OAuth token detected",
     "Use environment variable or OAuth flow"),
    ("aws_access_key",
     r"""AKIA[0-9A-Z]{16}""",
     "critical", "CWE-798",
     "AWS access key ID detected",
     "Use IAM roles or AWS_ACCESS_KEY_ID env var"),
    ("aws_secret_key",
     r"""(?:aws_secret_access_key|secret_key)\s*[:=]\s*['"]([a-zA-Z0-9/+=]{40})['"]""",
     "critical", "CWE-798",
     "AWS secret access key detected",
     "Use IAM roles or AWS_SECRET_ACCESS_KEY env var"),
    ("private_key",
     r"""-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----""",
     "critical", "CWE-321",
     "Private key embedded in source code",
     "Store keys in a secure vault, never commit to source control"),
    ("bearer_token",
     r"""(?:bearer|token|auth)\s*[:=]\s*['"]([a-zA-Z0-9_\-.]{20,})['"]""",
     "medium", "CWE-798",
     "Possible hardcoded bearer/auth token",
     "Use environment variables for authentication tokens"),
    ("password_assignment",
     r"""(?:password|passwd|pwd|secret)\s*[:=]\s*['"](?!(?:None|null|''|""|<|{|\*|test|example|placeholder|changeme)\b)[^'"]{8,}['"]""",
     "high", "CWE-798",
     "Possible hardcoded password or secret",
     "Use environment variables or a secrets manager"),
    ("jwt_token",
     r"""eyJ[a-zA-Z0-9_-]{10,}\.eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}""",
     "high", "CWE-798",
     "JWT token detected in source code",
     "Never hardcode JWT tokens; generate at runtime"),
    ("slack_webhook",
     r"""https://hooks\.slack\.com/services/T[a-zA-Z0-9_]+/B[a-zA-Z0-9_]+/[a-zA-Z0-9_]+""",
     "high", "CWE-798",
     "Slack webhook URL detected",
     "Store webhook URL in environment variable"),
    ("connection_string",
     r"""(?:mysql|postgres|postgresql|mongodb|redis)://[^'"\s]{10,}""",
     "high", "CWE-798",
     "Database connection string with possible credentials",
     "Use DATABASE_URL environment variable"),
]

SECRET_PATTERNS: list[SecurityPattern] = [
    SecurityPattern(
        name=name,
        pattern=re.compile(regex, re.IGNORECASE if name not in (
            "openai_key", "anthropic_key", "github_token", "github_oauth",
            "aws_access_key", "private_key", "jwt_token", "slack_webhook",
        ) else 0),
        severity=Severity(sev),
        category=Category.SECRET,
        cwe_id=cwe,
        message=msg,
        recommendation=rec,
    )
    for name, regex, sev, cwe, msg, rec in _SECRET_DEFS
]

# ---------------------------------------------------------------------------
# Code anti-pattern detection (DETECTORS only — these find risky code)
# ---------------------------------------------------------------------------

# These regex patterns DETECT dangerous constructs in scanned source files.
# They do NOT themselves call or use any dangerous functions.
_ANTI_PATTERN_DEFS: list[tuple[str, str, str, str, str, str, list[str], bool]] = [
    # (name, regex, severity, cwe, message, recommendation, languages, scan_comments)
    # Python detectors
    ("python_dynamic_eval",
     r"""\beval\s*\(""",
     "high", "CWE-95",
     "Dynamic code evaluation detected — code injection risk",
     "Use ast.literal_eval() for data, or avoid dynamic evaluation",
     ["python"], False),
    ("python_dynamic_exec",
     r"""\bexec\s*\(""",
     "high", "CWE-95",
     "Dynamic code execution detected — code injection risk",
     "Avoid dynamic code execution; use structured alternatives",
     ["python"], False),
    ("python_shell_true",
     r"""subprocess\.\w+\([^)]*shell\s*=\s*True""",
     "high", "CWE-78",
     "subprocess with shell=True — command injection risk",
     "Use subprocess with shell=False and pass args as list",
     ["python"], False),
    ("python_shell_true_standalone",
     r"""shell\s*=\s*True""",
     "medium", "CWE-78",
     "shell=True detected — possible command injection risk (check if used with subprocess)",
     "Use subprocess with shell=False and pass args as list",
     ["python"], False),
    ("python_pickle_loads",
     r"""pickle\.loads?\s*\(""",
     "high", "CWE-502",
     "pickle.load/loads — deserialization of untrusted data risk",
     "Use json or other safe serialization formats",
     ["python"], False),
    ("python_yaml_unsafe",
     r"""yaml\.load\s*\([^,)]+\)""",
     "medium", "CWE-502",
     "yaml.load() without SafeLoader — arbitrary code execution risk",
     "Use yaml.safe_load() or yaml.load(data, Loader=SafeLoader)",
     ["python"], False),
    ("python_sql_fstring",
     r"""(?:cursor\.execute|\.execute)\s*\(\s*f['"]""",
     "high", "CWE-89",
     "f-string in SQL query — SQL injection risk",
     "Use parameterized queries with ? or %s placeholders",
     ["python"], False),
    ("python_verify_false",
     r"""verify\s*=\s*False""",
     "medium", "CWE-295",
     "SSL verification disabled (verify=False)",
     "Enable SSL verification; use a CA bundle if needed",
     ["python"], False),
    ("python_weak_hash",
     r"""hashlib\.(?:md5|sha1)\s*\(""",
     "low", "CWE-328",
     "Weak hash algorithm (MD5/SHA1) — not suitable for security",
     "Use hashlib.sha256() or hashlib.sha3_256() for security",
     ["python"], False),
    ("python_tempfile_insecure",
     r"""tempfile\.mktemp\s*\(""",
     "medium", "CWE-377",
     "tempfile.mktemp() — race condition vulnerability",
     "Use tempfile.mkstemp() or tempfile.NamedTemporaryFile()",
     ["python"], False),
    # JavaScript/TypeScript detectors
    ("js_dynamic_eval",
     r"""\beval\s*\(""",
     "high", "CWE-95",
     "Dynamic code evaluation detected — code injection risk",
     "Avoid dynamic evaluation; use JSON.parse() for data",
     ["javascript", "typescript"], False),
    ("js_innerhtml",
     r"""\.innerHTML\s*=""",
     "medium", "CWE-79",
     "Direct innerHTML assignment — XSS risk",
     "Use textContent or a DOM sanitizer library",
     ["javascript", "typescript"], False),
    ("js_dangerously_set",
     r"""dangerouslySetInnerHTML""",
     "medium", "CWE-79",
     "dangerouslySetInnerHTML — XSS risk in React",
     "Sanitize content with DOMPurify before rendering",
     ["javascript", "typescript"], False),
    ("js_document_write",
     r"""document\.write\s*\(""",
     "medium", "CWE-79",
     "document.write() — XSS risk and bad practice",
     "Use DOM manipulation methods instead",
     ["javascript", "typescript"], False),
    # GlassWorm-class supply-chain detectors
    ("invisible_unicode_run",
     r"""[\u200B-\u200F\u2060-\u206F\uFE00-\uFE0F]{3,}|(?:[\U000E0000-\U000E007F]|[\U000E0100-\U000E01EF]){3,}""",
     "high", "CWE-506",
     "Run of invisible/non-rendering Unicode characters — possible steganographic payload (supply-chain attack)",
     "Audit with 'hexdump -C' or 'cat -v'; legitimate source should not contain runs of zero-width/variation-selector/tag characters",
     [], True),
    ("js_eval_on_decoded",
     r"""\b(?:eval|Function)\s*\(\s*(?:new\s+Function\s*\(\s*)?[^)]*?\b(?:atob|decodeURIComponent|unescape)\s*\(""",
     "critical", "CWE-94",
     "eval/Function() called on decoded data (atob/decodeURIComponent/unescape) — supply-chain obfuscation pattern",
     "Never eval decoded strings; use JSON.parse() for data, or refactor to explicit logic",
     ["javascript", "typescript"], False),
    ("js_eval_on_buffer",
     r"""\b(?:eval|Function)\s*\(\s*(?:new\s+Function\s*\(\s*)?[^)]*?Buffer\.from\s*\([^)]*?(?:['"]base64['"]|['"]hex['"])""",
     "critical", "CWE-94",
     "eval/Function() called on Buffer.from(..., 'base64'|'hex') decoded data — supply-chain obfuscation pattern",
     "Never eval decoded Buffers; refactor to explicit logic",
     ["javascript", "typescript"], False),
    ("js_eval_on_fromcharcode",
     r"""\b(?:eval|Function)\s*\(\s*(?:new\s+Function\s*\(\s*)?[^)]*?String\.fromCharCode\s*\(""",
     "critical", "CWE-94",
     "eval/Function() called on String.fromCharCode(...) — classic obfuscation pattern",
     "Never eval char-code sequences; this is almost always malicious",
     ["javascript", "typescript"], False),
    ("python_eval_on_b64decode",
     r"""\b(?:eval|exec)\s*\(\s*[^)]*?\b(?:b64decode|base64\.b64decode|urlsafe_b64decode)\s*\(""",
     "critical", "CWE-94",
     "eval/exec called on base64.b64decode(...) — supply-chain obfuscation pattern",
     "Never eval decoded data; refactor to explicit logic",
     ["python"], False),
    ("python_exec_on_codecs_decode",
     r"""\b(?:eval|exec)\s*\(\s*[^)]*?(?:\bcodecs\.decode\s*\(|\bbytes\.fromhex\s*\(|\bzlib\.decompress\s*\()""",
     "critical", "CWE-94",
     "eval/exec called on codecs.decode/bytes.fromhex/zlib.decompress — obfuscation pattern",
     "Never eval decoded/decompressed data; refactor to explicit logic",
     ["python"], False),
    ("python_exec_on_marshal_loads",
     r"""\b(?:eval|exec)\s*\(\s*[^)]*?\bmarshal\.loads\s*\(""",
     "critical", "CWE-94",
     "eval/exec called on marshal.loads(...) — code injection via deserialization",
     "Never execute marshal-deserialized data from untrusted sources",
     ["python"], False),
    # Shai-Hulud / dynamic-require obfuscation: require() with string concatenation
    # or template interpolation — used to evade literal 'child_process' string detection.
    ("js_dynamic_require_concat",
     r"""require\s*\(\s*(?:["'][^"']*["']\s*\+|`[^`]*\$\{)""",
     "critical", "CWE-94",
     "require() called with concatenated string or template interpolation — supply-chain obfuscation (Shai-Hulud style)",
     "Use static string literals for require(); dynamic module loading is almost always an obfuscation indicator",
     ["javascript", "typescript"], False),
    # MDN explicitly warns that string-argument setTimeout/setInterval behaves like eval().
    # Legitimate callers pass functions; string arguments are near-universally obfuscation.
    ("js_settimer_string_arg",
     r"""\b(?:setTimeout|setInterval)\s*\(\s*["'`]""",
     "high", "CWE-95",
     "setTimeout/setInterval called with a string argument — behaves like eval()",
     "Pass a function reference instead of a string; string args are implicit dynamic code execution",
     ["javascript", "typescript"], False),
    # -----------------------------------------------------------------------
    # Additional OWASP Top 10 detectors (Python)
    # -----------------------------------------------------------------------
    ("python_sql_format_string",
     r"""\b(?:cursor\.execute|\.execute)\s*\(\s*['"].*?%[sd]""",
     "high", "CWE-89",
     "SQL query uses %-formatting — SQL injection risk",
     "Use parameterized queries with ? or %s placeholders instead of string formatting",
     ["python"], False),
    ("python_sql_concat",
     r"""\b(?:cursor\.execute|\.execute)\s*\(\s*['"].*?\+""",
     "high", "CWE-89",
     "SQL query built with string concatenation — SQL injection risk",
     "Use parameterized queries with ? or %s placeholders instead of concatenation",
     ["python"], False),
    ("python_marshal_loads",
     r"""\bmarshal\.loads?\s*\(""",
     "high", "CWE-502",
     "marshal.load/loads — unsafe deserialization of arbitrary code objects",
     "Use json or other safe serialization formats; marshal is not safe for untrusted data",
     ["python"], False),
    ("python_debug_true",
     r"""\bDEBUG\s*=\s*True\b""",
     "medium", "CWE-489",
     "DEBUG mode enabled — may expose sensitive information in production",
     "Ensure DEBUG=False in production settings; use environment variables for configuration",
     ["python"], False),
    ("python_assert_security",
     r"""\bassert\b(?!.*(?:test|spec))""",
     "low", "CWE-617",
     "assert used outside tests — stripped in optimized mode (-O flag)",
     "Use explicit if/raise for security checks; assert is stripped with python -O",
     ["python"], False),
    ("hardcoded_ip_address",
     r"""(?:['"])\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?:['"])""",
     "medium", "CWE-1188",
     "Hardcoded IP address detected — may hinder deployment flexibility",
     "Use configuration files or environment variables for IP addresses",
     [], False),
    ("python_os_system",
     r"""\bos\.system\s*\(""",
     "high", "CWE-78",
     "os.system() — command injection risk via shell execution",
     "Use subprocess.run() with shell=False and pass args as a list",
     ["python"], False),
    ("python_popen",
     r"""\bos\.popen\s*\(""",
     "high", "CWE-78",
     "os.popen() — command injection risk via shell execution",
     "Use subprocess.run() with shell=False and pass args as a list",
     ["python"], False),
    ("python_ssrf_request",
     r"""\brequests\.(?:get|post|put|delete|patch|head)\s*\(\s*(?:f['"]|[^'"]*?\+|.*?\.format\()""",
     "high", "CWE-918",
     "HTTP request with dynamically constructed URL — potential SSRF",
     "Validate and whitelist URLs before making requests; avoid user-controlled URL construction",
     ["python"], False),
    ("python_path_traversal",
     r"""\bopen\s*\(\s*(?:os\.path\.join\s*\()?.*?(?:request|params|query|input|args)\b""",
     "high", "CWE-22",
     "File open with user-controlled path — path traversal risk",
     "Validate and sanitize file paths; use os.path.realpath() and check against allowed directories",
     ["python"], False),
    ("python_weak_random",
     r"""\brandom\.(?:random|randint|choice|randrange)\s*\(""",
     "low", "CWE-330",
     "Weak PRNG (random module) — not suitable for security-sensitive operations",
     "Use secrets module or os.urandom() for cryptographic randomness",
     ["python"], False),
    ("python_cors_wildcard",
     r"""(?:CORS|cors).*?\*""",
     "medium", "CWE-942",
     "CORS wildcard (*) allows any origin — may expose API to cross-origin attacks",
     "Restrict CORS to specific trusted origins instead of using wildcard",
     [], False),
    # -----------------------------------------------------------------------
    # Additional OWASP Top 10 detectors (JavaScript / TypeScript)
    # -----------------------------------------------------------------------
    ("js_no_escape_html",
     r"""\.html\s*\(\s*(?!['"]<)""",
     "medium", "CWE-79",
     "jQuery .html() with dynamic content — XSS risk",
     "Sanitize content before inserting; use .text() for untrusted data or DOMPurify for HTML",
     ["javascript", "typescript"], False),
    ("js_url_redirect",
     r"""(?:window\.location|location\.href|location\.assign|location\.replace)\s*=\s*(?!['"])""",
     "high", "CWE-601",
     "Open redirect via dynamic URL assignment — phishing risk",
     "Validate redirect URLs against a whitelist of allowed destinations",
     ["javascript", "typescript"], False),
    ("js_postmessage_wildcard",
     r"""\.postMessage\s*\([^)]*?,\s*['"]\*""",
     "medium", "CWE-346",
     "postMessage with wildcard origin (*) — message sent to any window",
     "Specify the exact target origin instead of '*' in postMessage",
     ["javascript", "typescript"], False),
    ("js_unsafe_regex",
     r"""new\s+RegExp\s*\(\s*(?!['"])""",
     "medium", "CWE-1333",
     "RegExp constructed from variable — potential ReDoS if user-controlled",
     "Validate and sanitize input before constructing RegExp; consider using a regex-safe library",
     ["javascript", "typescript"], False),
    ("js_prototype_pollution",
     r"""(?:__proto__|constructor\s*\.\s*prototype)\s*(?:\[|=)""",
     "high", "CWE-1321",
     "Prototype pollution — modifying Object.prototype can affect all objects",
     "Use Object.create(null), Map, or validate property names to prevent prototype pollution",
     ["javascript", "typescript"], False),
    ("js_child_process_exec",
     r"""\bexec\s*\(\s*(?!['"])""",
     "high", "CWE-78",
     "child_process.exec() with dynamic input — command injection risk",
     "Use execFile() or spawn() with arguments as an array; avoid shell interpolation",
     ["javascript", "typescript"], False),
    ("js_nosql_injection",
     r"""\$(?:gt|gte|lt|lte|ne|in|nin|or|and|not|regex|where|exists)\b""",
     "medium", "CWE-943",
     "NoSQL query operator detected — potential NoSQL injection if user-controlled",
     "Validate and sanitize query inputs; use explicit field comparisons instead of raw operators",
     ["javascript", "typescript"], False),
    # -----------------------------------------------------------------------
    # Additional OWASP Top 10 detectors (Go)
    # -----------------------------------------------------------------------
    ("go_sql_sprintf",
     r"""(?:fmt\.Sprintf|fmt\.Fprintf)\s*\([^)]*?(?:SELECT|INSERT|UPDATE|DELETE|WHERE|FROM)""",
     "high", "CWE-89",
     "SQL query built with fmt.Sprintf — SQL injection risk",
     "Use parameterized queries with database/sql placeholder syntax ($1, ?, etc.)",
     ["go"], False),
    ("go_unhandled_error",
     r"""(?:_\s*(?:,\s*)?=\s*|(?:,\s*)?_\s*=)\s*\w+\.\w+\(""",
     "low", "CWE-391",
     "Error return value ignored (assigned to _) — may mask failures",
     "Handle the error explicitly; at minimum log it for debugging",
     ["go"], False),
    ("go_tls_insecure",
     r"""InsecureSkipVerify\s*:\s*true""",
     "high", "CWE-295",
     "TLS certificate verification disabled (InsecureSkipVerify: true)",
     "Enable TLS verification; use a custom CA certificate pool if needed",
     ["go"], False),
    # -----------------------------------------------------------------------
    # Additional OWASP Top 10 detectors (Java / Kotlin)
    # -----------------------------------------------------------------------
    ("java_sql_concat",
     r"""(?:Statement|PreparedStatement)\s*.*?(?:executeQuery|executeUpdate|execute)\s*\(\s*(?!['"])""",
     "high", "CWE-89",
     "SQL execution with dynamic input — SQL injection risk",
     "Use PreparedStatement with parameterized queries (? placeholders)",
     ["java", "kotlin"], False),
    ("java_xxe",
     r"""(?:XMLInputFactory|DocumentBuilderFactory|SAXParserFactory)\.newInstance\(\)""",
     "medium", "CWE-611",
     "XML parser created without disabling external entities — XXE risk",
     "Disable external entities: setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true) and disable DTDs",
     ["java", "kotlin"], False),
    ("java_deserialization",
     r"""\bObjectInputStream\s*\(""",
     "high", "CWE-502",
     "ObjectInputStream — unsafe deserialization of untrusted data",
     "Use JSON/protobuf for serialization; if ObjectInputStream is required, use an allowlist filter",
     ["java", "kotlin"], False),
    ("java_weak_crypto",
     r"""Cipher\.getInstance\s*\(\s*['"](?:DES|RC4|Blowfish|RC2)""",
     "high", "CWE-327",
     "Weak cipher algorithm (DES/RC4/Blowfish/RC2) — easily broken",
     "Use AES-256-GCM or ChaCha20-Poly1305 for symmetric encryption",
     ["java", "kotlin"], False),
    # -----------------------------------------------------------------------
    # Additional OWASP Top 10 detectors (Ruby)
    # -----------------------------------------------------------------------
    ("ruby_system_call",
     r"""\b(?:system|exec|spawn|IO\.popen)\s*\(?\s*(?!['"])""",
     "high", "CWE-78",
     "Shell command execution with dynamic input — command injection risk",
     "Use parameterized system calls; pass command and arguments as separate array elements",
     ["ruby"], False),
    ("ruby_send_dynamic",
     r"""\.send\s*\(\s*(?:params|request|input)""",
     "high", "CWE-94",
     "Dynamic method dispatch (.send) with user-controlled input — code injection risk",
     "Validate method names against an explicit allowlist before calling .send",
     ["ruby"], False),
    # -----------------------------------------------------------------------
    # Multi-language detectors
    # -----------------------------------------------------------------------
    ("hardcoded_localhost",
     r"""['"](?:localhost|127\.0\.0\.1|0\.0\.0\.0):\d{2,5}['"]""",
     "low", "CWE-1188",
     "Hardcoded localhost address with port — may not work in containerized/deployed environments",
     "Use configuration files or environment variables for host/port settings",
     [], False),
    ("todo_fixme_security",
     r"""(?:#|//|/\*)\s*(?:TODO|FIXME|HACK|XXX)\s*.*?(?:security|auth|password|token|secret|credential|encrypt)""",
     "info", "CWE-546",
     "Security-related TODO/FIXME comment — unresolved security concern in code",
     "Address the security concern described in the comment before deploying to production",
     [], True),
    # -----------------------------------------------------------------------
    # Django framework detectors
    # -----------------------------------------------------------------------
    ("django_mark_safe",
     r"""\bmark_safe\s*\(""",
     "high", "CWE-79",
     "mark_safe() with potentially untrusted content — XSS risk",
     "Avoid mark_safe() on user-supplied data; use Django template autoescaping or bleach.clean()",
     ["python"], False),
    ("django_raw_sql",
     r"""\b(?:raw|extra)\s*\(\s*(?:f['"]|['"].*?%[sd])""",
     "high", "CWE-89",
     "SQL injection via Django raw()/extra() with string formatting",
     "Use parameterized queries: Model.objects.raw('SELECT ... WHERE id = %s', [user_id])",
     ["python"], False),
    ("django_csrf_exempt",
     r"""@csrf_exempt""",
     "medium", "CWE-352",
     "CSRF protection disabled via @csrf_exempt decorator",
     "Remove @csrf_exempt unless the endpoint genuinely requires it (e.g., webhook); use csrf_protect for selective protection",
     ["python"], False),
    ("django_debug_toolbar",
     r"""debug_toolbar""",
     "low", "CWE-489",
     "Django Debug Toolbar reference detected — must not be enabled in production",
     "Ensure debug_toolbar is only in INSTALLED_APPS when DEBUG=True; remove from production settings",
     ["python"], False),
    ("django_secret_key_hardcoded",
     r"""SECRET_KEY\s*=\s*['"][^'"]{10,}['"]""",
     "high", "CWE-798",
     "Hardcoded Django SECRET_KEY — compromises session security and CSRF protection",
     "Load SECRET_KEY from environment variable: SECRET_KEY = os.environ['DJANGO_SECRET_KEY']",
     ["python"], False),
    ("django_allowed_hosts_wildcard",
     r"""ALLOWED_HOSTS\s*=\s*\[['"\*]""",
     "medium", "CWE-942",
     "ALLOWED_HOSTS contains wildcard '*' — allows HTTP Host header attacks",
     "Set ALLOWED_HOSTS to specific domain names: ALLOWED_HOSTS = ['example.com']",
     ["python"], False),
    ("django_safe_string",
     r"""\bSafeString\s*\(|SafeText\s*\(|SafeData\s*\(""",
     "medium", "CWE-79",
     "Wrapping content in SafeString/SafeText/SafeData bypasses template autoescaping — XSS risk",
     "Let Django's template autoescaping handle output; only use Safe* wrappers after sanitizing with bleach",
     ["python"], False),
    ("django_session_cookie_insecure",
     r"""SESSION_COOKIE_SECURE\s*=\s*False""",
     "medium", "CWE-614",
     "Django session cookie transmitted without Secure flag — vulnerable to interception over HTTP",
     "Set SESSION_COOKIE_SECURE = True in production to require HTTPS for session cookies",
     ["python"], False),
    # -----------------------------------------------------------------------
    # Flask framework detectors
    # -----------------------------------------------------------------------
    ("flask_debug_mode",
     r"""app\.run\s*\([^)]*debug\s*=\s*True""",
     "high", "CWE-489",
     "Flask app running with debug=True — exposes interactive debugger and reloader in production",
     "Set debug=False in production; use FLASK_DEBUG environment variable for development",
     ["python"], False),
    ("flask_send_file_user_input",
     r"""send_file\s*\(\s*(?:request|f['"]|.*?\+)""",
     "high", "CWE-22",
     "Flask send_file() with user-controlled path — path traversal risk",
     "Use send_from_directory() with a fixed base directory; validate and sanitize filenames",
     ["python"], False),
    ("flask_jsonify_user_data",
     r"""make_response\s*\(\s*(?:request|f['"])""",
     "medium", "CWE-79",
     "Flask make_response() with unescaped user data — potential XSS or injection",
     "Use flask.jsonify() for JSON responses; escape HTML content with markupsafe.escape()",
     ["python"], False),
    ("flask_secret_key_hardcoded",
     r"""app\.secret_key\s*=\s*['"][^'"]{5,}['"]""",
     "high", "CWE-798",
     "Hardcoded Flask secret_key — compromises session signing and CSRF tokens",
     "Load secret_key from environment variable: app.secret_key = os.environ['FLASK_SECRET_KEY']",
     ["python"], False),
    ("flask_no_csrf",
     r"""WTF_CSRF_ENABLED\s*=\s*False""",
     "medium", "CWE-352",
     "CSRF protection disabled in Flask-WTF — forms vulnerable to cross-site request forgery",
     "Enable CSRF protection: set WTF_CSRF_ENABLED = True or remove the override",
     ["python"], False),
    # -----------------------------------------------------------------------
    # Express / Node.js framework detectors
    # -----------------------------------------------------------------------
    ("express_cors_wildcard",
     r"""cors\s*\(\s*\)""",
     "medium", "CWE-942",
     "Express CORS middleware with default config — allows all origins",
     "Configure cors() with specific origin: cors({ origin: 'https://example.com' })",
     ["javascript", "typescript"], False),
    ("express_helmet_disabled",
     r"""app\.disable\s*\(\s*['"]x-powered-by['"]""",
     "low", "CWE-200",
     "Manual X-Powered-By removal instead of using helmet — may miss other security headers",
     "Use helmet middleware for comprehensive security headers: app.use(helmet())",
     ["javascript", "typescript"], False),
    ("express_body_parser_limit",
     r"""bodyParser\.json\s*\(\s*\)""",
     "low", "CWE-400",
     "bodyParser.json() without size limit — vulnerable to large payload DoS",
     "Set a body size limit: bodyParser.json({ limit: '100kb' })",
     ["javascript", "typescript"], False),
    ("express_sql_template",
     r"""(?:query|execute)\s*\(\s*`[^`]*\$\{""",
     "high", "CWE-89",
     "SQL query built with template literal interpolation — SQL injection risk",
     "Use parameterized queries: db.query('SELECT * FROM users WHERE id = $1', [userId])",
     ["javascript", "typescript"], False),
    ("express_jwt_no_verify",
     r"""jwt\.decode\s*\([^)]*?,\s*\{[^}]*complete\s*:\s*true""",
     "high", "CWE-347",
     "JWT decoded without signature verification — token integrity not checked",
     "Use jwt.verify() instead of jwt.decode() to validate token signatures",
     ["javascript", "typescript"], False),
    ("express_rate_limit_missing",
     r"""app\.(?:post|put|delete)\s*\(\s*['"]\/(?:api|auth|login)""",
     "low", "CWE-799",
     "Sensitive API endpoint without apparent rate limiting — brute force risk",
     "Apply rate limiting middleware: app.use('/api', rateLimit({ windowMs: 15*60*1000, max: 100 }))",
     ["javascript", "typescript"], False),
    ("express_session_insecure",
     r"""(?:cookie|session).*?secure\s*:\s*false""",
     "medium", "CWE-614",
     "Session/cookie configured with secure: false — transmitted over HTTP",
     "Set secure: true in production to require HTTPS for cookies",
     ["javascript", "typescript"], False),
    # -----------------------------------------------------------------------
    # Spring / Java framework detectors
    # -----------------------------------------------------------------------
    ("spring_sql_concatenation",
     r"""@Query\s*\(\s*['"].*?\+""",
     "high", "CWE-89",
     "SQL injection in Spring @Query annotation via string concatenation",
     "Use named parameters in @Query: @Query('SELECT u FROM User u WHERE u.name = :name')",
     ["java", "kotlin"], False),
    ("spring_csrf_disabled",
     r"""\.csrf\s*\(\s*\)\s*\.disable\s*\(\)""",
     "medium", "CWE-352",
     "Spring Security CSRF protection disabled — forms vulnerable to cross-site request forgery",
     "Keep CSRF enabled for browser-facing endpoints; disable only for stateless APIs with token auth",
     ["java", "kotlin"], False),
    ("spring_cors_permit_all",
     r"""\.allowedOrigins\s*\(\s*['"]\*['"]""",
     "medium", "CWE-942",
     "Spring CORS configured with wildcard origin '*' — allows any domain to make requests",
     "Restrict allowedOrigins to specific trusted domains",
     ["java", "kotlin"], False),
    ("spring_actuator_exposed",
     r"""management\.endpoints\.web\.exposure\.include\s*=\s*\*""",
     "high", "CWE-200",
     "All Spring Boot actuator endpoints exposed — leaks environment, health, and config data",
     "Expose only needed endpoints: management.endpoints.web.exposure.include=health,info",
     ["java", "kotlin"], False),
    ("spring_hardcoded_password",
     r"""spring\.datasource\.password\s*=\s*[^\$]""",
     "high", "CWE-798",
     "Hardcoded database password in Spring configuration — credential exposure risk",
     "Use environment variable substitution: spring.datasource.password=${DB_PASSWORD}",
     ["java", "kotlin"], False),
    ("spring_security_permit_all",
     r"""\.permitAll\s*\(\s*\)""",
     "low", "CWE-862",
     "Spring Security .permitAll() — verify endpoint intentionally allows unauthenticated access",
     "Review whether the endpoint should require authentication; use .authenticated() for protected resources",
     ["java", "kotlin"], False),
    # -----------------------------------------------------------------------
    # Rails framework detectors
    # -----------------------------------------------------------------------
    ("rails_html_safe",
     r"""\.html_safe\b""",
     "high", "CWE-79",
     "Rails .html_safe on potentially untrusted content — XSS risk",
     "Use sanitize() helper or ERB autoescaping; only call .html_safe on content you fully control",
     ["ruby"], False),
    ("rails_raw_sql",
     r"""(?:find_by_sql|connection\.execute)\s*\(\s*(?!['"])""",
     "high", "CWE-89",
     "Rails raw SQL with dynamic input — SQL injection risk",
     "Use parameterized queries: User.find_by_sql(['SELECT * FROM users WHERE id = ?', id])",
     ["ruby"], False),
    ("rails_mass_assignment",
     r"""params\.permit!\b""",
     "high", "CWE-915",
     "Rails params.permit! allows mass assignment of all parameters — privilege escalation risk",
     "Use explicit strong parameters: params.require(:user).permit(:name, :email)",
     ["ruby"], False),
    ("rails_force_ssl_false",
     r"""force_ssl\s*=\s*false|config\.force_ssl\s*=\s*false""",
     "medium", "CWE-319",
     "Rails force_ssl disabled — application may serve requests over unencrypted HTTP",
     "Set config.force_ssl = true in production to redirect all HTTP to HTTPS",
     ["ruby"], False),
    ("rails_skip_forgery",
     r"""skip_before_action\s*:verify_authenticity_token""",
     "medium", "CWE-352",
     "Rails CSRF protection skipped via skip_before_action — forgery risk",
     "Keep CSRF verification enabled; skip only for API-only controllers with token authentication",
     ["ruby"], False),
    # -----------------------------------------------------------------------
    # General framework-agnostic detectors
    # -----------------------------------------------------------------------
    ("graphql_introspection",
     r"""introspection\s*[:=]\s*true""",
     "low", "CWE-200",
     "GraphQL introspection enabled — exposes full API schema to attackers in production",
     "Disable introspection in production: set introspection to false",
     [], False),
    ("hardcoded_port",
     r"""(?:PORT|port)\s*[:=]\s*\d{4,5}\b""",
     "low", "CWE-1188",
     "Hardcoded port number — reduces deployment flexibility",
     "Use environment variable: PORT = int(os.environ.get('PORT', 8080))",
     [], False),
    ("admin_route_unprotected",
     r"""(?:route|path|get|post)\s*\(\s*['"]\/admin""",
     "low", "CWE-862",
     "Admin route detected — ensure authentication and authorization middleware is applied",
     "Protect admin routes with authentication middleware and role-based access control",
     [], False),
    ("cookie_httponly_false",
     r"""httpOnly\s*[:=]\s*false|httponly\s*[:=]\s*False""",
     "medium", "CWE-1004",
     "Cookie without HttpOnly flag — accessible to JavaScript, increasing XSS impact",
     "Set httpOnly: true (or httponly=True) to prevent client-side script access to the cookie",
     [], False),
]

ANTI_PATTERNS: list[SecurityPattern] = [
    SecurityPattern(
        name=name,
        pattern=re.compile(regex),
        severity=Severity(sev),
        category=Category.ANTI_PATTERN,
        cwe_id=cwe,
        message=msg,
        recommendation=rec,
        languages=langs,
        scan_comments=scan_comments,
    )
    for name, regex, sev, cwe, msg, rec, langs, scan_comments in _ANTI_PATTERN_DEFS
]
