Every CVE Search/ Get API key

CVE-2026-92959

vm2 before 3.11.8 allowAsync Bypass via Promise Thenable

7.1 HighFix available

vm2 before 3.11.8 does not fully enforce the allowAsync: false option in VM and NodeVM. While localPromise.prototype.then is replaced with a handler that throws 'Async not available', the sandbox's Promise static methods (Promise.resolve, Promise.all, Promise.race, Promise.any, and Promise.allSettled) still assimilate attacker-supplied thenables: native promise resolution performs PromiseResolveThenableJob and invokes the sandboxed code's then method in a microtask without passing through the patched then, so the async restriction is never applied. As a result, sandboxed script can schedule work that runs after VM.run() or NodeVM.run() has returned and outside the configured timeout, continuing to execute after the host believes execution is complete.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
Low
Availability
High

Exploit likelihood

0.45%Up 0.2 points since Sep 18, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.1

vm2 before 3.11.8 allowAsync Bypass via Promise Thenable

vm2 before 3.11.8 does not fully enforce the allowAsync: false option in VM and NodeVM. While localPromise.prototype.then is replaced with a handler that throws 'Async not available', the sandbox's Promise static methods (Promise.resolve, Promise.all, Promise.race, Promise.any, and Promise.allSettled) still assimilate attacker-supplied thenables: native promise resolution performs PromiseResolveThenableJob and invokes the sandboxed code's then method in a microtask without passing through the patched then, so the async restriction is never applied. As a result, sandboxed script can schedule work that runs after VM.run() or NodeVM.run() has returned and outside the configured timeout, continuing to execute after the host believes execution is complete.

Record
CVE-2026-92959
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N
CVSS 3.1
7.1 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H
Weakness
CWE-693
Updated
2026-11-02
EUVDEUVD-2026-81608 7.1

vm2 before 3.11.8 does not fully enforce the allowAsync: false option in VM and NodeVM. While localPromise.prototype.then is replaced with a handler that throws 'Async not available', the sandbox's Promise static methods (Promise.resolve, Promise.all, Promise.race, Promise.any, and Promise.allSettled) still assimilate attacker-supplied thenables: native promise resolution performs PromiseResolveThenableJob and invokes the sandboxed code's then method in a microtask without passing through the patched then, so the async restriction is never applied. As a result, sandboxed script can schedule work that runs after VM.run() or NodeVM.run() has returned and outside the configured timeout, continuing to execute after the host believes execution is complete.

Record
EUVD-2026-81608
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-9538-79j2-h62h 7.1

Duplicate Advisory: `allowAsync: false` can be bypassed through Promise thenable assimilation in VM and NodeVM

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-f8gf-w286-fmq2. This link is maintained to preserve external references.

Original Description

vm2 before 3.11.8 does not fully enforce the allowAsync: false option in VM and NodeVM. While localPromise.prototype.then is replaced with a handler that throws 'Async not available', the sandbox's Promise static methods (Promise.resolve, Promise.all, Promise.race, Promise.any, and Promise.allSettled) still assimilate attacker-supplied thenables: native promise resolution performs PromiseResolveThenableJob and invokes the sandboxed code's then method in a microtask without passing through the patched then, so the async restriction is never applied. As a result, sandboxed script can schedule work that runs after VM.run() or NodeVM.run() has returned and outside the configured timeout, continuing to execute after the host believes execution is complete.

Record
GHSA-9538-79j2-h62h
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
7.1 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H
Weakness
CWE-693
Updated
2026-11-02
GitHub AdvisoriesGHSA-f8gf-w286-fmq2 7.1

vm2: `allowAsync: false` can be bypassed through Promise thenable assimilation in VM and NodeVM

Summary

When allowAsync is set to false, vm2 is expected to reject attempts to run asynchronous code. Direct use of Promise.prototype.then is blocked, but Promise static methods still assimilate attacker-controlled thenables. Promise.resolve(thenable), Promise.all([thenable]), Promise.race([thenable]), Promise.any([thenable]), and Promise.allSettled([thenable]) can invoke the thenable's then method in a microtask after VM.run() or NodeVM.run() has already returned.

This bypasses the documented async-execution restriction and runs outside the configured timeout, allowing sandboxed code to continue executing after the host believes execution is complete.

Details

