Aug 22, 2026·6 min read·0 visits
Unauthenticated remote code execution vulnerability in JSONata via prototype lookup chain traversal.
A critical prototype pollution and sandbox escape vulnerability was discovered in the JSONata query and transformation library before versions 1.8.8 and 2.2.0. By providing a malicious JSONata expression that bypasses ownership checks on object properties, remote attackers can execute arbitrary code in the context of the host Node.js application.
JSONata is a query and transformation language modeled on XPath that interprets queries and transformations against JSON input datasets. To evaluate expressions, JSONata compiles them into an Abstract Syntax Tree (AST) and steps through the nodes using its core evaluator engine. The engine acts as an interpreter, allowing users to define local variables, query specific fields, and invoke transformation operations. This flexibility requires strict sandbox boundaries when evaluating untrusted user queries.\n\nThe attack surface lies directly in the core evaluator's property resolution bottleneck. In standard Node.js environments, applications often evaluate query strings provided by external users or integrations. If these expressions are executed without safety boundaries, an attacker can access properties outside the scope of the local evaluation context. This behavior occurs because the evaluator performs raw property lookups on user-provided and internal JavaScript objects.\n\nThe vulnerability is classified under CWE-94 (Improper Control of Generation of Code or 'Code Injection'). Specifically, the engine's failure to validate object property ownership allows remote attackers to traverse the standard JavaScript prototype chain. By retrieving the global Function constructor, an adversary can compile and execute arbitrary JavaScript code inside the host process. This bypass leads to complete compromise of the hosting system's confidentiality, integrity, and availability.
The root cause of the vulnerability exists in JSONata's internal property resolution helper function, located in src/functions.js. During query evaluation, the engine resolves property keys using a central lookup routing function. This function takes an input object and a target key, and then returns the resolved value.\n\nIn JavaScript, all standard object instances inherit helper methods and meta-properties from Object.prototype. These properties include constructor, __proto__, __lookupSetter__, and __defineGetter__. When the lookup resolver evaluates an expression, it retrieves properties by directly indexing the object, e.g., using input[key]. Before the fix, the evaluator checked whether the input was an object and not a function, but it did not verify whether the key belonged to the object as an 'own' property.\n\nBecause of this omission, if a query references an inherited property such as constructor, the engine climbs up the prototype chain. The engine then returns the native prototype function or object to the evaluator scope. An attacker can construct a payload that chains these lookups. This chain moves from a standard object to the prototype accessor, and ultimately retrieves the global Function constructor. Once retrieved, the constructor serves as an execution vector to run arbitrary shell commands with the host process privileges.
The vulnerability was resolved by introducing explicit property ownership validation in the central resolver in src/functions.js. Below is the code diff of the patch implemented in both the v1 and v2 branches:\n\njavascript\n// Vulnerable Property Lookup\n} else if (input !== null && typeof input === 'object' && !isFunction(input)) {\n result = input[key];\n}\nreturn result;\n\n// Patched Property Lookup\n} else if (input !== null && typeof input === 'object' && Object.prototype.hasOwnProperty.call(input, key) && !isFunction(input)) {\n result = input[key];\n}\nreturn result;\n\n\nThe inclusion of Object.prototype.hasOwnProperty.call(input, key) ensures that the engine only returns values defined directly on the input instance. If the key exists on the prototype chain (such as constructor), the check fails. The resolver then returns undefined instead of returning the inherited function.\n\nInvoking hasOwnProperty indirectly via Object.prototype.hasOwnProperty.call is critical for application stability. Objects created without a prototype, such as Object.create(null), do not inherit methods from Object.prototype. Checking them using input.hasOwnProperty(key) would throw a TypeError, causing the application to crash. The indirect call avoids this issue, ensuring safety across all object types.\n\nAdditionally, supplementary hardening was introduced in PR #806. All internal maps, binding contexts, and regex lookup tables were refactored to use prototype-less objects created with Object.create(null). The maintainers also replaced for...in loops, which naturally traverse prototype properties, with direct Object.keys() iterations. Finally, they blacklisted internal variable prefixes like _jsonata_ to prevent injection vectors.
Exploitation is achieved by constructing a specific JSONata query that traverses the prototype chain to execute system commands. The sequence uses JavaScript's capability to override or access setters and getters on standard objects.\n\nFirst, the attacker executes __lookupSetter__('__proto__')(constructor) inside the query. This retrieves a reference to the global Function constructor from the prototype chain. Next, the attacker invokes this constructor to compile a dynamic shell payload. The payload retrieves the Node.js child_process module to run commands:\n\njavascript\nconstructor(\"return process.getBuiltinModule('child_process').execSync('id').toString()\")\n\n\nThe compiled payload is then bound as a getter named 'l' on the prototype of standard objects using __defineGetter__('l', ...). Finally, evaluating valueOf().l triggers the getter. This executes the system command within the context of the host process, returning the command output directly to the attacker.\n\nThis attack is highly operationalizable because it requires no specialized local environment variables, user authentication, or specific target configurations. The only prerequisite is that the host application must evaluate user-controlled JSONata expressions using standard runtime contexts. The execution flow can be visualized using the following flow chart:\n\nmermaid\ngraph LR\n A[\"Malicious JSONata Expression\"] --> B[\"Evaluate Expression\"]\n B --> C[\"Call __lookupSetter__('__proto__')(constructor)\"]\n C --> D[\"Access Global Function Constructor\"]\n D --> E[\"Bind Custom Getter 'l' with child_process Payload\"]\n E --> F[\"Trigger valueOf().l\"]\n F --> G[\"Execute Shell Command via host Node.js process\"]\n
The potential impact of CVE-2026-77413 is high. Because the JSONata evaluation runs directly within the Node.js runtime process, a successful escape gives the attacker full control of the hosting application. The attacker can execute arbitrary operating system commands, read sensitive system files, access environment variables, or establish persistent backdoors.\n\nThis vulnerability has been assigned a CVSS v4.0 Base Score of 9.3 (Critical). The CVSS vector is CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N. This score reflects that no prior authentication, user interaction, or specialized network conditions are required to trigger execution.\n\nFurthermore, the blast radius is extended because JSONata is commonly deployed in low-code integration platforms, database transformation pipelines, and API gateways. In these environments, applications often evaluate expressions supplied by end-users or third-party webhooks. A compromise in these locations can lead to lateral movement, data exfiltration, or access to cloud metadata services.
The primary remediation strategy is upgrading the JSONata library to a secure version. For systems on the v1.x release branch, administrators must upgrade to version 1.8.8 or higher. For systems on the v2.x release branch, administrators must upgrade to version 2.2.0 or higher.\n\nIn addition to upgrading, developers should implement execution guardrails introduced in version 2.2.0. These guardrails restrict runtime resources, helping prevent denial-of-service vectors like exponential backtracking or infinite loops. Below is an example of configuring these safety options:\n\njavascript\nimport jsonata from 'jsonata';\n\nconst options = {\n stack: 500, // Recursion limit\n timeout: 1000, // 1-second timeout\n sequence: 10000 // Intermediate allocation limit\n};\n\nconst expression = jsonata(userInput, options);\nconst result = await expression.evaluate(data);\n\n\nIf immediate patching is not possible, applications should block any JSONata expressions containing keywords linked to prototype access. These keywords include __proto__, constructor, prototype, __lookupSetter__, and __defineGetter__. However, keyword blocking is prone to bypasses and should only be used as a temporary workaround until the package is updated.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N| Attribute | Detail |
|---|---|
| CWE ID | CWE-94 |
| Attack Vector | Network |
| CVSS Score | 9.3 (Critical) |
| EPSS Score | N/A |
| Impact | Unauthenticated Remote Code Execution |
| Exploit Status | Proof of Concept (PoC) documented |
| KEV Status | Not Listed |
The software constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes the input before it is executed.
An uncontrolled resource consumption vulnerability (CWE-400/CWE-789) exists within the kin-openapi Go library prior to version 0.142.0. The vulnerability occurs during the processing of highly sparse array indexes inside query parameters defined in deepObject style. An unauthenticated remote attacker can exploit this flaw to cause an immediate Out-of-Memory (OOM) crash of the target application.
CVE-2026-63135 is a critical stored Cross-Site Scripting (XSS) vulnerability affecting YOURLS (Your Own URL Shortener) versions 1.5.1 up to (but not including) 1.10.4. Unauthenticated remote attackers can inject malicious JavaScript arrays by crafting an HTTP Referer header sent to a short URL redirect. This value is saved in the database logs and executed without context-aware escaping when an administrative user views the corresponding statistics visualization page.
CVE-2026-68508 is a high-severity arbitrary code execution vulnerability in facebookresearch/hydra (hydra-core) prior to version 1.3.4. The vulnerability exists within the dynamic instantiation system hydra.utils.instantiate(), which resolves and executes arbitrary Python callables from configuration files. An attacker capable of submitting untrusted configurations can achieve arbitrary code execution in the context of the consuming process.
A critical sandbox escape vulnerability in JSONata versions prior to 1.8.8 and 2.2.1 allows unauthenticated remote attackers to execute arbitrary code on the host machine. By submitting crafted JSONata expressions, an attacker can manipulate internal AST structures, bypass object clone helpers, spoof native function flags, and escape the evaluation environment to execute system commands through the Node.js runtime.
CVE-2026-77414 (GHSA-2943-5xfg-gq5f) is a critical sandbox escape and remote code execution vulnerability in the JSONata package. When JSONata processes untrusted expressions, it uses a vulnerable environment lookup check that can be shadowed by user-defined variables. Attackers can leverage this to traverse the prototype chain, reach the global Function constructor, and execute arbitrary system commands on the host machine.
An overly permissive default configuration in the Grav CMS Twig sandbox combined with a lack of neutralization of double-quote characters in the Asset rendering engine allows low-privileged page editors to inject malicious JavaScript into administrative contexts. This leads to a stored cross-site scripting (XSS) condition that compromises the sessions of super-administrators, facilitating complete privilege escalation.