"""
Threat intelligence database for dangerous callables.

Contains known dangerous Python callables and their risk levels.
"""

from dataclasses import dataclass
from enum import IntEnum
from typing import Dict, Set, Tuple, Optional, List
import re


class RiskLevel(IntEnum):
    """Risk levels for callables."""
    SAFE = 0
    LOW = 25
    MEDIUM = 50
    HIGH = 75
    CRITICAL = 100


@dataclass
class CallableRisk:
    """Risk information for a callable."""
    module: str
    name: str
    risk_level: RiskLevel
    description: str
    category: str
    cve_references: List[str] = None

    def __post_init__(self):
        if self.cve_references is None:
            self.cve_references = []

    @property
    def full_path(self) -> str:
        return f"{self.module}.{self.name}"


# Critical callables - direct code execution
CRITICAL_CALLABLES: Dict[Tuple[str, str], CallableRisk] = {
    # OS command execution
    ("os", "system"): CallableRisk(
        "os", "system", RiskLevel.CRITICAL,
        "Execute shell command", "command_execution"
    ),
    # posix is the Unix implementation of os module
    ("posix", "system"): CallableRisk(
        "posix", "system", RiskLevel.CRITICAL,
        "Execute shell command (Unix)", "command_execution"
    ),
    ("posix", "popen"): CallableRisk(
        "posix", "popen", RiskLevel.CRITICAL,
        "Open pipe to command (Unix)", "command_execution"
    ),
    ("nt", "system"): CallableRisk(
        "nt", "system", RiskLevel.CRITICAL,
        "Execute shell command (Windows)", "command_execution"
    ),
    ("os", "popen"): CallableRisk(
        "os", "popen", RiskLevel.CRITICAL,
        "Open pipe to command", "command_execution"
    ),
    ("os", "popen2"): CallableRisk(
        "os", "popen2", RiskLevel.CRITICAL,
        "Open pipe to command", "command_execution"
    ),
    ("os", "popen3"): CallableRisk(
        "os", "popen3", RiskLevel.CRITICAL,
        "Open pipe to command", "command_execution"
    ),
    ("os", "popen4"): CallableRisk(
        "os", "popen4", RiskLevel.CRITICAL,
        "Open pipe to command", "command_execution"
    ),
    ("os", "spawn"): CallableRisk(
        "os", "spawn", RiskLevel.CRITICAL,
        "Spawn new process", "command_execution"
    ),
    ("os", "spawnl"): CallableRisk(
        "os", "spawnl", RiskLevel.CRITICAL,
        "Spawn new process", "command_execution"
    ),
    ("os", "spawnle"): CallableRisk(
        "os", "spawnle", RiskLevel.CRITICAL,
        "Spawn new process", "command_execution"
    ),
    ("os", "spawnlp"): CallableRisk(
        "os", "spawnlp", RiskLevel.CRITICAL,
        "Spawn new process", "command_execution"
    ),
    ("os", "spawnlpe"): CallableRisk(
        "os", "spawnlpe", RiskLevel.CRITICAL,
        "Spawn new process", "command_execution"
    ),
    ("os", "spawnv"): CallableRisk(
        "os", "spawnv", RiskLevel.CRITICAL,
        "Spawn new process", "command_execution"
    ),
    ("os", "spawnve"): CallableRisk(
        "os", "spawnve", RiskLevel.CRITICAL,
        "Spawn new process", "command_execution"
    ),
    ("os", "spawnvp"): CallableRisk(
        "os", "spawnvp", RiskLevel.CRITICAL,
        "Spawn new process", "command_execution"
    ),
    ("os", "spawnvpe"): CallableRisk(
        "os", "spawnvpe", RiskLevel.CRITICAL,
        "Spawn new process", "command_execution"
    ),
    ("os", "execl"): CallableRisk(
        "os", "execl", RiskLevel.CRITICAL,
        "Replace process with new program", "command_execution"
    ),
    ("os", "execle"): CallableRisk(
        "os", "execle", RiskLevel.CRITICAL,
        "Replace process with new program", "command_execution"
    ),
    ("os", "execlp"): CallableRisk(
        "os", "execlp", RiskLevel.CRITICAL,
        "Replace process with new program", "command_execution"
    ),
    ("os", "execlpe"): CallableRisk(
        "os", "execlpe", RiskLevel.CRITICAL,
        "Replace process with new program", "command_execution"
    ),
    ("os", "execv"): CallableRisk(
        "os", "execv", RiskLevel.CRITICAL,
        "Replace process with new program", "command_execution"
    ),
    ("os", "execve"): CallableRisk(
        "os", "execve", RiskLevel.CRITICAL,
        "Replace process with new program", "command_execution"
    ),
    ("os", "execvp"): CallableRisk(
        "os", "execvp", RiskLevel.CRITICAL,
        "Replace process with new program", "command_execution"
    ),
    ("os", "execvpe"): CallableRisk(
        "os", "execvpe", RiskLevel.CRITICAL,
        "Replace process with new program", "command_execution"
    ),
    ("os", "fork"): CallableRisk(
        "os", "fork", RiskLevel.CRITICAL,
        "Fork process", "process_control"
    ),
    ("os", "forkpty"): CallableRisk(
        "os", "forkpty", RiskLevel.CRITICAL,
        "Fork process with PTY", "process_control"
    ),
    ("os", "kill"): CallableRisk(
        "os", "kill", RiskLevel.HIGH,
        "Send signal to process", "process_control"
    ),
    ("os", "killpg"): CallableRisk(
        "os", "killpg", RiskLevel.HIGH,
        "Send signal to process group", "process_control"
    ),

    # subprocess module
    ("subprocess", "call"): CallableRisk(
        "subprocess", "call", RiskLevel.CRITICAL,
        "Execute command", "command_execution"
    ),
    ("subprocess", "check_call"): CallableRisk(
        "subprocess", "check_call", RiskLevel.CRITICAL,
        "Execute command with check", "command_execution"
    ),
    ("subprocess", "check_output"): CallableRisk(
        "subprocess", "check_output", RiskLevel.CRITICAL,
        "Execute command and capture output", "command_execution"
    ),
    ("subprocess", "run"): CallableRisk(
        "subprocess", "run", RiskLevel.CRITICAL,
        "Execute command", "command_execution"
    ),
    ("subprocess", "Popen"): CallableRisk(
        "subprocess", "Popen", RiskLevel.CRITICAL,
        "Create subprocess", "command_execution"
    ),
    ("subprocess", "getoutput"): CallableRisk(
        "subprocess", "getoutput", RiskLevel.CRITICAL,
        "Execute shell command", "command_execution"
    ),
    ("subprocess", "getstatusoutput"): CallableRisk(
        "subprocess", "getstatusoutput", RiskLevel.CRITICAL,
        "Execute shell command", "command_execution"
    ),

    # pty module
    ("pty", "spawn"): CallableRisk(
        "pty", "spawn", RiskLevel.CRITICAL,
        "Spawn process with PTY", "command_execution"
    ),
    ("pty", "fork"): CallableRisk(
        "pty", "fork", RiskLevel.CRITICAL,
        "Fork with PTY", "command_execution"
    ),

    # Code execution - builtins
    ("builtins", "eval"): CallableRisk(
        "builtins", "eval", RiskLevel.CRITICAL,
        "Evaluate Python expression", "code_execution"
    ),
    ("builtins", "exec"): CallableRisk(
        "builtins", "exec", RiskLevel.CRITICAL,
        "Execute Python code", "code_execution"
    ),
    ("builtins", "compile"): CallableRisk(
        "builtins", "compile", RiskLevel.CRITICAL,
        "Compile Python code", "code_execution"
    ),
    ("builtins", "__import__"): CallableRisk(
        "builtins", "__import__", RiskLevel.HIGH,
        "Import module dynamically", "import"
    ),
    ("builtins", "open"): CallableRisk(
        "builtins", "open", RiskLevel.MEDIUM,
        "Open file", "file_access"
    ),
    ("builtins", "input"): CallableRisk(
        "builtins", "input", RiskLevel.LOW,
        "Read user input", "user_interaction"
    ),

    # __builtins__ variants
    ("__builtin__", "eval"): CallableRisk(
        "__builtin__", "eval", RiskLevel.CRITICAL,
        "Evaluate Python expression", "code_execution"
    ),
    ("__builtin__", "exec"): CallableRisk(
        "__builtin__", "exec", RiskLevel.CRITICAL,
        "Execute Python code", "code_execution"
    ),
    ("__builtin__", "compile"): CallableRisk(
        "__builtin__", "compile", RiskLevel.CRITICAL,
        "Compile Python code", "code_execution"
    ),
    ("__builtin__", "__import__"): CallableRisk(
        "__builtin__", "__import__", RiskLevel.HIGH,
        "Import module dynamically", "import"
    ),

    # importlib
    ("importlib", "import_module"): CallableRisk(
        "importlib", "import_module", RiskLevel.HIGH,
        "Import module dynamically", "import"
    ),
    ("importlib.util", "find_spec"): CallableRisk(
        "importlib.util", "find_spec", RiskLevel.MEDIUM,
        "Find module spec", "import"
    ),

    # runpy
    ("runpy", "run_module"): CallableRisk(
        "runpy", "run_module", RiskLevel.CRITICAL,
        "Run module as script", "code_execution"
    ),
    ("runpy", "run_path"): CallableRisk(
        "runpy", "run_path", RiskLevel.CRITICAL,
        "Run file as script", "code_execution"
    ),
    ("runpy", "_run_code"): CallableRisk(
        "runpy", "_run_code", RiskLevel.CRITICAL,
        "Execute code object (internal)", "code_execution"
    ),
    ("runpy", "_run_module_code"): CallableRisk(
        "runpy", "_run_module_code", RiskLevel.CRITICAL,
        "Execute module code (internal)", "code_execution"
    ),

    # code module
    ("code", "interact"): CallableRisk(
        "code", "interact", RiskLevel.CRITICAL,
        "Start interactive interpreter", "code_execution"
    ),
    ("code", "compile_command"): CallableRisk(
        "code", "compile_command", RiskLevel.HIGH,
        "Compile interactive command", "code_execution"
    ),

    # execfile variants (Python 2 builtin, various reimplementations)
    ("builtins", "execfile"): CallableRisk(
        "builtins", "execfile", RiskLevel.CRITICAL,
        "Execute Python file", "code_execution"
    ),
    ("__builtin__", "execfile"): CallableRisk(
        "__builtin__", "execfile", RiskLevel.CRITICAL,
        "Execute Python file (Python 2)", "code_execution"
    ),

    # File operations
    ("io", "open"): CallableRisk(
        "io", "open", RiskLevel.MEDIUM,
        "Open file", "file_access"
    ),
    ("io", "FileIO"): CallableRisk(
        "io", "FileIO", RiskLevel.MEDIUM,
        "Low-level file I/O", "file_access"
    ),
    ("pathlib", "Path"): CallableRisk(
        "pathlib", "Path", RiskLevel.LOW,
        "Create path object", "file_access"
    ),
    # Pathlib Path methods that write
    ("pathlib.Path", "write_text"): CallableRisk(
        "pathlib.Path", "write_text", RiskLevel.HIGH,
        "Write text to file", "file_modification"
    ),
    ("pathlib.Path", "write_bytes"): CallableRisk(
        "pathlib.Path", "write_bytes", RiskLevel.HIGH,
        "Write bytes to file", "file_modification"
    ),
    ("pathlib.Path", "unlink"): CallableRisk(
        "pathlib.Path", "unlink", RiskLevel.HIGH,
        "Delete file", "file_modification"
    ),
    ("pathlib.Path", "rmdir"): CallableRisk(
        "pathlib.Path", "rmdir", RiskLevel.HIGH,
        "Delete directory", "file_modification"
    ),
    ("pathlib.Path", "rename"): CallableRisk(
        "pathlib.Path", "rename", RiskLevel.MEDIUM,
        "Rename file", "file_modification"
    ),
    ("pathlib.Path", "mkdir"): CallableRisk(
        "pathlib.Path", "mkdir", RiskLevel.LOW,
        "Create directory", "file_modification"
    ),
    ("pathlib.Path", "touch"): CallableRisk(
        "pathlib.Path", "touch", RiskLevel.LOW,
        "Create empty file", "file_modification"
    ),

    # shutil - file operations
    ("shutil", "rmtree"): CallableRisk(
        "shutil", "rmtree", RiskLevel.HIGH,
        "Remove directory tree", "file_modification"
    ),
    ("shutil", "move"): CallableRisk(
        "shutil", "move", RiskLevel.MEDIUM,
        "Move file or directory", "file_modification"
    ),
    ("shutil", "copy"): CallableRisk(
        "shutil", "copy", RiskLevel.MEDIUM,
        "Copy file", "file_modification"
    ),
    ("shutil", "copy2"): CallableRisk(
        "shutil", "copy2", RiskLevel.MEDIUM,
        "Copy file with metadata", "file_modification"
    ),
    ("shutil", "copytree"): CallableRisk(
        "shutil", "copytree", RiskLevel.MEDIUM,
        "Copy directory tree", "file_modification"
    ),

    # os file operations
    ("os", "remove"): CallableRisk(
        "os", "remove", RiskLevel.HIGH,
        "Remove file", "file_modification"
    ),
    ("os", "unlink"): CallableRisk(
        "os", "unlink", RiskLevel.HIGH,
        "Remove file", "file_modification"
    ),
    ("os", "rmdir"): CallableRisk(
        "os", "rmdir", RiskLevel.HIGH,
        "Remove directory", "file_modification"
    ),
    ("os", "removedirs"): CallableRisk(
        "os", "removedirs", RiskLevel.HIGH,
        "Remove directories", "file_modification"
    ),
    ("os", "rename"): CallableRisk(
        "os", "rename", RiskLevel.MEDIUM,
        "Rename file", "file_modification"
    ),
    ("os", "renames"): CallableRisk(
        "os", "renames", RiskLevel.MEDIUM,
        "Rename file recursively", "file_modification"
    ),
    ("os", "replace"): CallableRisk(
        "os", "replace", RiskLevel.MEDIUM,
        "Replace file", "file_modification"
    ),
    ("os", "mkdir"): CallableRisk(
        "os", "mkdir", RiskLevel.LOW,
        "Create directory", "file_modification"
    ),
    ("os", "makedirs"): CallableRisk(
        "os", "makedirs", RiskLevel.LOW,
        "Create directories", "file_modification"
    ),
    ("os", "chmod"): CallableRisk(
        "os", "chmod", RiskLevel.HIGH,
        "Change file permissions", "file_modification"
    ),
    ("os", "chown"): CallableRisk(
        "os", "chown", RiskLevel.HIGH,
        "Change file owner", "file_modification"
    ),

    # Network operations
    ("socket", "socket"): CallableRisk(
        "socket", "socket", RiskLevel.HIGH,
        "Create network socket", "network"
    ),
    ("socket", "create_connection"): CallableRisk(
        "socket", "create_connection", RiskLevel.HIGH,
        "Create network connection", "network"
    ),
    ("urllib.request", "urlopen"): CallableRisk(
        "urllib.request", "urlopen", RiskLevel.HIGH,
        "Open URL", "network"
    ),
    ("urllib.request", "urlretrieve"): CallableRisk(
        "urllib.request", "urlretrieve", RiskLevel.HIGH,
        "Download file from URL", "network"
    ),
    ("urllib.request", "Request"): CallableRisk(
        "urllib.request", "Request", RiskLevel.MEDIUM,
        "Create URL request", "network"
    ),
    ("http.client", "HTTPConnection"): CallableRisk(
        "http.client", "HTTPConnection", RiskLevel.HIGH,
        "Create HTTP connection", "network"
    ),
    ("http.client", "HTTPSConnection"): CallableRisk(
        "http.client", "HTTPSConnection", RiskLevel.HIGH,
        "Create HTTPS connection", "network"
    ),
    ("ftplib", "FTP"): CallableRisk(
        "ftplib", "FTP", RiskLevel.HIGH,
        "Create FTP connection", "network"
    ),
    ("smtplib", "SMTP"): CallableRisk(
        "smtplib", "SMTP", RiskLevel.HIGH,
        "Create SMTP connection", "network"
    ),

    # requests library (third-party but common)
    ("requests", "get"): CallableRisk(
        "requests", "get", RiskLevel.HIGH,
        "HTTP GET request", "network"
    ),
    ("requests", "post"): CallableRisk(
        "requests", "post", RiskLevel.HIGH,
        "HTTP POST request", "network"
    ),
    ("requests", "put"): CallableRisk(
        "requests", "put", RiskLevel.HIGH,
        "HTTP PUT request", "network"
    ),
    ("requests", "delete"): CallableRisk(
        "requests", "delete", RiskLevel.HIGH,
        "HTTP DELETE request", "network"
    ),
    ("requests", "request"): CallableRisk(
        "requests", "request", RiskLevel.HIGH,
        "HTTP request", "network"
    ),
    ("requests", "Session"): CallableRisk(
        "requests", "Session", RiskLevel.HIGH,
        "Create HTTP session", "network"
    ),

    # Deserialization chains (can lead to RCE)
    ("pickle", "loads"): CallableRisk(
        "pickle", "loads", RiskLevel.CRITICAL,
        "Deserialize pickle data - nested RCE", "deserialization"
    ),
    ("pickle", "load"): CallableRisk(
        "pickle", "load", RiskLevel.CRITICAL,
        "Deserialize pickle from file - nested RCE", "deserialization"
    ),
    ("_pickle", "loads"): CallableRisk(
        "_pickle", "loads", RiskLevel.CRITICAL,
        "Deserialize pickle data (C implementation)", "deserialization"
    ),
    ("cPickle", "loads"): CallableRisk(
        "cPickle", "loads", RiskLevel.CRITICAL,
        "Deserialize pickle data (legacy C)", "deserialization"
    ),
    ("marshal", "loads"): CallableRisk(
        "marshal", "loads", RiskLevel.CRITICAL,
        "Deserialize marshal data", "deserialization"
    ),
    ("marshal", "load"): CallableRisk(
        "marshal", "load", RiskLevel.CRITICAL,
        "Deserialize marshal from file", "deserialization"
    ),
    ("yaml", "load"): CallableRisk(
        "yaml", "load", RiskLevel.CRITICAL,
        "Deserialize YAML (unsafe)", "deserialization"
    ),
    ("yaml", "unsafe_load"): CallableRisk(
        "yaml", "unsafe_load", RiskLevel.CRITICAL,
        "Deserialize YAML (unsafe)", "deserialization"
    ),
    ("yaml", "full_load"): CallableRisk(
        "yaml", "full_load", RiskLevel.HIGH,
        "Deserialize YAML (full)", "deserialization"
    ),
    ("jsonpickle", "decode"): CallableRisk(
        "jsonpickle", "decode", RiskLevel.CRITICAL,
        "Deserialize jsonpickle", "deserialization"
    ),
    ("shelve", "open"): CallableRisk(
        "shelve", "open", RiskLevel.HIGH,
        "Open shelve database (uses pickle)", "deserialization"
    ),
    ("dill", "loads"): CallableRisk(
        "dill", "loads", RiskLevel.CRITICAL,
        "Deserialize dill data", "deserialization"
    ),
    ("cloudpickle", "loads"): CallableRisk(
        "cloudpickle", "loads", RiskLevel.CRITICAL,
        "Deserialize cloudpickle data", "deserialization"
    ),
    # numpy.load can execute pickle if allow_pickle=True
    ("numpy", "load"): CallableRisk(
        "numpy", "load", RiskLevel.HIGH,
        "NumPy load (may unpickle if allow_pickle=True)", "deserialization"
    ),
    ("numpy", "loads"): CallableRisk(
        "numpy", "loads", RiskLevel.HIGH,
        "NumPy loads (may unpickle)", "deserialization"
    ),

    # Code object creation (for sophisticated attacks)
    ("types", "CodeType"): CallableRisk(
        "types", "CodeType", RiskLevel.CRITICAL,
        "Create code object", "code_execution"
    ),
    ("types", "FunctionType"): CallableRisk(
        "types", "FunctionType", RiskLevel.CRITICAL,
        "Create function from code", "code_execution"
    ),
    ("types", "LambdaType"): CallableRisk(
        "types", "LambdaType", RiskLevel.CRITICAL,
        "Create lambda function", "code_execution"
    ),
    ("types", "MethodType"): CallableRisk(
        "types", "MethodType", RiskLevel.HIGH,
        "Create bound method", "code_execution"
    ),
    ("types", "ModuleType"): CallableRisk(
        "types", "ModuleType", RiskLevel.MEDIUM,
        "Create module object", "code_execution"
    ),

    # ctypes (can call arbitrary C functions)
    ("ctypes", "CDLL"): CallableRisk(
        "ctypes", "CDLL", RiskLevel.CRITICAL,
        "Load C library", "native_code"
    ),
    ("ctypes", "cdll"): CallableRisk(
        "ctypes", "cdll", RiskLevel.CRITICAL,
        "C library loader", "native_code"
    ),
    ("ctypes", "windll"): CallableRisk(
        "ctypes", "windll", RiskLevel.CRITICAL,
        "Windows DLL loader", "native_code"
    ),
    ("ctypes", "oledll"): CallableRisk(
        "ctypes", "oledll", RiskLevel.CRITICAL,
        "OLE DLL loader", "native_code"
    ),
    ("ctypes", "pydll"): CallableRisk(
        "ctypes", "pydll", RiskLevel.CRITICAL,
        "Python DLL loader", "native_code"
    ),
    ("ctypes", "pythonapi"): CallableRisk(
        "ctypes", "pythonapi", RiskLevel.CRITICAL,
        "Python C API access", "native_code"
    ),
    ("ctypes", "cast"): CallableRisk(
        "ctypes", "cast", RiskLevel.HIGH,
        "Cast pointer", "native_code"
    ),
    ("ctypes", "memmove"): CallableRisk(
        "ctypes", "memmove", RiskLevel.CRITICAL,
        "Copy memory", "native_code"
    ),
    ("ctypes", "memset"): CallableRisk(
        "ctypes", "memset", RiskLevel.CRITICAL,
        "Set memory", "native_code"
    ),
    ("ctypes", "string_at"): CallableRisk(
        "ctypes", "string_at", RiskLevel.HIGH,
        "Read memory as string", "native_code"
    ),

    # multiprocessing
    ("multiprocessing", "Process"): CallableRisk(
        "multiprocessing", "Process", RiskLevel.HIGH,
        "Create new process", "process_control"
    ),
    ("multiprocessing", "Pool"): CallableRisk(
        "multiprocessing", "Pool", RiskLevel.HIGH,
        "Create process pool", "process_control"
    ),

    # threading
    ("threading", "Thread"): CallableRisk(
        "threading", "Thread", RiskLevel.MEDIUM,
        "Create new thread", "concurrency"
    ),

    # getattr/setattr for attribute manipulation
    ("builtins", "getattr"): CallableRisk(
        "builtins", "getattr", RiskLevel.MEDIUM,
        "Get attribute dynamically", "introspection"
    ),
    ("builtins", "setattr"): CallableRisk(
        "builtins", "setattr", RiskLevel.MEDIUM,
        "Set attribute dynamically", "introspection"
    ),
    ("builtins", "delattr"): CallableRisk(
        "builtins", "delattr", RiskLevel.MEDIUM,
        "Delete attribute dynamically", "introspection"
    ),
    ("builtins", "globals"): CallableRisk(
        "builtins", "globals", RiskLevel.MEDIUM,
        "Access global namespace", "introspection"
    ),
    ("builtins", "locals"): CallableRisk(
        "builtins", "locals", RiskLevel.LOW,
        "Access local namespace", "introspection"
    ),
    ("builtins", "vars"): CallableRisk(
        "builtins", "vars", RiskLevel.LOW,
        "Access object namespace", "introspection"
    ),

    # Apply functions
    ("functools", "reduce"): CallableRisk(
        "functools", "reduce", RiskLevel.MEDIUM,
        "Apply function cumulatively", "functional"
    ),

    # sys module
    ("sys", "exit"): CallableRisk(
        "sys", "exit", RiskLevel.MEDIUM,
        "Exit interpreter", "process_control"
    ),
    ("sys", "modules"): CallableRisk(
        "sys", "modules", RiskLevel.MEDIUM,
        "Access loaded modules", "introspection"
    ),

    # webbrowser - can be used for phishing, data exfiltration
    ("webbrowser", "open"): CallableRisk(
        "webbrowser", "open", RiskLevel.HIGH,
        "Open URL in browser (potential phishing)", "external_app"
    ),
    ("webbrowser", "open_new"): CallableRisk(
        "webbrowser", "open_new", RiskLevel.HIGH,
        "Open URL in new browser (potential phishing)", "external_app"
    ),
    ("webbrowser", "open_new_tab"): CallableRisk(
        "webbrowser", "open_new_tab", RiskLevel.HIGH,
        "Open URL in new tab (potential phishing)", "external_app"
    ),

    # tempfile (for staging attacks)
    ("tempfile", "NamedTemporaryFile"): CallableRisk(
        "tempfile", "NamedTemporaryFile", RiskLevel.LOW,
        "Create named temp file", "file_access"
    ),
    ("tempfile", "mktemp"): CallableRisk(
        "tempfile", "mktemp", RiskLevel.LOW,
        "Create temp filename", "file_access"
    ),

    # =========================================================================
    # PickleCloak paper gadgets (Table VI) — "The Art of Hide and Seek"
    # arXiv:2508.19774
    # =========================================================================

    # --- ACE gadgets (Arbitrary Code Execution) - stdlib ---
    ("cProfile", "run"): CallableRisk(
        "cProfile", "run", RiskLevel.CRITICAL,
        "Profile/execute code string", "code_execution"
    ),
    ("cProfile", "runctx"): CallableRisk(
        "cProfile", "runctx", RiskLevel.CRITICAL,
        "Profile/execute code in context", "code_execution"
    ),
    ("cProfile.Profile", "run"): CallableRisk(
        "cProfile.Profile", "run", RiskLevel.CRITICAL,
        "Profile/execute code string", "code_execution"
    ),
    ("cProfile.Profile", "runctx"): CallableRisk(
        "cProfile.Profile", "runctx", RiskLevel.CRITICAL,
        "Profile/execute code in context", "code_execution"
    ),
    ("cgitb", "lookup"): CallableRisk(
        "cgitb", "lookup", RiskLevel.CRITICAL,
        "Evaluate expression via cgitb", "code_execution"
    ),
    ("code", "InteractiveInterpreter.runcode"): CallableRisk(
        "code", "InteractiveInterpreter.runcode", RiskLevel.CRITICAL,
        "Execute code via interpreter", "code_execution"
    ),
    ("code.InteractiveInterpreter", "runcode"): CallableRisk(
        "code.InteractiveInterpreter", "runcode", RiskLevel.CRITICAL,
        "Execute code via interpreter", "code_execution"
    ),
    ("dataclasses", "_create_fn"): CallableRisk(
        "dataclasses", "_create_fn", RiskLevel.CRITICAL,
        "Create and exec function dynamically", "code_execution"
    ),
    ("distutils.spawn", "spawn"): CallableRisk(
        "distutils.spawn", "spawn", RiskLevel.CRITICAL,
        "Spawn external command", "command_execution"
    ),
    ("doctest", "debug_script"): CallableRisk(
        "doctest", "debug_script", RiskLevel.CRITICAL,
        "Execute script via doctest debugger", "code_execution"
    ),
    ("doctest", "_normalize_module"): CallableRisk(
        "doctest", "_normalize_module", RiskLevel.CRITICAL,
        "Import and execute module", "code_execution"
    ),
    ("idlelib.calltip", "get_entity"): CallableRisk(
        "idlelib.calltip", "get_entity", RiskLevel.CRITICAL,
        "Evaluate expression via IDLE", "code_execution"
    ),
    ("idlelib.run", "Executive.runcode"): CallableRisk(
        "idlelib.run", "Executive.runcode", RiskLevel.CRITICAL,
        "Execute code via IDLE runner", "code_execution"
    ),
    ("idlelib.run.Executive", "runcode"): CallableRisk(
        "idlelib.run.Executive", "runcode", RiskLevel.CRITICAL,
        "Execute code via IDLE runner", "code_execution"
    ),
    ("profile", "run"): CallableRisk(
        "profile", "run", RiskLevel.CRITICAL,
        "Profile/execute code string", "code_execution"
    ),
    ("profile", "runctx"): CallableRisk(
        "profile", "runctx", RiskLevel.CRITICAL,
        "Profile/execute code in context", "code_execution"
    ),
    ("profile.Profile", "run"): CallableRisk(
        "profile.Profile", "run", RiskLevel.CRITICAL,
        "Profile/execute code string", "code_execution"
    ),
    ("profile.Profile", "runctx"): CallableRisk(
        "profile.Profile", "runctx", RiskLevel.CRITICAL,
        "Profile/execute code in context", "code_execution"
    ),
    ("logging.config", "_resolve"): CallableRisk(
        "logging.config", "_resolve", RiskLevel.CRITICAL,
        "Resolve and import arbitrary dotted name", "code_execution"
    ),
    ("logging.config", "_install_handlers"): CallableRisk(
        "logging.config", "_install_handlers", RiskLevel.CRITICAL,
        "Install log handlers (can execute code)", "code_execution"
    ),
    ("pydoc", "pipepager"): CallableRisk(
        "pydoc", "pipepager", RiskLevel.CRITICAL,
        "Pipe text through shell command", "command_execution"
    ),
    ("pydoc", "importfile"): CallableRisk(
        "pydoc", "importfile", RiskLevel.CRITICAL,
        "Import and execute arbitrary file", "code_execution"
    ),
    ("pydoc", "locate"): CallableRisk(
        "pydoc", "locate", RiskLevel.CRITICAL,
        "Locate and import arbitrary object", "code_execution"
    ),
    ("pydoc", "safeimport"): CallableRisk(
        "pydoc", "safeimport", RiskLevel.CRITICAL,
        "Import arbitrary module", "code_execution"
    ),
    ("pydoc", "tempfilepager"): CallableRisk(
        "pydoc", "tempfilepager", RiskLevel.CRITICAL,
        "Write to temp file and execute pager", "command_execution"
    ),
    ("trace", "Trace.run"): CallableRisk(
        "trace", "Trace.run", RiskLevel.CRITICAL,
        "Trace/execute code", "code_execution"
    ),
    ("trace.Trace", "run"): CallableRisk(
        "trace.Trace", "run", RiskLevel.CRITICAL,
        "Trace/execute code", "code_execution"
    ),
    ("trace", "Trace.runctx"): CallableRisk(
        "trace", "Trace.runctx", RiskLevel.CRITICAL,
        "Trace/execute code in context", "code_execution"
    ),
    ("trace.Trace", "runctx"): CallableRisk(
        "trace.Trace", "runctx", RiskLevel.CRITICAL,
        "Trace/execute code in context", "code_execution"
    ),
    ("unittest.loader", "TestLoader._get_module_from_name"): CallableRisk(
        "unittest.loader", "TestLoader._get_module_from_name", RiskLevel.CRITICAL,
        "Import module by name", "code_execution"
    ),
    ("unittest.loader.TestLoader", "_get_module_from_name"): CallableRisk(
        "unittest.loader.TestLoader", "_get_module_from_name", RiskLevel.CRITICAL,
        "Import module by name", "code_execution"
    ),
    ("unittest.mock", "_importer"): CallableRisk(
        "unittest.mock", "_importer", RiskLevel.CRITICAL,
        "Import arbitrary module via mock", "code_execution"
    ),
    ("uuid", "_get_command_stdout"): CallableRisk(
        "uuid", "_get_command_stdout", RiskLevel.CRITICAL,
        "Execute shell command for UUID", "command_execution"
    ),
    ("_osx_support", "_read_output"): CallableRisk(
        "_osx_support", "_read_output", RiskLevel.CRITICAL,
        "Execute shell command (macOS)", "command_execution"
    ),
    ("asyncio.unix_events", "_UnixSubprocessTransport"): CallableRisk(
        "asyncio.unix_events", "_UnixSubprocessTransport", RiskLevel.CRITICAL,
        "Create subprocess transport", "command_execution"
    ),
    ("lib2to3.pgen2.grammar", "Grammar.loads"): CallableRisk(
        "lib2to3.pgen2.grammar", "Grammar.loads", RiskLevel.CRITICAL,
        "Deserialize grammar (pickle loads)", "code_execution"
    ),
    ("lib2to3.pgen2.grammar.Grammar", "loads"): CallableRisk(
        "lib2to3.pgen2.grammar.Grammar", "loads", RiskLevel.CRITICAL,
        "Deserialize grammar (pickle loads)", "code_execution"
    ),
    ("test.support", "PythonSymlink._call"): CallableRisk(
        "test.support", "PythonSymlink._call", RiskLevel.CRITICAL,
        "Execute Python via symlink", "command_execution"
    ),
    ("test.support.PythonSymlink", "_call"): CallableRisk(
        "test.support.PythonSymlink", "_call", RiskLevel.CRITICAL,
        "Execute Python via symlink", "command_execution"
    ),
    ("idlelib.autocomplete", "AutoComplete.get_entity"): CallableRisk(
        "idlelib.autocomplete", "AutoComplete.get_entity", RiskLevel.CRITICAL,
        "Evaluate expression via IDLE autocomplete", "code_execution"
    ),
    ("idlelib.autocomplete.AutoComplete", "get_entity"): CallableRisk(
        "idlelib.autocomplete.AutoComplete", "get_entity", RiskLevel.CRITICAL,
        "Evaluate expression via IDLE autocomplete", "code_execution"
    ),
    ("idlelib.debugobj", "ObjectTreeItem.SetText"): CallableRisk(
        "idlelib.debugobj", "ObjectTreeItem.SetText", RiskLevel.CRITICAL,
        "Evaluate expression via IDLE debugger", "code_execution"
    ),
    ("idlelib.debugobj.ObjectTreeItem", "SetText"): CallableRisk(
        "idlelib.debugobj.ObjectTreeItem", "SetText", RiskLevel.CRITICAL,
        "Evaluate expression via IDLE debugger", "code_execution"
    ),

    # --- NumPy ACE gadgets ---
    ("numpy.f2py.capi_maps", "getinit"): CallableRisk(
        "numpy.f2py.capi_maps", "getinit", RiskLevel.CRITICAL,
        "Execute code via f2py init generation", "code_execution"
    ),
    ("numpy.f2py.crackfortran", "_eval_scalar"): CallableRisk(
        "numpy.f2py.crackfortran", "_eval_scalar", RiskLevel.CRITICAL,
        "Evaluate expression via f2py", "code_execution"
    ),
    ("numpy.f2py.crackfortran", "analyzevars"): CallableRisk(
        "numpy.f2py.crackfortran", "analyzevars", RiskLevel.CRITICAL,
        "Execute code via f2py variable analysis", "code_execution"
    ),
    ("numpy.f2py.crackfortran", "myeval"): CallableRisk(
        "numpy.f2py.crackfortran", "myeval", RiskLevel.CRITICAL,
        "Evaluate expression via f2py", "code_execution"
    ),
    ("numpy.f2py.crackfortran", "param_eval"): CallableRisk(
        "numpy.f2py.crackfortran", "param_eval", RiskLevel.CRITICAL,
        "Evaluate parameter expression via f2py", "code_execution"
    ),
    ("numpy.f2py.crackfortran", "vars2fortran"): CallableRisk(
        "numpy.f2py.crackfortran", "vars2fortran", RiskLevel.CRITICAL,
        "Execute code via f2py variable conversion", "code_execution"
    ),
    ("numpy.f2py.diagnose", "run_command"): CallableRisk(
        "numpy.f2py.diagnose", "run_command", RiskLevel.CRITICAL,
        "Execute shell command via f2py", "command_execution"
    ),
    ("numpy.f2py.capi_maps", "load_f2cmap_file"): CallableRisk(
        "numpy.f2py.capi_maps", "load_f2cmap_file", RiskLevel.CRITICAL,
        "Execute code via f2py map loading", "code_execution"
    ),
    ("numpy.distutils.cpuinfo", "command_by_line"): CallableRisk(
        "numpy.distutils.cpuinfo", "command_by_line", RiskLevel.CRITICAL,
        "Execute shell command for CPU info", "command_execution"
    ),
    ("numpy.distutils.cpuinfo", "command_info"): CallableRisk(
        "numpy.distutils.cpuinfo", "command_info", RiskLevel.CRITICAL,
        "Execute shell command for CPU info", "command_execution"
    ),
    ("numpy.distutils.cpuinfo", "getoutput"): CallableRisk(
        "numpy.distutils.cpuinfo", "getoutput", RiskLevel.CRITICAL,
        "Execute shell command and get output", "command_execution"
    ),
    ("numpy.distutils.exec_command", "_exec_command"): CallableRisk(
        "numpy.distutils.exec_command", "_exec_command", RiskLevel.CRITICAL,
        "Execute shell command via numpy", "command_execution"
    ),
    ("numpy.distutils.lib2def", "getnm"): CallableRisk(
        "numpy.distutils.lib2def", "getnm", RiskLevel.CRITICAL,
        "Execute nm command via numpy", "command_execution"
    ),
    ("numpy.distutils.misc_util", "get_cmd"): CallableRisk(
        "numpy.distutils.misc_util", "get_cmd", RiskLevel.CRITICAL,
        "Get and execute command", "command_execution"
    ),
    ("numpy.distutils.misc_util", "exec_mod_from_location"): CallableRisk(
        "numpy.distutils.misc_util", "exec_mod_from_location", RiskLevel.CRITICAL,
        "Execute module from file location", "code_execution"
    ),
    ("numpy.core", "_ufunc_reconstruct"): CallableRisk(
        "numpy.core", "_ufunc_reconstruct", RiskLevel.CRITICAL,
        "Reconstruct ufunc (can execute code)", "code_execution"
    ),
    ("numpy.core.fromnumeric", "_wrapfunc"): CallableRisk(
        "numpy.core.fromnumeric", "_wrapfunc", RiskLevel.CRITICAL,
        "Wrap and call arbitrary function", "code_execution"
    ),
    ("numpy.lib.utils", "_makenamedict"): CallableRisk(
        "numpy.lib.utils", "_makenamedict", RiskLevel.CRITICAL,
        "Import and inspect module", "code_execution"
    ),
    ("numpy.testing._private.utils", "measure"): CallableRisk(
        "numpy.testing._private.utils", "measure", RiskLevel.CRITICAL,
        "Execute code string for measurement", "code_execution"
    ),
    ("numpy.testing._private.utils", "runstring"): CallableRisk(
        "numpy.testing._private.utils", "runstring", RiskLevel.CRITICAL,
        "Execute code string", "code_execution"
    ),
    ("numpy.core.tests.test_multiarray", "TestPickling._loads"): CallableRisk(
        "numpy.core.tests.test_multiarray", "TestPickling._loads", RiskLevel.CRITICAL,
        "Pickle loads via numpy test", "deserialization"
    ),
    ("numpy.core.tests.test_multiarray.TestPickling", "_loads"): CallableRisk(
        "numpy.core.tests.test_multiarray.TestPickling", "_loads", RiskLevel.CRITICAL,
        "Pickle loads via numpy test", "deserialization"
    ),

    # --- SymPy ACE gadgets ---
    ("sympy", "lazy_function"): CallableRisk(
        "sympy", "lazy_function", RiskLevel.CRITICAL,
        "Lazy import and call function", "code_execution"
    ),
    ("sympy", "sympify"): CallableRisk(
        "sympy", "sympify", RiskLevel.CRITICAL,
        "Parse and evaluate expression (uses eval)", "code_execution"
    ),
    ("sympy.parsing.sympy_parser", "eval_expr"): CallableRisk(
        "sympy.parsing.sympy_parser", "eval_expr", RiskLevel.CRITICAL,
        "Evaluate parsed expression", "code_execution"
    ),
    ("sympy.physics.mechanics.functions", "_sub_func"): CallableRisk(
        "sympy.physics.mechanics.functions", "_sub_func", RiskLevel.CRITICAL,
        "Execute substitution function", "code_execution"
    ),
    ("sympy.printing.tests.test_repr", "sT"): CallableRisk(
        "sympy.printing.tests.test_repr", "sT", RiskLevel.CRITICAL,
        "Evaluate expression via sympify", "code_execution"
    ),
    ("sympy.utilities.source", "get_class"): CallableRisk(
        "sympy.utilities.source", "get_class", RiskLevel.CRITICAL,
        "Import and return arbitrary class", "code_execution"
    ),
    ("sympy.utilities.lambdify", "lambdify"): CallableRisk(
        "sympy.utilities.lambdify", "lambdify", RiskLevel.CRITICAL,
        "Create callable from expression (uses eval)", "code_execution"
    ),
    ("sympy.utilities._compilation.compilation", "compile_run_strings"): CallableRisk(
        "sympy.utilities._compilation.compilation", "compile_run_strings", RiskLevel.CRITICAL,
        "Compile and run code strings", "code_execution"
    ),
    ("sympy.external.importtools", "import_module"): CallableRisk(
        "sympy.external.importtools", "import_module", RiskLevel.CRITICAL,
        "Import arbitrary module", "code_execution"
    ),
    ("sympy.external.tests.test_codegen", "try_run"): CallableRisk(
        "sympy.external.tests.test_codegen", "try_run", RiskLevel.CRITICAL,
        "Compile and run code", "code_execution"
    ),
    ("sympy.polys.monomials", "MonomialOps._build"): CallableRisk(
        "sympy.polys.monomials", "MonomialOps._build", RiskLevel.CRITICAL,
        "Build and exec monomial operations", "code_execution"
    ),
    ("sympy.polys.monomials.MonomialOps", "_build"): CallableRisk(
        "sympy.polys.monomials.MonomialOps", "_build", RiskLevel.CRITICAL,
        "Build and exec monomial operations", "code_execution"
    ),
    ("sympy.plotting.experimental_lambdify", "Lambdifier.__init__"): CallableRisk(
        "sympy.plotting.experimental_lambdify", "Lambdifier.__init__", RiskLevel.CRITICAL,
        "Create lambdifier (uses eval)", "code_execution"
    ),
    ("sympy.plotting.experimental_lambdify.Lambdifier", "__init__"): CallableRisk(
        "sympy.plotting.experimental_lambdify.Lambdifier", "__init__", RiskLevel.CRITICAL,
        "Create lambdifier (uses eval)", "code_execution"
    ),

    # --- Pandas ACE gadgets ---
    ("pandas._version", "run_command"): CallableRisk(
        "pandas._version", "run_command", RiskLevel.CRITICAL,
        "Execute shell command for versioning", "command_execution"
    ),

    # --- File Write gadgets ---
    ("uu", "decode"): CallableRisk(
        "uu", "decode", RiskLevel.HIGH,
        "Decode uuencoded file (arbitrary write)", "file_write"
    ),
    ("trace", "CoverageResults.write_results_file"): CallableRisk(
        "trace", "CoverageResults.write_results_file", RiskLevel.HIGH,
        "Write coverage results to arbitrary file", "file_write"
    ),
    ("trace.CoverageResults", "write_results_file"): CallableRisk(
        "trace.CoverageResults", "write_results_file", RiskLevel.HIGH,
        "Write coverage results to arbitrary file", "file_write"
    ),
    ("tracemalloc", "Snapshot.dump"): CallableRisk(
        "tracemalloc", "Snapshot.dump", RiskLevel.HIGH,
        "Dump memory snapshot to file", "file_write"
    ),
    ("tracemalloc.Snapshot", "dump"): CallableRisk(
        "tracemalloc.Snapshot", "dump", RiskLevel.HIGH,
        "Dump memory snapshot to file", "file_write"
    ),
    ("profile", "Profile.dump_stats"): CallableRisk(
        "profile", "Profile.dump_stats", RiskLevel.HIGH,
        "Dump profiler stats to file", "file_write"
    ),
    ("profile.Profile", "dump_stats"): CallableRisk(
        "profile.Profile", "dump_stats", RiskLevel.HIGH,
        "Dump profiler stats to file", "file_write"
    ),
    ("pipes", "Template.open_w"): CallableRisk(
        "pipes", "Template.open_w", RiskLevel.HIGH,
        "Open file for writing via pipe template", "file_write"
    ),
    ("pipes.Template", "open_w"): CallableRisk(
        "pipes.Template", "open_w", RiskLevel.HIGH,
        "Open file for writing via pipe template", "file_write"
    ),
    ("mailbox", "_create_carefully"): CallableRisk(
        "mailbox", "_create_carefully", RiskLevel.HIGH,
        "Create file carefully (arbitrary write)", "file_write"
    ),
    ("http.cookiejar", "LWPCookieJar.save"): CallableRisk(
        "http.cookiejar", "LWPCookieJar.save", RiskLevel.HIGH,
        "Save cookies to arbitrary file", "file_write"
    ),
    ("http.cookiejar.LWPCookieJar", "save"): CallableRisk(
        "http.cookiejar.LWPCookieJar", "save", RiskLevel.HIGH,
        "Save cookies to arbitrary file", "file_write"
    ),
    ("distutils.file_util", "_copy_file_contents"): CallableRisk(
        "distutils.file_util", "_copy_file_contents", RiskLevel.HIGH,
        "Copy file contents (arbitrary write)", "file_write"
    ),
    ("distutils.file_util", "write_file"): CallableRisk(
        "distutils.file_util", "write_file", RiskLevel.HIGH,
        "Write file (arbitrary write)", "file_write"
    ),
    ("xml.etree.ElementTree", "ElementTree.write"): CallableRisk(
        "xml.etree.ElementTree", "ElementTree.write", RiskLevel.HIGH,
        "Write XML to arbitrary file", "file_write"
    ),
    ("xml.etree.ElementTree.ElementTree", "write"): CallableRisk(
        "xml.etree.ElementTree.ElementTree", "write", RiskLevel.HIGH,
        "Write XML to arbitrary file", "file_write"
    ),
    ("numpy", "save"): CallableRisk(
        "numpy", "save", RiskLevel.HIGH,
        "Save array to arbitrary file", "file_write"
    ),
    ("numpy", "savez"): CallableRisk(
        "numpy", "savez", RiskLevel.HIGH,
        "Save arrays to arbitrary file", "file_write"
    ),
    ("numpy", "savez_compressed"): CallableRisk(
        "numpy", "savez_compressed", RiskLevel.HIGH,
        "Save compressed arrays to arbitrary file", "file_write"
    ),
    ("numpy", "savetxt"): CallableRisk(
        "numpy", "savetxt", RiskLevel.HIGH,
        "Save text to arbitrary file", "file_write"
    ),
    ("sympy.utilities.codegen", "CodeGen.write"): CallableRisk(
        "sympy.utilities.codegen", "CodeGen.write", RiskLevel.HIGH,
        "Write generated code to file", "file_write"
    ),
    ("sympy.utilities.codegen.CodeGen", "write"): CallableRisk(
        "sympy.utilities.codegen.CodeGen", "write", RiskLevel.HIGH,
        "Write generated code to file", "file_write"
    ),

    # --- File Read gadgets ---
    ("_pyio", "_open_code_with_warning"): CallableRisk(
        "_pyio", "_open_code_with_warning", RiskLevel.HIGH,
        "Open code file for reading", "file_read"
    ),
    ("turtle", "config_dict"): CallableRisk(
        "turtle", "config_dict", RiskLevel.HIGH,
        "Read turtle config file", "file_read"
    ),
    ("doctest", "_load_testfile"): CallableRisk(
        "doctest", "_load_testfile", RiskLevel.HIGH,
        "Load test file contents", "file_read"
    ),
    ("pkgutil", "ImpLoader.get_data"): CallableRisk(
        "pkgutil", "ImpLoader.get_data", RiskLevel.HIGH,
        "Read arbitrary package data", "file_read"
    ),
    ("pkgutil.ImpLoader", "get_data"): CallableRisk(
        "pkgutil.ImpLoader", "get_data", RiskLevel.HIGH,
        "Read arbitrary package data", "file_read"
    ),
    ("pydoc", "_url_handler"): CallableRisk(
        "pydoc", "_url_handler", RiskLevel.HIGH,
        "Handle URL (can read files)", "file_read"
    ),
    ("pipes", "Template.open_r"): CallableRisk(
        "pipes", "Template.open_r", RiskLevel.HIGH,
        "Open file for reading via pipe template", "file_read"
    ),
    ("pipes.Template", "open_r"): CallableRisk(
        "pipes.Template", "open_r", RiskLevel.HIGH,
        "Open file for reading via pipe template", "file_read"
    ),
    ("shlex", "shlex.sourcehook"): CallableRisk(
        "shlex", "shlex.sourcehook", RiskLevel.HIGH,
        "Open source file via shlex", "file_read"
    ),
    ("shlex.shlex", "sourcehook"): CallableRisk(
        "shlex.shlex", "sourcehook", RiskLevel.HIGH,
        "Open source file via shlex", "file_read"
    ),
    ("argparse", "FileType.__call__"): CallableRisk(
        "argparse", "FileType.__call__", RiskLevel.HIGH,
        "Open arbitrary file via argparse", "file_read"
    ),
    ("argparse.FileType", "__call__"): CallableRisk(
        "argparse.FileType", "__call__", RiskLevel.HIGH,
        "Open arbitrary file via argparse", "file_read"
    ),
    ("fileinput", "hook_compressed"): CallableRisk(
        "fileinput", "hook_compressed", RiskLevel.HIGH,
        "Open compressed file for reading", "file_read"
    ),
    ("numpy", "memmap"): CallableRisk(
        "numpy", "memmap", RiskLevel.HIGH,
        "Memory-map arbitrary file", "file_read"
    ),
    ("numpy", "fromfile"): CallableRisk(
        "numpy", "fromfile", RiskLevel.HIGH,
        "Read data from arbitrary file", "file_read"
    ),
    ("numpy", "genfromtxt"): CallableRisk(
        "numpy", "genfromtxt", RiskLevel.HIGH,
        "Read text data from arbitrary file", "file_read"
    ),
    ("numpy", "loadtxt"): CallableRisk(
        "numpy", "loadtxt", RiskLevel.HIGH,
        "Load text from arbitrary file", "file_read"
    ),
    ("numpy", "DataSource.open"): CallableRisk(
        "numpy", "DataSource.open", RiskLevel.HIGH,
        "Open arbitrary data source", "file_read"
    ),
    ("numpy.DataSource", "open"): CallableRisk(
        "numpy.DataSource", "open", RiskLevel.HIGH,
        "Open arbitrary data source", "file_read"
    ),
    ("pandas", "read_csv"): CallableRisk(
        "pandas", "read_csv", RiskLevel.HIGH,
        "Read arbitrary CSV file", "file_read"
    ),
    ("pandas", "read_excel"): CallableRisk(
        "pandas", "read_excel", RiskLevel.HIGH,
        "Read arbitrary Excel file", "file_read"
    ),
    ("pandas", "read_json"): CallableRisk(
        "pandas", "read_json", RiskLevel.HIGH,
        "Read arbitrary JSON file", "file_read"
    ),
    ("pandas", "read_table"): CallableRisk(
        "pandas", "read_table", RiskLevel.HIGH,
        "Read arbitrary table file", "file_read"
    ),

    # --- Network gadgets ---
    ("urllib.request", "URLopener.retrieve"): CallableRisk(
        "urllib.request", "URLopener.retrieve", RiskLevel.HIGH,
        "Retrieve URL (legacy opener)", "network"
    ),
    ("urllib.request.URLopener", "retrieve"): CallableRisk(
        "urllib.request.URLopener", "retrieve", RiskLevel.HIGH,
        "Retrieve URL (legacy opener)", "network"
    ),
    ("urllib.request", "URLopener.open"): CallableRisk(
        "urllib.request", "URLopener.open", RiskLevel.HIGH,
        "Open URL (legacy opener)", "network"
    ),
    ("urllib.request.URLopener", "open"): CallableRisk(
        "urllib.request.URLopener", "open", RiskLevel.HIGH,
        "Open URL (legacy opener)", "network"
    ),
    ("pandas", "read_pickle"): CallableRisk(
        "pandas", "read_pickle", RiskLevel.CRITICAL,
        "Deserialize pickle via pandas (can fetch URL)", "deserialization"
    ),

    # --- Helper gadgets (getattr equivalents) ---
    ("xmlrpc.server", "resolve_dotted_attribute"): CallableRisk(
        "xmlrpc.server", "resolve_dotted_attribute", RiskLevel.MEDIUM,
        "Resolve dotted attribute path (getattr chain)", "introspection"
    ),
    ("unittest.mock", "_dot_lookup"): CallableRisk(
        "unittest.mock", "_dot_lookup", RiskLevel.MEDIUM,
        "Lookup dotted attribute path (getattr chain)", "introspection"
    ),
    ("lib2to3.fixer_util", "attr_chain"): CallableRisk(
        "lib2to3.fixer_util", "attr_chain", RiskLevel.MEDIUM,
        "Chain attribute access", "introspection"
    ),
    ("test.support", "get_attribute"): CallableRisk(
        "test.support", "get_attribute", RiskLevel.MEDIUM,
        "Get attribute from object (getattr wrapper)", "introspection"
    ),
}