The documented VM option says allowAsync: false should cause attempts to run async code to throw a VMError; README.md:139-145 also recommends using it with timeout. The implementation enforces part of this policy by replacing localPromise.prototype.then with an AsyncErrorHandler when async is disabled:

  • lib/setup-sandbox.js:1629-1637 defines AsyncErrorHandler, whose apply and construct traps throw VMError: Async not available.
  • lib/setup-sandbox.js:1743-1752 installs that handler on localPromise.prototype.then when allowAsync is false.

However, Promise static methods are still exposed and rebound to localPromise:

  • lib/setup-sandbox.js:1816-1819 wraps Promise.all.
  • lib/setup-sandbox.js:1821-1824 wraps Promise.race.
  • lib/setup-sandbox.js:1826-1830 wraps Promise.allSettled.
  • lib/setup-sandbox.js:1833-1837 wraps Promise.any.
  • lib/setup-sandbox.js:1840-1843 wraps Promise.resolve.

Those wrappers prevent species attacks by forcing localPromise as the constructor, but they do not reject or neutralize thenables when allowAsync is false. Native Promise resolution then performs PromiseResolveThenableJob and calls the attacker-controlled then method asynchronously. That job does not go through the patched localPromise.prototype.then method, so the AsyncErrorHandler is never reached.

NodeVM inherits the same sandbox Promise setup through VM (lib/nodevm.js:328-332), so the same thenable-assimilation bypass is reachable in NodeVM as well.

The transformer fast path is not the root cause, but it explains why the minimal PoC is parser-independent: payloads below contain none of catch, import, async, with, the internal state identifier, or \u, so lib/transformer.js:82-89 returns without AST parsing.

PoC

Maintainer-runnable clean-checkout recipe:

npm install
node - <<'NODE'
const {VM, NodeVM} = require('./');

async function runVmCase(name, code) {
  const events = [];
  const vm = new VM({allowAsync: false, timeout: 10, sandbox: {mark: value => events.push(value)}});
  try {
    const ret = vm.run(code);
    console.log(`${name}: returned ${ret}`);
  } catch (e) {
    console.log(`${name}: threw ${e.name}:${e.message}`);
  }
  await new Promise(resolve => setImmediate(resolve));
  console.log(`${name} events: ${events.length ? events.join(',') : '<none>'}`);
}

async function runNodeVmCase(name, code) {
  const events = [];
  const vm = new NodeVM({allowAsync: false, sandbox: {mark: value => events.push(value)}});
  try {
    const ret = vm.run(code);
    console.log(`${name}: returned ${ret}`);
  } catch (e) {
    console.log(`${name}: threw ${e.name}:${e.message}`);
  }
  await new Promise(resolve => setImmediate(resolve));
  console.log(`${name} events: ${events.length ? events.join(',') : '<none>'}`);
}

