Server Implementation
A tool handler passes model-supplied text into `eval`, `Function`, `exec`, or `compile`, letting the LLM (or whoever prompts it) execute arbitrary code in the server process.
Code injection is the stricter sibling of command injection: instead of running a shell, the application evaluates the attacker's input as code in the host language. In Python it looks like `eval(input_str)` or `exec(input_str)`; in JavaScript, `eval()` or `new Function(...)`. It is sometimes justified as a way to accept math expressions or template strings, but in practice there is always a safer alternative.
Tool authors sometimes add an `evaluate` or `transform` tool so the model can do "a little scripting." The model happily obliges and sends in whatever text makes the task easier — including code it was tricked into emitting by indirect prompt injection in a document it retrieved. The result is remote code execution triggered by a malicious wiki page.
@server.tool() |
def evaluate(expression: str) -> str: |
"""Evaluate a Python expression and return the result.""" |
return str(eval(expression)) |
from asteval import Interpreter |
_aeval = Interpreter(minimal=True, use_numpy=False) |
@server.tool() |
def evaluate(expression: str) -> str: |
"""Evaluate a numeric expression (arithmetic only, no names or calls).""" |
if len(expression) > 256: |
raise ValueError("expression too long") |
result = _aeval(expression) |
if _aeval.error: |
raise ValueError("invalid expression") |
return str(result) |
We match calls to `eval`, `exec`, `Function`, `compile`, `vm.runInThisContext`, and the language equivalents, then check whether any tool-handler parameter flows into them. Taint analysis runs across imports and helper functions.
See the full threat catalog for every documented detection.
CVEs of the same CWE class. Not MCP-specific, but exemplify the failure mode MCPSafe detects.
MCPSafe runs this check — and every other rule in the catalog — on any MCP server you paste in.
Scan now