Aug 21, 2026·7 min read·6 visits
A critical vulnerability in JSONata permits attackers to bypass query sandbox isolation and achieve remote code execution by shadowing local lookup methods and traversing the prototype chain.
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.
JSONata is a domain-specific query and transformation language widely embedded in Node.js applications to execute structured operations over JSON payloads. The interpreter is designed to execute queries safely without granting direct access to the underlying JavaScript execution environment. To maintain safety, JSONata relies on an internal sandbox that isolates the query execution runtime from the global JavaScript scope.
This security boundary is compromised when users can query, read, or modify administrative properties belonging to the runtime environment. In typical web applications, JSONata is used in back-end microservices, integration platforms, and API gateways where users supply custom query templates to shape application outputs. If an attacker can inject queries that escape the sandbox, they can interact directly with the Node.js runtime process.
CVE-2026-77414 identifies a vulnerability where JSONata's internal scope-tracking implementation fails to protect internal properties of JavaScript objects during variable resolution. By manipulating the variable binding mechanism, an attacker can traverse the JavaScript prototype chain. This prototype chain traversal leads to a complete sandbox escape, enabling arbitrary code execution on the hosting system.
The root cause of this vulnerability lies in how JSONata manages execution frames and resolves variable names inside the src/jsonata.js module. Every variable definition or query execution context is tracked using standard environment frames initialized by the internal createFrame function. This function initializes a local context bindings map using a standard JavaScript object literal (var bindings = {}).
Because bindings is instantiated as a standard object literal, it inherits properties from Object.prototype, including native methods like hasOwnProperty, toString, and constructor. To verify if a variable lookup request is defined within the local frame, the lookup mechanism invokes bindings.hasOwnProperty(name). This implementation assumes that the hasOwnProperty property on the bindings object will always resolve to the native Object.prototype.hasOwnProperty function.
However, JSONata allows dynamic variable assignment within the query expression itself. An attacker can assign a custom value to a variable named hasOwnProperty, which shadows the native method on the bindings object. When the evaluator subsequently invokes bindings.hasOwnProperty(name), it executes the user-defined value instead of the native method, causing the existence check to bypass or fail. This lookup failure allows the evaluator to skip the frame boundary check and fall back to searching the prototype chain for the requested key.
The remediation of CVE-2026-77414 required rewriting how frame scopes are initialized and how property lookups are performed. The primary fix is implemented in commit 59e25144fc3b7125f6befd71b8a6e14e1fa610d2. The vulnerable initialization of the bindings map as a standard object literal was refactored to use Object.create(null). This creates a prototype-free object, completely severing any inheritance from Object.prototype.
// VULNERABLE CODE
function createFrame(enclosingEnvironment) {
var bindings = {}; // Regular object inheriting Object.prototype
const newFrame = {
bind: function (name, value) {
bindings[name] = value;
},
lookup: function (name) {
var value;
if(bindings.hasOwnProperty(name)) { // Vulnerable direct check
value = bindings[name];
} else if (enclosingEnvironment) {
value = enclosingEnvironment.lookup(name);
}
return value;
}
};
return newFrame;
}// PATCHED CODE
function createFrame(enclosingEnvironment) {
var bindings = Object.create(null); // Secure: Prototype-free map
const newFrame = {
bind: function (name, value) {
bindings[name] = value;
},
lookup: function (name) {
var value;
// Secure lookup: explicit call to prototype-safe method
if(Object.prototype.hasOwnProperty.call(bindings, name)) {
value = bindings[name];
} else if (enclosingEnvironment) {
value = enclosingEnvironment.lookup(name);
}
return value;
}
};
return newFrame;
}In addition to secure variable maps, secondary commits protected internal evaluation pathways. Commit 47c0e58542202c705726663166dbee5fcae47d06 blocks wildcard queries from extracting internal structures of function objects. Commit f174348c7fa30f271b63ddedf0767e814004bc4d introduces a check that prevents users from manually passing objects containing internal fields like _jsonata_lambda or _jsonata_function. Together, these patches ensure that attackers cannot spoof internal runtime abstractions.
Exploitation of CVE-2026-77414 requires the attacker to construct a query that pollutes the frame bindings map and redirects variable resolution to the prototype chain. The initial step is to shadow the lookup method by binding a custom variable: $hasOwnProperty := $spread($string). This variable assignment overwrites the identifier hasOwnProperty in the local frame, disrupting the standard evaluation of the bindings.hasOwnProperty(name) condition.
Once the native method is shadowed, subsequent lookups bypass local boundaries. The evaluator allows access to prototype-inherited identifiers, resolving the custom query variable $constructor to the native Object.prototype.constructor function. The attacker can then invoke this constructor dynamically to generate arbitrary JavaScript functions in the host runtime context.
import jsonata from 'jsonata';
// A crafted expression bypassing the environment lookup check
const expression = jsonata(`
(
$hasOwnProperty := $spread($string);
$__proto__ := $constructor;
$constructor("return process.getBuiltinModule('child_process').execSync('id',{stdio:'inherit'})")();
)`);
// Triggering evaluation against an empty context
await expression.evaluate({});The final payload invokes the global Function constructor with a body that accesses Node.js's built-in dynamic module import mechanisms. By executing process.getBuiltinModule('child_process').execSync(...), the payload escapes the JSONata virtual machine wrapper and executes system-level terminal commands. The evaluation succeeds without requiring specialized application conditions or authentication states.
The technical impact of this vulnerability is classified as critical, carrying a CVSS v4.0 base score of 9.3. The attack is executable entirely over the network, requires no specific user interaction, and can be initiated by unauthenticated users. It results in a complete loss of confidentiality, integrity, and availability on the hosting server.
When a server-side Node.js application accepts user-defined JSONata expressions for execution, the exploit permits arbitrary shell execution within the container or host operating system. Threat actors can read sensitive configuration files, capture environment variables, and steal database credentials. They can also use local access to pivot into internal cloud environments or connect to remote command-and-control networks.
In serverless and microservice deployments, an attacker can leverage this access to persist inside the runtime, compromise adjacent database instances, or deploy unauthorized payloads. Because the vulnerability results in direct operating system shell execution, any service utilizing vulnerable versions of JSONata must be treated as completely compromised upon exposure.
Remediation requires upgrading the jsonata NPM package to a secure release. For deployments operating on the 1.x branch, the secure backported version is 1.8.8. Deployments on the 2.x branch must upgrade to version 2.2.1 or higher. Applications should automate dependency checks using audit tools to detect vulnerable instances.
If immediate software upgrade is not possible, developers should deploy virtual machine isolation or low-privilege containerization. Running Node.js instances within gVisor, Firecracker, or strict Linux namespaces limits the blast radius of any system-level execution. Additionally, untrusted user inputs should not be processed directly by the JSONata evaluator if possible.
Security teams must also implement egress filtering on databases and application servers. Restricting outbound network connections prevents escaped web shells from downloading additional malware or communicating with command-and-control servers, disrupting the final stages of a potential exploit attempt.
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| Product | Affected Versions | Fixed Version |
|---|---|---|
jsonata JSONata | < 1.8.8 | 1.8.8 |
jsonata JSONata | >= 2.0.0 < 2.2.1 | 2.2.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-94 |
| Attack Vector | Network |
| CVSS v4.0 | 9.3 (Critical) |
| Impact | Remote Code Execution (RCE) / Sandbox Escape |
| Exploit Status | Proof-of-Concept |
| 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 the code segment is executed.
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.
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.
An authenticated Twig sandbox escape vulnerability in Winter CMS allows users with template-editing privileges to bypass sandbox restrictions and execute arbitrary PHP code. This vulnerability represents a complete bypass of the sandbox protections introduced by the previous patch for CVE-2024-54149.
A missing authorization vulnerability in Fleet device management software allows unauthenticated remote attackers to access proprietary enterprise iOS packages (.ipa) and manifest configurations by scanning predictable integer identifiers.
A relative path traversal vulnerability (CWE-23) in the client-side sftp utility of OpenSSH before version 10.4 allows malicious or compromised SFTP servers to write or overwrite files outside the intended destination directory when a user executes a direct one-shot download command.
A SQL injection vulnerability exists in the activity list endpoints of Fleet Device Management. Authenticated users can manipulate the order_key parameter to sort database queries by arbitrary columns, including columns not projected in the SELECT query. This flaw allows attackers to establish an inference oracle to extract sensitive information from the database.