· via dev.to (home feed)
CVE-2026-100721: vm2 sandbox escape traced to unanchored prefix regex in module allowlist
A dev.to root-cause analysis traces vm2's 9.5-rated CVE-2026-100721 to an allowlist regex with no end anchor, letting guest code load non-approved sibling modules with host privileges. Fixed in vm2 3.12.2.

A prefix that acted as permission
A root-cause analysis published on dev.to traces CVE-2026-100721, a 9.5-rated sandbox escape in the Node.js sandboxing library vm2, to a single missing regex boundary in the mechanism that polices which modules guest code may load.
The flaw is neither a parser bug nor a classic VM breakout, the author explains. It is an authorization defect: vm2's NodeVM lets an embedder supply a custom resolver through require.external, and vm2 records each resolved path so later require calls can be checked against the allowlist. That recording step, in LegacyResolver.customResolve inside lib/resolver-compat.js, stored the resolved path as a regex with only a start anchor — no trailing path separator and no end-of-string marker. The subsequent check, isPathAllowedForModule, therefore approved any path that merely began with the approved string.
One allowlisted module authorized its siblings
The maintainer's own advisory, GHSA-5h3f-q97h-ccvc, published on September 8 together with a working proof-of-concept harness, spells out the attack shape. If a sandbox allowlists a module named foo and a second package foo2 sits beside it, the first legitimate require records the prefix .../node_modules/foo. Guest code can then request the absolute path .../node_modules/foo2/index.js; the prefix matches, and because NodeVM's default context is host, the sibling loads through hostRequire and its top-level code executes with full Node.js authority before vm2's readonly wrappers apply.
The advisory's harness signals success with a PREFIX_PWN string produced by a host-side child_process call, while a control setup lacking the overlapping prefix is rejected with ENOTFOUND, according to the dev.to write-up.
Timeline and severity
The NVD record appeared on September 27 with VulnCheck as the CNA, and NVD currently marks it as deferred while the MITRE record lists it as published — a discrepancy worth checking before citing. The GHSA advisory and the fix in vm2 3.12.2 both shipped on September 8. Scoring sits at 9.5 under CVSS 4.0 and 9.0 under 3.1, but SSVC rates exploitation as proof-of-concept only: the author found no reports of in-the-wild use and no KEV entry. Two sibling advisories landed the same day — CVE-2026-100722 and CVE-2026-100723, the latter a 7.5-severity zlib issue — so patching this bug alone does not clear the library.
Who is actually exposed
The dev.to analysis is careful about scope. The bug requires a non-default setup: require.external combined with a custom resolver function. A stock vm2 configuration without a custom resolver does not reach customResolve this way, and the CVSS 4.0 vector encodes that precondition while CISA's SSVC marks the issue not automatable. The sting is that a custom resolver paired with an allowlist is exactly the documented way to grant controlled external module access — the feature exists to be the security boundary, and the boundary over-matched.
Detecting and fixing it
The fix in 3.12.2 switches the recording to a boundary-matched base path and, per the release notes, changes no APIs, so upgrading is the whole remedy. Teams unable to patch immediately can audit in minutes: npm ls vm2 confirms the installed version, and grepping for new NodeVM alongside external: and a resolve function locates the risky configuration. The author also relays — explicitly flagged as unverified — a claim that the npm advisory database lagged the fix, with npm audit reportedly calling 3.12.1 clean for at least one record, which is a reason to trust the installed-version check over the auditor alone. The author notes they did not reproduce the escape themselves; the account follows the NVD description, the maintainer's advisory and the release notes, which agree on the mechanism.
vm2 is no longer the dead project folklore claims
The analysis also corrects a widespread assumption. Although vm2 was discontinued in 2023 amid the maintainer's own warnings that its security problems could not be fully fixed, the repository now shows a steady release ladder from v3.10.0 in late 2025 through 3.12.2, a security policy file, and CI covering Bun as well as Node — and the maintainer published this advisory himself, with a working PoC, on the day the fix shipped. The author's measured conclusion is that an actively maintained isolation library is not automatically fit to face a determined adversary: vm2 may remain reasonable for hardening untrusted-but-not-adversarial input, but not as a hard boundary against hostile code.
Why it matters
For anyone embedding vm2 with the documented external-module pattern, the allowlist was quietly broader than intended, and a one-line version bump closes it. The broader lesson generalizes well past this library: any access-control check built on string-prefix matching — CI path filters, registry hostnames, file path allowlists — is authorization code that deserves boundary anchoring and tests. And the episode is a reminder to re-verify "dead project" folklore before it drives an upgrade-or-rip-out decision.
- #node-js
- #security
- #javascript
- #vulnerability
- #sandbox