VNX-94 – Code Injection
Overview
This rule flags calls to dynamic code evaluation functions — eval(), exec(), new Function(), compile(), PHP’s eval(), and Ruby’s eval()/instance_eval() — especially when user-controlled data or dynamically constructed strings are passed as the argument. Code injection allows an attacker to supply arbitrary code that is executed by the application with the full privileges of the runtime process. This maps to CWE-94: Improper Control of Generation of Code (‘Code Injection’).
Severity: Critical | CWE: CWE-94 – Code Injection
Why This Matters
Code injection is the most direct and complete form of application compromise possible: the attacker writes the code, the server runs it. Unlike SQL injection or command injection, there is no database or OS boundary — the attacker’s payload executes in the same language, the same process, and with the same permissions as the application. In JavaScript, eval(userInput) gives the attacker access to the entire Node.js environment including require('fs') and require('child_process'). In Python, eval(user_input) can call __import__('os').system('...'). The rule also flags setTimeout(stringArg) and setInterval(stringArg) in JavaScript because these functions evaluate their first argument as code when it is a string rather than a function reference.
What Gets Flagged
// FLAGGED: eval with user input
app.post('/calculate', (req, res) => {
const expr = req.body.expression;
const result = eval(expr); // attacker sends: process.mainModule.require('child_process').exec('...')
res.json({ result });
});
// FLAGGED: new Function with dynamic content
const fn = new Function('return ' + req.query.formula);
// FLAGGED: setTimeout with string argument
setTimeout("cleanup('" + userId + "')", 1000);
# FLAGGED: Python eval with input
@app.route('/calc')
def calc():
expr = request.args['expr']
result = eval(expr) # allows __import__('os').system('id')
return str(result)
# FLAGGED: Python exec — flagged regardless
exec(compile(user_code, '<string>', 'exec'))
<?php
// FLAGGED: PHP eval
$code = $_POST['code'];
eval($code); // direct code injection
<?php
// FLAGGED: preg_replace with /e modifier
preg_replace('/' . $_GET['pattern'] . '/e', $_GET['replace'], $subject);
# FLAGGED: Ruby eval
eval(params[:code])
Remediation
- Do not use eval or equivalent functions with external data. In almost all cases there is a safer alternative.
# SAFE: Use ast.literal_eval for parsing data expressions (not code)
import ast
def parse_value(s: str):
# Only parses Python literals: strings, numbers, lists, dicts, tuples, bools, None
return ast.literal_eval(s)
# SAFE: For mathematical expressions, use a dedicated expression parser
# e.g. simpleeval, numexpr, or operator trees — never eval()
// SAFE: Use Function properly — only with static, developer-controlled template strings
// Better: use a math expression library like mathjs
const { evaluate } = require('mathjs');
app.post('/calculate', (req, res) => {
try {
const result = evaluate(req.body.expression); // sandboxed math evaluation
res.json({ result });
} catch (e) {
res.status(400).json({ error: 'Invalid expression' });
}
});
// SAFE: setTimeout with a function reference, not a string
setTimeout(() => cleanup(userId), 1000);
<?php
// SAFE: Use a whitelist-based expression evaluator rather than eval
// Or restructure logic to avoid dynamic code execution entirely
// If dynamic dispatch is needed, use a dispatch table:
$allowed = ['add' => fn($a,$b) => $a + $b, 'mul' => fn($a,$b) => $a * $b];
$op = $_POST['operation'];
if (!array_key_exists($op, $allowed)) {
http_response_code(400); exit;
}
$result = $allowed[$op]($a, $b);
Replace
preg_replacewith/emodifier withpreg_replace_callback(). The/emodifier was deprecated in PHP 5.5 and removed in PHP 7.0.Use a sandboxed execution environment if dynamic code execution is a genuine requirement. Node.js has
vm.runInNewContext(), Python hasRestrictedPython, and there are purpose-built sandboxes for all major languages.Audit all existing uses of eval/exec even when the argument appears to be developer-controlled — code paths change over time and a future refactor may introduce a vulnerability.