Safely Evaluate ESTree Expressions
eval-estree-expression is a JavaScript library designed for the safe, synchronous, and asynchronous evaluation of ESTree-compliant Abstract Syntax Trees (ASTs). It is currently at version 3.0.1, with development active and a 4.0.0-beta release available, indicating a steady release cadence. This package differentiates itself by focusing specifically on expressions, avoiding the inherent dangers of direct `eval()` usage by operating on ASTs from parsers like `@babel/parser`, `esprima`, or `acorn`. It provides a controlled environment, requiring explicit context for variables and offering options to enable potentially unsafe features like arbitrary function calls with caution. The library strictly operates on Node.js version 14 or greater and does not support JavaScript statements or assignment operators by default, ensuring a higher degree of security when evaluating untrusted expressions compared to general-purpose JavaScript evaluators. Its design choice to work with ASTs makes it a robust alternative to libraries like `expr-eval` which have faced critical remote code execution vulnerabilities due to insufficient validation of evaluation contexts.
Common errors
-
ReferenceError: 'myVariable' is not defined
cause An expression attempts to access a variable or property ('myVariable') that is not present in the provided `context` object.fixEnsure all variables referenced in the expression are explicitly provided as properties in the `context` object passed to `evaluate` or `evaluate.sync`. -
SyntaxError: Assign expression is not supported
cause The input AST (from the parsed expression) contains an assignment operation (e.g., `x = 10`) or a statement not supported by the evaluator.fixThe library is designed for pure expressions. Do not attempt to evaluate JavaScript statements or expressions that modify state. Remove assignment operators or convert them to pure expressions where possible. -
TypeError: 'myFunction' is not a function
cause An expression attempts to call a function (`myFunction`) when the `functions` option is not explicitly enabled, or a function expression/statement is present without the `generate` option.fixIf you intend to allow function calls, enable the `functions: true` option in the `evaluate` options. If you need to evaluate full function expressions or statements, use `generate: true` (with extreme caution due to security implications).
Warnings
- breaking Version 3.x and later of `eval-estree-expression` requires Node.js version 14 or greater. This may break existing applications running on older Node.js runtimes.
- gotcha Enabling the `functions: true` or `generate: true` options can introduce security vulnerabilities by allowing arbitrary function calls or function expressions/statements to be evaluated. This can lead to remote code execution if expressions or contexts originate from untrusted user input.
- gotcha The library is designed for expressions and does not support JavaScript assignment operators (e.g., `=`, `+=`, `--`) or general statements by default. Attempting to evaluate code with these constructs will result in an error.
- gotcha While `eval-estree-expression` avoids direct `eval()`, it does not inherently provide a secure sandbox against all forms of malicious code execution. If user-controlled data is passed in the `context` object, especially if it contains objects with malicious getters or methods, it could potentially be exploited.
Install
-
npm install eval-estree-expression -
yarn add eval-estree-expression -
pnpm add eval-estree-expression
Imports
- evaluate
const { evaluate } = require('eval-estree-expression');import { evaluate } from 'eval-estree-expression'; - evaluate.sync
import { evaluate } from 'eval-estree-expression'; const result = evaluate.sync(ast, context); - parseExpression (from @babel/parser)
import { parse } from '@babel/parser'; // parseExpression is typically more suitable for expressions than general `parse`import { parseExpression } from '@babel/parser';
Quickstart
import { evaluate } from 'eval-estree-expression';
import { parseExpression } from '@babel/parser';
async function runEvaluation() {
const expressionString = 'user.age > 18 && user.status === "active" ? "Eligible" : "Not Eligible"';
const context = {
user: {
name: 'Alice',
age: 25,
status: 'active'
}
};
try {
// Parse the expression string into an ESTree AST
const ast = parseExpression(expressionString, {
sourceType: 'script', // or 'module'
plugins: ['estree'] // Ensure Babel outputs ESTree-compatible AST
});
// Synchronous evaluation
const syncResult = evaluate.sync(ast, context);
console.log('Synchronous result:', syncResult); // Expected: Eligible
// Asynchronous evaluation (returns a Promise)
const asyncResult = await evaluate(ast, { ...context, functions: true }); // functions option enabled for example
console.log('Asynchronous result:', asyncResult); // Expected: Eligible
// Example with a different context
const anotherContext = {
user: {
name: 'Bob',
age: 16,
status: 'inactive'
}
};
const syncResult2 = evaluate.sync(ast, anotherContext);
console.log('Synchronous result (Bob):', syncResult2); // Expected: Not Eligible
} catch (error) {
console.error('Evaluation error:', error.message);
}
}
runEvaluation();