+31
-2
@@ -233,4 +233,11 @@ 'use strict'; | ||
| * @param {boolean} [options.nesting=false] - | ||
| * <b>WARNING: Allowing this is a security risk as scripts can create a NodeVM which can require any host module.</b> | ||
| * Allow nesting of VMs. | ||
| * <b>WARNING: <code>nesting: true</code> is an escape hatch — sandbox code | ||
| * can <code>require('vm2')</code> and construct nested NodeVMs whose | ||
| * <code>require</code> config is chosen by the sandbox, NOT constrained by | ||
| * the outer config. Enabling <code>nesting: true</code> grants the sandbox | ||
| * unrestricted host module access. Do not enable for untrusted code. | ||
| * See README "`nesting: true` is an escape hatch".</b> | ||
| * Combining <code>nesting: true</code> with <code>require: false</code> | ||
| * throws <code>VMError</code> at construction (GHSA-8hg8-63c5-gwmx) — the | ||
| * pair is contradictory. | ||
| * @param {("commonjs"|"none")} [options.wrapper="commonjs"] - <code>commonjs</code> to wrap script into CommonJS wrapper, | ||
@@ -247,2 +254,24 @@ * <code>none</code> to retrieve value returned by the script. | ||
| constructor(options = {}) { | ||
| // SECURITY (GHSA-8hg8-63c5-gwmx): `nesting: true` injects a NESTING_OVERRIDE | ||
| // builtin that exposes `vm2` to the sandbox regardless of `require: false`. | ||
| // The sandbox then constructs an inner NodeVM with attacker-chosen `require` | ||
| // config (unconstrained by the outer config — by design of nesting) and | ||
| // reaches `child_process` for full host RCE. The contradictory option pair | ||
| // is the specific trap the advisory describes; reject it at construction | ||
| // with a clear error rather than silently producing an unsandboxed config. | ||
| // (Bare `nesting: true` without `require: false` continues to work as the | ||
| // documented escape hatch; the README "`nesting: true` is an escape hatch" | ||
| // section explains the broader trade-off.) | ||
| if (options.nesting === true && options.require === false) { | ||
| throw new VMError( | ||
| 'NodeVM `nesting: true` is incompatible with `require: false`. ' | ||
| + '`nesting: true` is an escape hatch that lets sandbox code ' | ||
| + '`require(\'vm2\')` and construct nested VMs unconstrained by the outer ' | ||
| + 'config — which contradicts `require: false`. To deny all requires, ' | ||
| + 'remove `nesting: true`. To allow nested VMs, replace `require: false` ' | ||
| + 'with an explicit config (e.g. `require: { builtin: [] }`) so the ' | ||
| + 'tradeoff is visible. See README "`nesting: true` is an escape hatch". ' | ||
| + 'Context: GHSA-8hg8-63c5-gwmx.' | ||
| ); | ||
| } | ||
| const { | ||
@@ -249,0 +278,0 @@ compiler, |
+1
-1
@@ -16,3 +16,3 @@ { | ||
| ], | ||
| "version": "3.11.0", | ||
| "version": "3.11.1", | ||
| "main": "index.js", | ||
@@ -19,0 +19,0 @@ "sideEffects": false, |
+18
-0
@@ -505,2 +505,20 @@ # vm2 [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![License][license-image]][license-url] [](https://github.com/patriksimek/vm2/actions/workflows/test.yml) [![Known Vulnerabilities][snyk-image]][snyk-url] | ||
| ### 5. `nesting: true` is an escape hatch | ||
| `nesting: true` lets sandbox code `require('vm2')` and construct nested NodeVMs. **The nested VM's `require` config is chosen by the sandbox code that constructs it, not constrained by the outer VM.** Concretely: | ||
| ```js | ||
| const vm = new NodeVM({ nesting: true, require: { builtin: [] } }); | ||
| vm.run(` | ||
| const { NodeVM: NVM } = require('vm2'); | ||
| // Inner VM's config is whatever the sandbox writes here: | ||
| const inner = new NVM({ require: { builtin: ['child_process'] } }); | ||
| inner.run('require("child_process").execSync("id")'); // RCE | ||
| `); | ||
| ``` | ||
| If you set `nesting: true`, you have effectively granted the sandbox the same trust level you have. **Do not enable `nesting: true` for untrusted code.** Use it only when you trust the sandboxed code itself but want VM-style execution semantics (fresh global, controlled timeouts) for non-security reasons. | ||
| The combination `{ nesting: true, require: false }` throws `VMError` at construction (GHSA-8hg8-63c5-gwmx) because the pair is contradictory: `nesting: true` makes `vm2` requireable regardless of `require: false`, so the deny-all expectation cannot be honored. To deny all requires, remove `nesting: true`. To allow nested VMs, replace `require: false` with an explicit config so the tradeoff is visible. | ||
| ## Known Issues | ||
@@ -507,0 +525,0 @@ |
324860
0.99%7873
0.37%539
3.45%