* Create a new NodeVM instance. * * Unlike VM, NodeVM lets you use require same way like in regular node. * * However, it does not use the timeout. * * @public * @param {Object} [options] - VM options. * @param {Object} [options.sandbox] - Objects that will be copied into the
(options = {})
| 250 | * @throws {VMError} If the compiler is unknown. |
| 251 | */ |
| 252 | constructor(options = {}) { |
| 253 | const { |
| 254 | compiler, |
| 255 | eval: allowEval, |
| 256 | wasm, |
| 257 | console: consoleType = 'inherit', |
| 258 | require: requireOpts = false, |
| 259 | nesting = false, |
| 260 | wrapper = 'commonjs', |
| 261 | sourceExtensions = ['js'], |
| 262 | argv, |
| 263 | env, |
| 264 | strict = false, |
| 265 | sandbox, |
| 266 | timeout, |
| 267 | allowAsync, |
| 268 | bufferAllocLimit |
| 269 | } = options; |
| 270 | |
| 271 | // SECURITY (GHSA-m4wx-m65x-ghrr, supersedes GHSA-8hg8-63c5-gwmx): |
| 272 | // A truthy `nesting` injects a NESTING_OVERRIDE builtin that exposes |
| 273 | // `vm2` to the sandbox. When `requireOpts` is not a real require-config |
| 274 | // object, `makeResolverFromLegacyOptions` produces a resolver whose ONLY |
| 275 | // builtin is `vm2` — a pure escape primitive (sandbox does |
| 276 | // `require('vm2')`, builds an inner NodeVM with attacker-chosen |
| 277 | // `require` config, reaches `child_process` for host RCE). |
| 278 | // |
| 279 | // The guard mirrors the actual reachability of that insecure resolver |
| 280 | // on two axes: |
| 281 | // |
| 282 | // 1. `nesting` truthiness, not strict `=== true`. The override is gated |
| 283 | // a few lines below by `nesting && NESTING_OVERRIDE`, which fires for |
| 284 | // ANY truthy value (`1`, `'yes'`, `{}`, `[]`, etc.). The check must |
| 285 | // cover every truthy `nesting` value the override gate accepts. |
| 286 | // |
| 287 | // 2. `requireOpts` must be an actual require-config object (or a |
| 288 | // `Resolver` instance), not just "truthy". `makeResolverFromLegacyOptions` |
| 289 | // destructures every primitive/function value to all-`undefined` and |
| 290 | // falls into the same NESTING_OVERRIDE-only `if (!options)` branch as |
| 291 | // falsy values do. The shapes that collapse to the insecure resolver: |
| 292 | // - `require: false` / `undefined` / `null` / `0` / `''` |
| 293 | // → falsy → `if (!options)` branch |
| 294 | // - `require: true` / `1` / `'yes'` / `Symbol()` / `function(){}` |
| 295 | // → truthy non-object → destructured to all-undefined → |
| 296 | // `makeBuiltinsFromLegacyOptions(undefined, …, override)` → |
| 297 | // same NESTING_OVERRIDE-only resolver |
| 298 | // The documented escape hatch (a truthy `nesting` + an explicit |
| 299 | // `require` config object, even `{}`) continues to work — a non-null |
| 300 | // object counts as the developer's deliberate acknowledgment of the |
| 301 | // tradeoff. A custom `Resolver` instance is also accepted (the |
| 302 | // `customResolver` path below bypasses `makeResolverFromLegacyOptions` |
| 303 | // entirely, so NESTING_OVERRIDE is never injected into it). |
| 304 | const hasRealRequireConfig = |
| 305 | requireOpts instanceof Resolver |
| 306 | || (typeof requireOpts === 'object' && requireOpts !== null); |
| 307 | if (nesting && !hasRealRequireConfig) { |
| 308 | throw new VMError( |
| 309 | 'NodeVM `nesting` requires an explicit `require` config object. ' |
nothing calls this directly
no test coverage detected