← All Articles 🔒 Security Vulnerabilities Critical By Nishant Kumar Verma Updated

eval() and new Function() in JavaScript: Why AI Code Uses Them Wrong

9 of 113 JavaScript projects we scanned still used eval() or new Function(). Why dynamic code execution is dangerous and the safer alternatives for each use.

SEC-008

Quick Answer

eval() and new Function() run a string as JavaScript at runtime, so any user input that reaches them becomes code running with your app's privileges. In VibeDoctor's scans, 9 of 113 JavaScript and TypeScript projects (8%) still had one of them at their latest scan, and new Function() was more common than eval(). In almost every case there is a safer tool: JSON.parse, a math expression library, a lookup table, or a real sandbox.

What We See in Real Projects

These numbers come from VibeDoctor's scans of the 336 projects connected to an account, as of each project's most recent scan (May to October 2026). The language split uses the 127 projects with a file-by-file language breakdown. No project or owner is identified.

At the latest scan Projects
Any dynamic code execution 11 of 336 (3.3%)
JavaScript or TypeScript projects with eval() or new Function() 9 of 113 (8%)
...with new Function() 7
...with eval() 4
...passing request input into new Function() 1
Python projects with eval() or exec() 3 of 49 (6%)

Only one of the seven projects with new Function() fed request input into it. In the other six, our scanners found no request input flowing in. Those still deserve a look, because data from a database, a config file or an API response can be attacker-controlled too, but it is a different level of urgency. VibeDoctor grades these findings by that evidence (see below).

What Makes eval() So Dangerous?

eval() takes a string and runs it as JavaScript in the current scope, with access to your variables, the DOM, the network and, on the server, the Node.js process. There is no sandbox and no permission check. Whatever string reaches it runs with your app's full privileges.

Code injection is part of the Injection category in the OWASP Top 10. Dynamic code execution is one of the harder variants to spot in review, because it does not look like SQL injection or XSS. It looks like a feature.

How AI Tools Reach for eval()

The typical scenario: a founder asks Bolt or Cursor to "let users enter a custom formula" or "evaluate a config expression". The quickest working demo uses eval().

// BAD - eval() with user input
app.post('/api/calculate', (req, res) => {
  const { formula } = req.body;
  const result = eval(formula);
  res.json({ result });
});

// BAD - new Function() with a user-controlled string
function buildFilter(userExpression) {
  const fn = new Function('item', `return ${userExpression}`);
  return items.filter(fn);
}

// BAD - setTimeout with a string argument is eval too
setTimeout(`updateDashboard('${userId}')`, 1000);

All three give an attacker a code execution path. Sending formula=require('child_process').execSync('cat /etc/passwd').toString() to the first endpoint runs a shell command on your server. The second lets an attacker write a "filter" that calls fetch() to send your data elsewhere. The third is an XSS vector if userId comes from user input.

The Full Family of Dynamic Execution Functions

Function Risk Common misuse pattern
eval(str) Critical Formula evaluation, JSON parsing fallback
new Function(str) Critical Dynamic filter and sort callbacks, template functions
setTimeout(str, ms) High Delayed callbacks with interpolated values
setInterval(str, ms) High Polling with dynamic function names
document.write(str) High Dynamic HTML injection (also an XSS risk)

Safe Alternatives for Every Use Case

// GOOD - math expressions: use a maintained parser library (mathjs 15.2.0 or later)
import { evaluate } from 'mathjs';

app.post('/api/calculate', (req, res) => {
  const { formula } = req.body;
  try {
    const result = evaluate(formula); // "2 + 3 * sin(pi/2)"
    res.json({ result });
  } catch (err) {
    res.status(400).json({ error: 'Invalid formula' });
  }
});

// GOOD - JSON: JSON.parse, never eval('(' + input + ')')
const data = JSON.parse(userInput);

// GOOD - dynamic property access: an allowlist and bracket notation
const allowedFields = ['name', 'email', 'age'];
const key = req.query.field;
if (allowedFields.includes(key)) {
  const value = user[key];
}

// GOOD - dynamic behaviour: a lookup table of real functions
const sorters = {
  name: (a, b) => a.name.localeCompare(b.name),
  date: (a, b) => new Date(a.date) - new Date(b.date),
  price: (a, b) => a.price - b.price,
};
items.sort(sorters[req.query.sort] ?? sorters.name);

The principle: define behaviour in code, not in strings. If users choose between behaviours, give them a fixed set of options and map each one to a real function.

When You Really Must Run User Code: Sandboxing

Plugin systems, no-code builders and user scripting are legitimate cases for running code someone else wrote. The answer there is isolation, not eval().

isolated-vm: runs code in a separate V8 isolate with no access to your Node.js process. Avoid vm2, which has had sandbox-escape CVEs and is no longer maintained, and do not treat Node's built-in vm module as a security boundary.

Web Workers: in the browser, a worker runs on a separate thread without DOM access, so code you pass to it cannot touch your page directly.

Deno: if you are building a runtime for user scripts, Deno's permission flags deny file system, network and environment access unless you grant them.

Even inside a sandbox, validate inputs, rate-limit execution, and set CPU and memory limits. A sandbox contains damage; it does not replace input validation.

How to Find eval() in Your Codebase

Search your project for eval(, new Function(, setTimeout(" and setInterval(". Any hit whose argument includes a variable, template literal or concatenation needs a look. Pay most attention where the value comes from req.body, req.query, req.params or any other user-facing input.

VibeDoctor's Vibe Check flags eval() and new Function() in JavaScript and TypeScript, and eval() and exec() in Python, with the file and line. eval() is reported as high. new Function() is medium unless request input reaches it, which raises it to high, and one built only from fixed strings is not reported. Free to sign up.

A Content Security Policy helps at runtime. With script-src 'self' and no 'unsafe-eval', browsers block eval() and new Function(), and every blocked call shows up as a CSP violation in the console, which also tells you where they are.

FAQ

Is eval() ever safe to use?

Only when the string is entirely under your control: never user input, environment variables, database values or network responses. That covers very few real cases, and even then a lookup table or a plain conditional is usually easier to maintain.

Is new Function() safer than eval()?

Slightly. new Function() runs in the global scope, so it cannot see your local variables. It still runs arbitrary code with access to globals, DOM APIs and Node.js built-ins, so with user input it is just as dangerous.

Do linters catch eval()?

Yes, if you turn the rules on. ESLint has no-eval, no-implied-eval (string arguments to setTimeout and setInterval) and no-new-func; add them to your config explicitly. Biome reports global eval() with lint/security/noGlobalEval, and eslint-plugin-security adds detect-eval-with-expression.

What about template engines that use eval internally?

Engines such as EJS and Handlebars compile templates into functions. The risk depends on whether user input reaches the template source. Use the engine's escaping, and never pass raw user input as a template.

How do I safely evaluate math formulas from users?

Use a maintained math expression library such as mathjs, version 15.2.0 or later, which fixes two high-severity advisories published in April 2026. It parses the expression into its own syntax tree and evaluates math, so input like require('child_process') fails instead of running. Avoid expr-eval: it has not been updated since 2022 and has an unpatched critical code-execution advisory, CVE-2026-12866.

Diagnose your codebase - free

VibeDoctor checks for SEC-008 and 148 other issues across 21 diagnostic areas - security, performance, code quality, and more.

SCAN MY APP →