CVE-2025-12735 - expr-eval context function RCE

expr-eval is commonly used as the “safe math parser” replacement for JavaScript eval(). CVE-2025-12735 is the uncomfortable edge case: vulnerable versions can still reach attacker-influenced functions through the variables object passed to evaluate(). If an application lets untrusted users submit formula strings while the evaluator context contains callable values, object members, request helpers, file helpers, shell wrappers, database clients, or tenant-scoped service objects, the formula boundary can become code execution.

GitHub Advisory Database lists the original expr-eval package as affected through 2.0.2 with no patched original-package version. It lists expr-eval-fork as fixed in 3.0.1; do not stop at expr-eval-fork 3.0.0, because that line was also marked affected. NVD/CISA-ADP scores CVE-2025-12735 as Critical. NVD also tracks an adjacent original-package prototype-pollution issue as CVE-2025-13204, so a mergeable PR should treat original expr-eval replacement as the safer remediation shape even if the scanner only reported one of the two CVEs.

When to use it

  • A repository installs, vendors, bundles, or documents expr-eval or expr-eval-fork.
  • User-controlled formulas, rules, calculations, dashboards, reports, workflow conditions, or templates reach evaluate() with a variables/context object.
  • Evaluator context can contain functions, service clients, tenant objects, request objects, filesystem/process helpers, SDKs, loggers, or secrets.
  • You need a bounded PR or triage note that replaces the vulnerable evaluator and reduces context objects to explicit inert data.

Inputs

  • Package manifests, lockfiles, vendored bundles, Dockerfiles, generated dependency reports, SBOMs, evaluator imports, wrappers, and formula features.
  • Expression ownership, server/browser reachability, variable-map construction, allowed formula identifiers, service/helper exposure, and logging behavior.
  • Available package install, evaluator regression tests, lint/typecheck, build, container build, SBOM, and dependency/security scan commands.

Affected versions

  • Vulnerable: expr-eval <= 2.0.2.
  • No fixed original-package version: GitHub Advisory Database currently lists no patched version for expr-eval.
  • Vulnerable fork line: expr-eval-fork <= 3.0.0.
  • Fixed fork line: expr-eval-fork 3.0.1+.
  • Affected code shape: user-controlled expression strings are evaluated with a variables/context object that can contain functions, object members, privileged service clients, filesystem or process helpers, request helpers, tenant data, secrets, or other non-data objects.
  • Fixed code shape: the expression evaluator is patched or replaced, and the evaluation context is reduced to explicit inert primitives or reviewed pure functions required for the formula use case.

Indicator-of-exposure

  • The repository installs, vendors, bundles, or documents expr-eval or expr-eval-fork.
  • Formula, calculator, workflow-rule, pricing, report, analytics, low-code, dashboard, feature-flag, policy, alerting, template, or user-defined field features pass user-editable strings to Parser.parse(...).evaluate(...), parser.evaluate(...), or Expression.evaluate(...).
  • The variables object is built from request bodies, database records, tenant configuration, workflow context, plugin state, object spreads, or service containers instead of a minimal literal map.
  • The variables object contains functions, class instances, object members, proxies, methods, module exports, fs, process, child_process, network clients, database clients, queue clients, logger objects, SDK clients, or secrets.
  • Server-side use is reachable by anonymous, authenticated, cross-tenant, customer, marketplace, webhook, or integration input.

Quick checks:

rg -n "expr-eval|expr-eval-fork|new Parser\\(|Parser\\.parse|\\.evaluate\\(|parser\\.evaluate|Expression\\.evaluate" .
npm ls expr-eval expr-eval-fork
pnpm why expr-eval expr-eval-fork
yarn why expr-eval expr-eval-fork

Windows:

rg -n "expr-eval|expr-eval-fork|new Parser\(|Parser\.parse|\.evaluate\(|parser\.evaluate|Expression\.evaluate" .
npm ls expr-eval expr-eval-fork
pnpm why expr-eval expr-eval-fork
yarn why expr-eval expr-eval-fork

Do not validate exposure by running formulas that spawn processes, write files, read secrets, issue network requests, or touch production data.