(async () => {
  await runVmCase('VM Promise.resolve thenable', `Promise.resolve({then(r){mark('resolve-thenable')}}); 1`);
  await runVmCase('VM Promise.all thenable', `Promise.all([{then(r){mark('all-thenable')}}]); 1`);
  await runVmCase('VM Promise.race thenable', `Promise.race([{then(r){mark('race-thenable')}}]); 1`);
  await runVmCase('VM Promise.any thenable', `Promise.any([{then(r){mark('any-thenable')}}]); 1`);
  await runVmCase('VM…
Record
GHSA-f8gf-w286-fmq2
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N
CVSS 3.1
7.1 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H
Weakness
CWE-693
Updated
2026-11-02
NVD 7.1

vm2 before 3.11.8 does not fully enforce the allowAsync: false option in VM and NodeVM. While localPromise.prototype.then is replaced with a handler that throws 'Async not available', the sandbox's Promise static methods (Promise.resolve, Promise.all, Promise.race, Promise.any, and Promise.allSettled) still assimilate attacker-supplied thenables: native promise resolution performs PromiseResolveThenableJob and invokes the sandboxed code's then method in a microtask without passing through the patched then, so the async restriction is never applied. As a result, sandboxed script can schedule work that runs after VM.run() or NodeVM.run() has returned and outside the configured timeout, continuing to execute after the host believes execution is complete.

Record
CVE-2026-92959
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
7.1 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H
Weakness
CWE-693
Updated
2026-11-02
OSV 7.1

vm2 before 3.11.8 allowAsync Bypass via Promise Thenable

vm2 before 3.11.8 does not fully enforce the allowAsync: false option in VM and NodeVM. While localPromise.prototype.then is replaced with a handler that throws 'Async not available', the sandbox's Promise static methods (Promise.resolve, Promise.all, Promise.race, Promise.any, and Promise.allSettled) still assimilate attacker-supplied thenables: native promise resolution performs PromiseResolveThenableJob and invokes the sandboxed code's then method in a microtask without passing through the patched then, so the async restriction is never applied. As a result, sandboxed script can schedule work that runs after VM.run() or NodeVM.run() has returned and outside the configured timeout, continuing to execute after the host believes execution is complete.

Record
CVE-2026-92959
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-693
Updated
2026-11-02
Show 6 moreShow less
OSVGHSA-9538-79j2-h62h 7.1

Duplicate Advisory: `allowAsync: false` can be bypassed through Promise thenable assimilation in VM and NodeVM

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-f8gf-w286-fmq2. This link is maintained to preserve external references.

Original Description

vm2 before 3.11.8 does not fully enforce the allowAsync: false option in VM and NodeVM. While localPromise.prototype.then is replaced with a handler that throws 'Async not available', the sandbox's Promise static methods (Promise.resolve, Promise.all, Promise.race, Promise.any, and Promise.allSettled) still assimilate attacker-supplied thenables: native promise resolution performs PromiseResolveThenableJob and invokes the sandboxed code's then method in a microtask without passing through the patched then, so the async restriction is never applied. As a result, sandboxed script can schedule work that runs after VM.run() or NodeVM.run() has returned and outside the configured timeout, continuing to execute after the host believes execution is complete.

Record
GHSA-9538-79j2-h62h
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
7.1 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H
Weakness
CWE-693
Updated
2026-11-02
OSVGHSA-f8gf-w286-fmq2 7.1

vm2: `allowAsync: false` can be bypassed through Promise thenable assimilation in VM and NodeVM

Summary

When allowAsync is set to false, vm2 is expected to reject attempts to run asynchronous code. Direct use of Promise.prototype.then is blocked, but Promise static methods still assimilate attacker-controlled thenables. Promise.resolve(thenable), Promise.all([thenable]), Promise.race([thenable]), Promise.any([thenable]), and Promise.allSettled([thenable]) can invoke the thenable's then method in a microtask after VM.run() or NodeVM.run() has already returned.

This bypasses the documented async-execution restriction and runs outside the configured timeout, allowing sandboxed code to continue executing after the host believes execution is complete.

Details

The documented VM option says allowAsync: false should cause attempts to run async code to throw a VMError; README.md:139-145 also recommends using it with timeout. The implementation enforces part of this policy by replacing localPromise.prototype.then with an AsyncErrorHandler when async is disabled:

  • lib/setup-sandbox.js:1629-1637 defines AsyncErrorHandler, whose apply and construct traps throw VMError: Async not available.
  • lib/setup-sandbox.js:1743-1752 installs that handler on localPromise.prototype.then when allowAsync is false.

However, Promise static methods are still exposed and rebound to localPromise:

  • lib/setup-sandbox.js:1816-1819 wraps Promise.all.
  • lib/setup-sandbox.js:1821-1824 wraps Promise.race.
  • lib/setup-sandbox.js:1826-1830 wraps Promise.allSettled.
  • lib/setup-sandbox.js:1833-1837 wraps Promise.any.
  • lib/setup-sandbox.js:1840-1843 wraps Promise.resolve.

Those wrappers prevent species attacks by forcing localPromise as the constructor, but they do not reject or neutralize thenables when allowAsync is false. Native Promise resolution then performs PromiseResolveThenableJob and calls the attacker-controlled then method asynchronously. That job does not go through the patched localPromise.prototype.then method, so the AsyncErrorHandler is never reached.

NodeVM inherits the same sandbox Promise setup through VM (lib/nodevm.js:328-332), so the same thenable-assimilation bypass is reachable in NodeVM as well.

The transformer fast path is not the root cause, but it explains why the minimal PoC is parser-independent: payloads below contain none of catch, import, async, with, the internal state identifier, or \u, so lib/transformer.js:82-89 returns without AST parsing.

PoC

Maintainer-runnable clean-checkout recipe:

npm install
node - <<'NODE'
const {VM, NodeVM} = require('./');

async function runVmCase(name, code) {
  const events = [];
  const vm = new VM({allowAsync: false, timeout: 10, sandbox: {mark: value => events.push(value)}});
  try {
    const ret = vm.run(code);
    console.log(`${name}: returned ${ret}`);
  } catch (e) {
    console.log(`${name}: threw ${e.name}:${e.message}`);
  }
  await new Promise(resolve => setImmediate(resolve));
  console.log(`${name} events: ${events.length ? events.join(',') : '<none>'}`);
}

async function runNodeVmCase(name, code) {
  const events = [];
  const vm = new NodeVM({allowAsync: false, sandbox: {mark: value => events.push(value)}});
  try {
    const ret = vm.run(code);
    console.log(`${name}: returned ${ret}`);
  } catch (e) {
    console.log(`${name}: threw ${e.name}:${e.message}`);
  }
  await new Promise(resolve => setImmediate(resolve));
  console.log(`${name} events: ${events.length ? events.join(',') : '<none>'}`);
}

(async () => {
  await runVmCase('VM Promise.resolve thenable', `Promise.resolve({then(r){mark('resolve-thenable')}}); 1`);
  await runVmCase('VM Promise.all thenable', `Promise.all([{then(r){mark('all-thenable')}}]); 1`);
  await runVmCase('VM Promise.race thenable', `Promise.race([{then(r){mark('race-thenable')}}]); 1`);
  await runVmCase('VM Promise.any thenable', `Promise.any([{then(r){mark('any-thenable')}}]); 1`);
  await runVmCase('VM…
Record
GHSA-f8gf-w286-fmq2
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N
CVSS 3.1
7.1 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H
Weakness
CWE-693
Updated
2026-11-02
OSVCLSA-2026-1791279062 No score

TuxCare security update for vm2 (8 CVEs)

TuxCare rebuilt vm2 to address 8 CVEs. Fixed in 3.11.7-tuxcare.1.

Record
CLSA-2026-1791279062
Updated
2026-11-02
OSVCLSA-2026-1791666488 No score

TuxCare security update for vm2 (4 CVEs)

TuxCare rebuilt vm2 to address 4 CVEs. Fixed in 3.10.0-tuxcare.7.

Record
CLSA-2026-1791666488
Updated
2026-11-02
OSVCLSA-2026-1791888184 No score

TuxCare security update for vm2 (2 CVEs)

TuxCare rebuilt vm2 to address 2 CVEs. Fixed in 3.11.5-tuxcare.8.

Record
CLSA-2026-1791888184
Updated
2026-11-02
OSVROOT-APP-NPM-CVE-2026-92959 No score

CVE-2026-92959 in vm2 - Patched by Root

Root has patched CVE-2026-92959 in the vm2 package for Root:npm. Multiple fixed versions available.

Record
ROOT-APP-NPM-CVE-2026-92959
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
vm2
npm
through 3.11.7
3.11.8
vm2
TuxCare:npm
From an advisory covering 8 CVEs
3.11.7-tuxcare.0 up to 3.11.7-tuxcare.1, plus listed versions
3.11.7
3.11.7-tuxcare.1
vm2
TuxCare:npm
From an advisory covering 4 CVEs
3.10.0-tuxcare.0 up to 3.10.0-tuxcare.7, plus listed versions
3.10.03.10.0-tuxcare.13.10.0-tuxcare.23.10.0-tuxcare.33.10.0-tuxcare.43.10.0-tuxcare.53.10.0-tuxcare.6
3.10.0-tuxcare.7
vm2
TuxCare:npm
From an advisory covering 2 CVEs
3.11.5-tuxcare.0 up to 3.11.5-tuxcare.8, plus listed versions
3.11.53.11.5-tuxcare.13.11.5-tuxcare.23.11.5-tuxcare.33.11.5-tuxcare.43.11.5-tuxcare.53.11.5-tuxcare.63.11.5-tuxcare.7
3.11.5-tuxcare.8
vm2
npm
before 3.11.8
3.11.8
vm2
Root:npm
before 3.11.6-aikido.1
3.11.6-aikido.1
@rootio/vm2
Root:npm
before 3.11.6-root.io.1
3.11.6-root.io.1

History

History
RecordedChangedSources
affected, facts
affected
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-92959 is a high vulnerability (CVSS 7.1) in Patriksimek Vm2, published September 17, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.45%, the 39th percentile. Fixed in vm2 3.11.8.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open