expr-eval CVE-2025-12735 · CVE-2025-13204
Code execution and prototype pollution via unrestricted member access in expr-eval.
The problem
expr-eval <= 2.0.2 (all published versions) is affected by
CVE-2025-12735, CVE-2025-13204 (CWE-94 Code Injection / CWE-1321 Prototype Pollution). The published package has
no fix available — exactly what npm audit reports. `expr-eval` lets callers evaluate string expressions against a supplied scope, including member access such as `foo.bar`. Because member access was unrestricted, a crafted expression could reach JavaScript internals: `foo.__proto__` exposes (and can pollute) `Object.prototype`, and the `constructor` chain can reach the `Function` constructor to run arbitrary code. Any application that evaluates untrusted expressions is affected.
The fix — drop-in, no code changes
Add an overrides entry so every direct and transitive dependency on
expr-eval resolves to the patched fork, then reinstall:
{
"overrides": {
"expr-eval": "npm:@keep-lts/expr-eval@^2.0.3"
}
}
Equivalent for Yarn: use resolutions. The public API is unchanged — nothing else to do.
✓ Live on npm: @keep-lts/expr-eval · or install directly: npm i @keep-lts/expr-eval
This is rarely the only one
If expr-eval turned up in your npm audit, there are very likely other abandoned dependencies in the same tree whose CVEs also have no fix on npm. Send us your npm audit output — or your Snyk / Dependabot list — and we'll tell you, for free, which of those findings we already fix. No call, no obligation.
Mid-audit and need a vendor on the hook? Keep LTS Pro adds an SLA on future CVEs and the SBOM / VEX artefacts your auditor will accept — all async, no calls.
What we changed
A `memberAccess(object, property)` guard wraps every runtime member-access site and throws when the property is `__proto__`, `constructor`, or `prototype`. Legitimate expressions — member access to ordinary properties, arithmetic, and built-in functions like `sqrt`/`max` — are unchanged. The same guard is present in `dist/bundle.js` (CommonJS), `dist/index.mjs` (ESM), and `dist/bundle.min.js` (regenerated from the patched source).
Proof of concept (the vulnerability)
const { Parser } = require('expr-eval');
const parser = new Parser();
parser.evaluate('foo.__proto__', { foo: {} }); // returns Object.prototype on the unpatched version
@keep-lts/expr-eval@2.0.3How we keep it trustworthy
- Minimal, surgical patch — security only, no feature changes.
- A regression test that fails on the original and passes on the patch; legitimate behaviour preserved.
- Published under the
@keep-ltsorg with the full advisory and tests inside the package; upstream license & attribution retained.
Full advisory and changelog ship inside the package (SECURITY.md, CHANGELOG.md).