Remediation strategy

  • Remove the original expr-eval package from controlled manifests, lockfiles, vendored bundles, generated dependency reports, SBOMs, container layers, and examples. Replace it with expr-eval-fork 3.0.1+ only when the fork’s API and breaking changes are compatible with the application.
  • If the application only needs a small formula language, consider replacing the dependency with a purpose-built parser or typed rule interpreter instead of carrying a general evaluator.
  • Do not rely on a package bump alone. Patch evaluator call sites so the variables object is built from an explicit allowlist of primitive values and reviewed pure functions. Do not pass request objects, service containers, SDK clients, module exports, ORM models, loggers, filesystem helpers, process helpers, or tenant context objects into evaluate().
  • Reject or normalize formula identifiers before evaluation. Block member-path access to dangerous names such as constructor, prototype, __proto__, process, globalThis, require, module, exports, and any application helper that performs I/O or state changes.
  • Add a compatibility layer if renaming expr-eval imports to expr-eval-fork would otherwise cause a broad application diff. The compatibility layer should expose only the parser features this product uses.
  • Add safe regression tests using local fixtures: a legitimate formula still evaluates, direct calls to non-allowlisted functions fail, nested member calls fail, prototype-pollution-style property names fail, and no test creates files, launches commands, opens sockets, or reads real secrets.
  • If an exposed server-side formula surface evaluated untrusted input while the process held secrets or tenant data, patch first, then review logs and rotate reachable credentials according to the service incident policy.

The prompt

Model context: this prompt was generated by GPT 5.5 Extra High reasoning.

You are remediating CVE-2025-12735 / GHSA-jc85-fpwf-qm7x, the critical
`expr-eval` context-function code-injection issue. Also check the adjacent
original-package prototype-pollution CVE-2025-13204 when `expr-eval` is
present. Produce exactly one output:

- A reviewer-ready PR/change request that replaces vulnerable evaluator
  versions, narrows all evaluator contexts to inert allowlisted data, adds safe
  regression tests, refreshes generated artifacts, and documents operator
  follow-up, or
- TRIAGE.md if this repository does not control an affected package, lockfile,
  formula feature, runtime evaluator call site, generated artifact, or
  deployment surface.

## Rules

- Scope only CVE-2025-12735, directly related `expr-eval` or
  `expr-eval-fork` remediation, adjacent CVE-2025-13204 exposure when the
  original package is present, evaluator context hardening, and generated
  artifact refreshes.
- Treat request bodies, formulas, variables objects, tenant config, workflow
  state, service clients, filesystem helpers, shell helpers, SDK clients,
  credentials, secrets, logs, and evaluator errors as sensitive.
- Do not generate, run, preserve, publish, or link live RCE payloads.
- Do not prove exposure by spawning processes, writing files, reading local
  files, opening sockets, making outbound requests, or touching production
  data.
- Do not replace a vulnerable evaluator with JavaScript `eval()`, `new
  Function()`, Node `vm`, or a broader template engine.
- Do not auto-merge.

## Steps

1. Inventory every JavaScript/TypeScript workspace, package manifest, lockfile,
   vendored bundle, generated dependency report, SBOM, Dockerfile,
   devcontainer, and deployment artifact controlled by this repository.
2. Find every reference to `expr-eval`, `expr-eval-fork`, `new Parser(...)`,
   `Parser.parse(...)`, `parser.evaluate(...)`, and
   `Expression.evaluate(...)`.
3. Resolve actual installed versions from lockfiles and package-manager
   output. Mark a target vulnerable when it resolves `expr-eval <= 2.0.2`,
   any original `expr-eval` line without a reviewed downstream patch, or
   `expr-eval-fork <= 3.0.0`.
4. For each evaluator call site, determine:
   - who controls the expression string;
   - whether the surface is server-side, browser-only, internal, customer
     reachable, cross-tenant, webhook reachable, or marketplace/integration
     reachable;
   - how the variables object is built;
   - whether the variables object can contain functions, class instances,
     object members, service clients, filesystem/process helpers, request
     helpers, database clients, logger objects, SDK clients, tenant data, or
     secrets.
5. If the repository only contains unrelated prose or an externally owned
   formula service, stop with `TRIAGE.md` listing checked files, owner,
   observed versions, required fixed state, and any operator action.
6. Replace vulnerable packages:
   - remove original `expr-eval` unless this repository carries and verifies a
     reviewed downstream patch;
   - upgrade `expr-eval-fork` to `3.0.1+`;
   - update imports, compatibility wrappers, lockfiles, SBOMs, generated
     dependency reports, container layers, and docs.
