This document lists breaking changes in the library to help users migrate between versions.
null is no longer silently casted to 0. This means that from version 6 onwards, null == 0 will no longer be true and null == someVariable with someVariable having a null value will become true. (This was not the case before.)
Background: This change addresses critical security vulnerabilities:
- CVE-2025-12735 - Code injection via arbitrary function calls
- CVE-2025-13204 - Prototype pollution via
__proto__,prototype,constructoraccess - silentmatt/expr-eval#289 - Member function call bypass
What Changed: Functions can no longer be passed directly via the evaluation context. All functions that need to be called from expressions must be explicitly registered in parser.functions.
Before (Vulnerable):
const parser = new Parser();
// This pattern is NO LONGER ALLOWED
parser.evaluate('customFunc()', { customFunc: () => 'result' });
// This also NO LONGER WORKS
parser.evaluate('obj.method()', {
obj: {
method: () => 'dangerous'
}
});After (Secure):
const parser = new Parser();
// Register functions explicitly
parser.functions.customFunc = () => 'result';
parser.evaluate('customFunc()');
// For methods on objects, register them as top-level functions
parser.functions.objMethod = () => 'safe';
parser.evaluate('objMethod()');What Still Works:
- Passing primitive values (strings, numbers, booleans) via context
- Passing arrays and objects with non-function properties via context
- Using built-in Math functions (sin, cos, sqrt, etc.)
- Using inline-defined functions in expressions:
(f(x) = x * 2)(5) - Using functions registered in
parser.functions
Migration Guide:
-
Identify function usage: Search your codebase for patterns like
evaluate('...', { fn: ... })wherefnis a function. -
Register functions before evaluation:
// Before parser.evaluate('calculate(x)', { calculate: myFunc, x: 5 }); // After parser.functions.calculate = myFunc; parser.evaluate('calculate(x)', { x: 5 });
-
For dynamic functions: If you need to register functions dynamically:
const parser = new Parser(); parser.functions.dynamicFn = createDynamicFunction(); const result = parser.evaluate('dynamicFn()'); delete parser.functions.dynamicFn; // Clean up if needed
Access to the following properties is now blocked to prevent prototype pollution attacks:
__proto__prototypeconstructor
Attempting to access these properties in variable names or member expressions will throw an AccessError.
Example:
// These will throw AccessError
parser.evaluate('x.__proto__', { x: {} });
parser.evaluate('__proto__', { __proto__: {} });What Changed: The || operator was repurposed for logical OR (JavaScript-style). A new | (pipe) operator was introduced for array and string concatenation. Additionally, the && operator was added for logical AND.
Before (original expr-eval 2.x):
// || was used for concatenation
parser.evaluate('"hello" || " world"'); // "hello world"
parser.evaluate('[1, 2] || [3, 4]'); // [1, 2, 3, 4]After (v4.0.0+):
// | is now used for concatenation
parser.evaluate('"hello" | " world"'); // "hello world"
parser.evaluate('[1, 2] | [3, 4]'); // [1, 2, 3, 4]
// || is now logical OR
parser.evaluate('true || false'); // true
parser.evaluate('false || true'); // true
// && is logical AND (new)
parser.evaluate('true && false'); // false
parser.evaluate('true && true'); // trueMigration Guide:
- Find concatenation usage: Search your expressions for
||used with strings or arrays - Replace with pipe: Change
||to|for concatenation operations - Review logical operations: If you were using
orkeyword, you can now also use||
The package was renamed from expr-eval to @pro-fa/expr-eval and ported to TypeScript.
# Remove old package
npm uninstall expr-eval
# Install new package
npm install @pro-fa/expr-evalUpdate imports:
// Before
const { Parser } = require('expr-eval');
// After
import { Parser } from '@pro-fa/expr-eval';