# Patterns for wildcard matching
DANGEROUS_MODULE_PATTERNS = [
    re.compile(r'^subprocess$'),
    re.compile(r'^os$'),
    re.compile(r'^pty$'),
    re.compile(r'^commands$'),  # Legacy Python 2
    re.compile(r'^runpy$'),     # Script execution
    re.compile(r'^posix$'),     # Unix os implementation
    re.compile(r'^nt$'),        # Windows os implementation
]


class ThreatDatabase:
    """Database of known dangerous callables."""

    def __init__(self):
        self.callables = dict(CRITICAL_CALLABLES)
        self._module_patterns = list(DANGEROUS_MODULE_PATTERNS)
        # Known dangerous functions that may appear in nested paths
        self._dangerous_funcs = {
            'system', 'popen', 'popen2', 'popen3', 'popen4',
            'spawn', 'spawnl', 'spawnle', 'spawnlp', 'spawnlpe',
            'spawnv', 'spawnve', 'spawnvp', 'spawnvpe',
            'exec', 'execl', 'execle', 'execlp', 'execlpe',
            'execv', 'execve', 'execvp', 'execvpe',
            'eval', 'compile', '__import__', 'execfile',
            'call', 'check_call', 'check_output', 'run', 'Popen',
            'loads', 'load', 'urlopen', 'urlretrieve',
            '_run_code', '_run_module_code', 'run_module', 'run_path',
            # PickleCloak paper gadget function names
            'runcode', 'runctx', 'sympify', 'lambdify',
            'pipepager', 'tempfilepager', 'importfile',
            'run_command', '_exec_command', 'getoutput',
            '_eval_scalar', 'myeval', 'param_eval',
            'eval_expr', 'compile_run_strings',
        }

    def lookup(self, module: str, name: str) -> Optional[CallableRisk]:
        """Look up a callable in the database."""
        # Direct lookup
        key = (module, name)
        if key in self.callables:
            return self.callables[key]

        # Check for module-level patterns
        for pattern in self._module_patterns:
            if pattern.match(module):
                # Return a generic risk for dangerous modules
                return CallableRisk(
                    module, name, RiskLevel.HIGH,
                    f"Function from dangerous module '{module}'",
                    "dangerous_module"
                )

        # Check for nested module paths like "torch serialization.os.system"
        # The dangerous callable may be embedded in the name field
        full_path = f"{module}.{name}"
        nested_risk = self._check_nested_path(full_path, module, name)
        if nested_risk:
            return nested_risk

        return None

    def _check_nested_path(self, full_path: str, module: str, name: str) -> Optional[CallableRisk]:
        """Check for dangerous patterns in nested module paths."""
        # Safe built-in types that are commonly used in pickle reconstruction
        safe_builtins = {
            'set', 'frozenset', 'list', 'dict', 'tuple', 'str', 'bytes', 'bytearray',
            'int', 'float', 'complex', 'bool', 'type', 'object', 'slice', 'range',
            'map', 'filter', 'zip', 'enumerate', 'reversed', 'sorted',
            'property', 'classmethod', 'staticmethod', 'super',
            # Python 2 compatibility types
            'long', 'unicode', 'basestring', 'xrange', 'buffer', 'file',
            # Safe built-in functions for object manipulation
            'getattr', 'hasattr', 'isinstance', 'issubclass', 'len', 'repr', 'hash',
            'id', 'dir', 'callable', 'iter', 'next', 'all', 'any', 'min', 'max', 'sum',
            'abs', 'round', 'pow', 'divmod', 'ord', 'chr', 'bin', 'hex', 'oct', 'format',
        }

        # Check if this is a safe builtin access (e.g., __builtin__.set)
        if module in ('builtins', '__builtin__', '__builtins__') and name in safe_builtins:
            return None  # Safe, not dangerous

        # Split on dots to find embedded dangerous modules
        parts = full_path.replace(' ', '.').split('.')

        for i, part in enumerate(parts):
            # Check if this part is a dangerous module
            if part in ('os', 'subprocess', 'builtins', '__builtin__', 'pty', 'commands', 'posix', 'nt', 'runpy'):
                # Get the remaining parts as the function name
                remaining = parts[i + 1:] if i + 1 < len(parts) else []
                if remaining:
                    func_name = remaining[0]
                    # Skip if it's a safe builtin type
                    if func_name in safe_builtins:
                        continue
                    if func_name in self._dangerous_funcs:
                        return CallableRisk(
                            part, func_name, RiskLevel.CRITICAL,
                            f"Nested path contains {part}.{func_name} - likely obfuscated attack",
                            "nested_module_attack"
                        )
                    # Even without specific function, the module is dangerous
                    return CallableRisk(
                        part, '.'.join(remaining), RiskLevel.HIGH,
                        f"Nested path contains dangerous module '{part}'",
                        "nested_module_attack"
                    )

            # Check for dangerous deserialization modules
            if part in ('pickle', '_pickle', 'cPickle', 'marshal', 'yaml'):
                remaining = parts[i + 1:] if i + 1 < len(parts) else []
                if remaining and remaining[0] in ('load', 'loads', 'unsafe_load'):
                    return CallableRisk(
                        part, remaining[0], RiskLevel.CRITICAL,
                        f"Nested deserialization chain: {part}.{remaining[0]}",
                        "deserialization_chain"
                    )

            # Check for pathlib.Path with dangerous methods
            if part == 'Path' and i > 0 and parts[i-1] == 'pathlib':
                remaining = parts[i + 1:] if i + 1 < len(parts) else []
                if remaining:
                    func_name = remaining[0]
                    if func_name in ('write_text', 'write_bytes', 'unlink', 'rmdir'):
                        return CallableRisk(
                            'pathlib.Path', func_name, RiskLevel.HIGH,
                            f"File modification via pathlib.Path.{func_name}",
                            "file_modification"
                        )

            # Check for dangerous function names anywhere in the path (e.g., _pydev_bundle._pydev_execfile.execfile)
            if part in self._dangerous_funcs:
                return CallableRisk(
                    module, name, RiskLevel.CRITICAL,
                    f"Dangerous function '{part}' found in callable path",
                    "dangerous_function"
                )

        return None

    def add_callable(self, risk: CallableRisk) -> None:
        """Add a callable to the database."""
        self.callables[(risk.module, risk.name)] = risk

    def get_risk_level(self, module: str, name: str) -> RiskLevel:
        """Get risk level for a callable."""
        risk = self.lookup(module, name)
        return risk.risk_level if risk else RiskLevel.SAFE


# Module-level convenience function
_default_db = ThreatDatabase()


def is_dangerous_callable(module: str, name: str) -> Tuple[bool, Optional[CallableRisk]]:
    """Check if a callable is dangerous."""
    risk = _default_db.lookup(module, name)
    if risk and risk.risk_level >= RiskLevel.MEDIUM:
        return True, risk
    return False, risk


def get_threat_database() -> ThreatDatabase:
    """Get the default threat database."""
    return _default_db