7. Harden every evaluator context:
   - build a fresh null-prototype or plain literal object from explicit
     allowlisted formula variables;
   - copy only primitive values and reviewed pure functions required by the
     formula feature;
   - block inherited properties, prototype mutation names, constructor access,
     member traversal to application helpers, and object spreads from request
     or tenant context objects;
   - never pass service containers, request objects, ORM models, SDK clients,
     module exports, filesystem helpers, process helpers, loggers, or secrets
     into `evaluate()`.
8. Add safe regression tests:
   - legitimate formulas still work;
   - direct non-allowlisted function calls are rejected;
   - nested member calls to context object methods are rejected;
   - `constructor`, `prototype`, and `__proto__` style identifiers are
     rejected or treated as data;
   - evaluator errors do not log secrets, full request bodies, tenant data, or
     dangerous expression strings;
   - tests prove no process spawn, file write, file read, or network request
     happens before rejection, using spies or mocks rather than real effects.
9. Add a PR body section named `CVE-2025-12735 operator actions` that states:
   - package versions before and after;
   - whether original `expr-eval` was removed or a reviewed downstream patch
     was carried;
   - which formula/evaluator surfaces were customer reachable or server-side;
   - which context values were removed or allowlisted;
   - whether CVE-2025-13204 was also checked;
   - whether logs, secrets, tokens, or tenant credentials need review or
     rotation;
   - which validation commands passed.
10. Run available validation: package install, lockfile integrity, unit tests,
    evaluator regression tests, dependency/security scans, lint/typecheck,
    build, container build, SBOM refresh, generated docs checks, and a
    non-secret local smoke test.
11. Use PR title:
    `fix(sec): remediate CVE-2025-12735 in expr-eval`.

## Stop conditions

- No controlled manifest, lockfile, generated artifact, or runtime evaluator
  call site uses `expr-eval` or `expr-eval-fork`.
- The repository only invokes a formula service owned by another team or SaaS
  provider.
- A safe replacement would require a broad product migration outside the CVE
  remediation scope.
- The fixed fork's breaking changes cannot be adopted without product owner
  decisions; document containment and owner deadline in `TRIAGE.md`.
- Safe verification would require live RCE, real secrets, production formulas,
  customer data, production logs, or external network callbacks.
- Validation fails for unrelated pre-existing reasons; document those failures
  instead of broadening scope.

Verification - what the reviewer looks for

  • No controlled manifest, lockfile, SBOM, generated dependency report, container layer, or deployment artifact resolves original expr-eval without a reviewed patch, or expr-eval-fork <= 3.0.0.
  • Every evaluator call site builds its context from explicit allowlisted data instead of passing request, service, SDK, filesystem, process, or tenant objects through to evaluate().
  • Regression tests prove direct function calls, nested member calls, and prototype-style property names are rejected without real process, filesystem, network, or secret access.
  • The PR distinguishes package presence from reachable evaluator exposure.
  • Operator notes cover server-side/customer-reachable formula surfaces, logs, secrets, tenant data, CVE-2025-13204 adjacency, and any credential rotation decision.

Output contract

  • Reviewer-ready PR removing vulnerable expr-eval/expr-eval-fork versions or replacing them with a reviewed safe parser path and refreshed artifacts.
  • Evaluator contexts rebuilt from explicit allowlisted primitives or reviewed pure functions, with request/service/SDK/filesystem/process objects excluded.
  • Safe regression tests for non-allowlisted function calls, nested member calls, prototype-style identifiers, legitimate formulas, and secret-safe errors without real process, file, network, or secret access.
  • TRIAGE.md when package, formula runtime, or migration ownership is outside this repository.

Watch for

  • Upgrading to expr-eval-fork 3.0.0 instead of 3.0.1+; the advisory marks 3.0.0 as affected.
  • Editing package.json while a lockfile, generated dependency report, SBOM, Docker layer, or vendored bundle still resolves the vulnerable package.
  • Passing a sanitized expression to a still-dangerous variables object.
  • Treating browser-only calculator widgets the same as server-side customer-reachable formula engines; exposure and operator work differ.
  • Replacing expr-eval with eval, new Function, Node vm, or a template compiler that is easier to exploit.
  • Logging rejected formulas, evaluator errors, variable maps, request bodies, tenant data, secrets, or generated credentials during incident review.

References