Research
Security News
Kill Switch Hidden in npm Packages Typosquatting Chalk and Chokidar
Socket researchers found several malicious npm packages typosquatting Chalk and Chokidar, targeting Node.js developers with kill switches and data theft.
Heavily based on expression-eval and jsep, with thanks to their awesome work.
Forked from expression-eval v5.0.0. Many thanks to @donmccurdy for the initial package
JavaScript expression parsing and evaluation.
IMPORTANT: As mentioned under Security below, this library does not attempt to provide a secure sandbox for evaluation. Evaluation involving user inputs (expressions or values) may lead to unsafe behavior. If your project requires a secure sandbox, consider alternatives such as vm2.
Evaluates an estree expression from jsep
(as well as @babel/parser, esprima,
acorn, or any other library that parses and returns a valid estree
expression).
Install:
npm install --save jse-eval
Import:
// ES6
import { parse, evaluate, compile, jsep } from 'jse-eval';
// CommonJS
const { parse, evaluate, compile, jsep } = require('jse-eval');
// UMD / standalone script
const { parse, evaluate, compile, jsep } = window.jseEval;
import { parse } from 'jse-eval';
const ast = parse('1 + foo');
The result of the parse is an AST (abstract syntax tree), like:
{
"type": "BinaryExpression",
"operator": "+",
"left": {
"type": "Literal",
"value": 1,
"raw": "1"
},
"right": {
"type": "Identifier",
"name": "foo"
}
}
Evaluation executes the AST using the given context (eval(ast, context)
. By default, the context is empty.
import { parse, evaluate } from 'jse-eval';
const ast = parse('a + b / c'); // abstract syntax tree (AST)
const value = eval(ast, {a: 2, b: 2, c: 5}); // 2.4
// alternatively:
const value = await evalAsync(ast, {a: 2, b: 2, c: 5}); // 2.4
Since the default context is empty, it prevents using built-in JS functions.
To allow those functions, they can be added to the context
argument passed into the eval
method:
const context = {
Date,
Array,
Object,
encodeURI,
decodeURI,
isFinite,
isNaN,
JSON,
Math,
parseFloat,
parseInt,
RegExp,
// ...myCustomPropertiesAndFunctions,
};
import { compile } from 'jse-eval';
const fn = compile('foo.bar + 10');
fn({foo: {bar: 'baz'}}); // 'baz10'
// alternatively:
import { compileAsync } from 'jse-eval';
const fn = compileAsync('foo.bar + 10');
fn({foo: {bar: 'baz'}}); // 'baz10'
import { evalExpr } from 'jse-eval';
evalExpr('foo.bar + 10', {foo: {bar: 'baz'}}); // baz10
// alternatively:
import { evalExprAsync } from 'jse-eval';
evalExprAsync('foo.bar + 10', {foo: {bar: 'baz'}}); // baz10
import { registerPlugin } from 'jse-eval';
registerPlugin(
require('@jsep-plugin/arrow'),
require('@jsep-plugin/assignment'),
require('@jsep-plugin/async-await'),
require('@jsep-plugin/new'),
require('@jsep-plugin/object'),
require('@jsep-plugin/regex'),
require('@jsep-plugin/spread'),
require('@jsep-plugin/template'),
require('@jsep-plugin/ternary')
);
// or alternatively:
const { jsep } = require('jse-eval');
jsep.plugins.register(
require('@jsep-plugin/arrow'),
require('@jsep-plugin/assignment'),
// ...
);
To modify the evaluation, use any of the modification methods:
addUnaryOp(operator, evaluator)
. Will add the operator to jsep, and the function to evaluate the operatoraddBinaryOp(operator, precedence | evaluator, evaluator)
. Will add the operator to jsep at the given
precedence (if provided), and the function to evaluate the operatoraddEvaluator(nodeType, evaluator)
. Will add the evaluator function to the map of functions
for each node type. This evaluator will be called with the ExpressionEval instance bound to it.
The evaluator is responsible for handling both sync and async, as needed, but can use the this.isAsync
or this.evalSyncAsync()
to help.
CallExpression
and ArrowFunctionExpression
to throw an errordefault
node type handler before
throwing an error for an unknown node type. If any other behavior is desired, this can be overridden
by providing a new default
evaluator.Extensions may also be added as plugins using the registerPlugin(myPlugin1, myPlugin2...)
method.
The plugins are extensions of the JSEP format. If the init
method is defined in the plugin,
then the plugin will be added to JSEP, and/or if the initEval
method is defined in the plugin,
then the initEval
method will be called with the JseEval class as both this
and as an argument
so the plugin code may extend as necessary.
import * as expr from 'jse-eval';
expr.addBinaryOp('**', 11, true, (a, b) => a ** b);
console.log(expr.evalExpr('2 ** 3 ** 2')); // 512
expr.addBinaryOp('^', 11, (a, b) => Math.pow(a, b)); // Replace XOR with Exponent
console.log(expr.evalExpr('3^2')); // 9
expr.addEvaluator('TestNodeType', function(node) {
return node.test + this.context.string
});
console.log(expr.eval({ type: 'TestNodeType', test: 'testing ' }, { string: 'jse-eval' })); // 'testing jse-eval'
// override default implementation:
expr.addEvaluator('Identifier', function myIdentifier(node: Identifier) {
return context?.[node.name];
});
console.log(expr.eval({ type: 'Identifier', name: 'x' }, { x: 'jse-eval' })); // 'jse-eval'
const myPlugin = {
name: 'Exponentiation',
init(jsep) {
// if only adding to jsep. Otherwise it's redundant with initEval
jsep.addBinaryOp('**', 11, true);
},
initEval(JseEval) {
JseEval.addBinaryOp('**', (a, b) => a ** b);
},
};
expr.registerPlugin(myPlugin);
console.log(expr.evalExpr('2 ** 3 ** 2')); // 512
This project will try to stay current with all JSEP's node types::
ArrayExpression
LogicalExpression
/BinaryExpression
CallExpression
potentially unsafeConditionalExpression
Compound
Compound support will evaluate each expression and return the result of the final oneIdentifier
Literal
MemberExpression
ThisExpression
UnaryExpression
As well as the optional plugin node types:
ArrowFunctionExpression
potentially unsafeAssignmentExpression
/UpdateExpression
AwaitExpression
NewExpression
ObjectExpression
SpreadElement
TaggedTemplateExpression
/TemplateLiteral
Depending on your specific use-case, there are other related packages available, including:
Although this package does avoid the use of eval()
, it cannot guarantee that user-provided expressions, or user-provided inputs to evaluation, will not modify the state or behavior of your application. This library does not attempt to provide a secure sandbox for evaluation. Evaluation of arbitrary user inputs (expressions or values) may lead to unsafe behavior. If your project requires a secure sandbox, consider alternatives such as vm2.
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on the guidelines for contributing and then feel free to submit a PR with your contribution.
Help us keep this project open and inclusive. Please read and follow the Code of Conduct.
MIT License.
FAQs
JavaScript expression parsing and evaluation.
The npm package jse-eval receives a total of 1,461 weekly downloads. As such, jse-eval popularity was classified as popular.
We found that jse-eval demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers found several malicious npm packages typosquatting Chalk and Chokidar, targeting Node.js developers with kill switches and data theft.
Security News
pnpm 10 blocks lifecycle scripts by default to improve security, addressing supply chain attack risks but sparking debate over compatibility and workflow changes.
Product
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.