Never use eval(), new Function(), or vm.runInScript() with user-provided input. These functions execute arbitrary code with the full privileges of your Node.js process — an attacker can read files, access databases, or take over the entire server.
Why This Matters
eval() and new Function() execute arbitrary JavaScript with the same permissions as your Node.js process. An attacker who controls the input to these functions can read environment variables (database credentials, API keys), access the filesystem, make network requests, or spawn child processes — achieving full remote code execution.
eval(), new Function(), and vm.runInNewContext() compile and execute strings as JavaScript code. When user input reaches these functions, the attacker can execute arbitrary code with the full privileges of your Node.js process.
This is not a theoretical risk — it is the most severe vulnerability class (Remote Code Execution / RCE). An attacker who achieves RCE can:
Read process.env to steal database credentials, API keys, and secrets
Read and write files on the filesystem
Make network requests to internal services
Spawn child processes to install backdoors or exfiltrate data
Pivot to other systems in the network
Even vm.runInNewContext() is not a sandbox — it can be escaped to access the host process.
The rule
Never use eval(), new Function(), vm.runInNewContext(), or setTimeout/setInterval with string arguments. If you need to parse structured data, use JSON.parse(). If you need dynamic behavior, use a lookup table or strategy pattern.
Bad example
// eval with user input — full RCEapp.post("/calculate", (req, res) => { const result = eval(req.body.expression); // Attacker sends: process.env.DATABASE_URL res.json({ result });});// new Function with user input — same riskconst fn = new Function("return " + req.body.formula);// setTimeout with string — evaluates as codesetTimeout("alert(' + userInput + ')", 1000);
Good example
// Safe math evaluation with a parser libraryimport { evaluate } from "mathjs";app.post("/calculate", (req, res) => { const result = evaluate(req.body.expression); // Only evaluates math res.json({ result });});// Strategy pattern instead of dynamic codeconst operations: Record<string, (a: number, b: number) => number> = { add: (a, b) => a + b, subtract: (a, b) => a - b, multiply: (a, b) => a * b,};app.post("/calculate", (req, res) => { const { operation, a, b } = req.body; const fn = operations[operation]; if (!fn) return res.status(400).json({ error: "Unknown operation" }); res.json({ result: fn(a, b) });});