Socket
Socket
Sign inDemoInstall

vm2

Package Overview
Dependencies
2
Maintainers
3
Versions
65
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 3.9.17 to 3.9.18

lib/builtin.js

6

CHANGELOG.md

@@ -0,1 +1,7 @@

v3.9.18 (2023-05-15)
--------------------
[fix] Multiple security fixes.
[new] Add resolver API to create a shared resolver for multiple `NodeVM` instances allowing to cache scripts and increase sandbox startup times.
[new] Allow to pass a function to `require.context` which is called with the filename allowing to specify the context pre file.
v3.9.17 (2023-04-17)

@@ -2,0 +8,0 @@ --------------------

54

index.d.ts

@@ -63,2 +63,20 @@ import { EventEmitter } from 'events';

/**
* Function that will be called to load a built-in into a vm.
*/
export type BuiltinLoad = (vm: NodeVM) => any;
/**
* Either a function that will be called to load a built-in into a vm or an object with a init method and a load method to load the built-in.
*/
export type Builtin = BuiltinLoad | {init: (vm: NodeVM)=>void, load: BuiltinLoad};
/**
* Require method
*/
export type HostRequire = (id: string) => any;
/**
* This callback will be called to specify the context to use "per" module. Defaults to 'sandbox' if no return value provided.
*/
export type PathContextCallback = (modulePath: string, extensionType: string) => 'host' | 'sandbox';
/**
* Require options for a VM

@@ -71,14 +89,15 @@ */

*/
builtin?: string[];
builtin?: readonly string[];
/*
* `host` (default) to require modules in host and proxy them to sandbox. `sandbox` to load, compile and
* require modules in sandbox. Built-in modules except `events` always required in host and proxied to sandbox
* require modules in sandbox or a callback which chooses the context based on the filename.
* Built-in modules except `events` always required in host and proxied to sandbox
*/
context?: "host" | "sandbox";
context?: "host" | "sandbox" | PathContextCallback;
/** `true`, an array of allowed external modules or an object with external options (default: `false`) */
external?: boolean | string[] | { modules: string[], transitive: boolean };
external?: boolean | readonly string[] | { modules: readonly string[], transitive: boolean };
/** Array of modules to be loaded into NodeVM on start. */
import?: string[];
import?: readonly string[];
/** Restricted path(s) where local modules can be required (default: every path). */
root?: string | string[];
root?: string | readonly string[];
/** Collection of mock modules (both external or built-in). */

@@ -89,3 +108,3 @@ mock?: any;

/** Custom require to require host and built-in modules. */
customRequire?: (id: string) => any;
customRequire?: HostRequire;
/** Load modules in strict mode. (default: true) */

@@ -103,3 +122,16 @@ strict?: boolean;

export abstract class Resolver {
private constructor(fs: VMFileSystemInterface, globalPaths: readonly string[], builtins: Map<string, Builtin>);
}
/**
* Create a resolver as normal `NodeVM` does given `VMRequire` options.
*
* @param options The options that would have been given to `NodeVM`.
* @param override Custom overrides for built-ins.
* @param compiler Compiler to be used for loaded modules.
*/
export function makeResolverFromLegacyOptions(options: VMRequire, override?: {[key: string]: Builtin}, compiler?: CompilerFunction): Resolver;
/**
* Options for creating a VM

@@ -116,3 +148,3 @@ */

/**
* Script timeout in milliseconds. Timeout is only effective on code you run through `run`.
* Script timeout in milliseconds. Timeout is only effective on code you run through `run`.
* Timeout is NOT effective on any method returned by VM.

@@ -149,3 +181,3 @@ */

/** `true` or an object to enable `require` options (default: `false`). */
require?: boolean | VMRequire;
require?: boolean | VMRequire | Resolver;
/**

@@ -159,3 +191,3 @@ * **WARNING**: This should be disabled. It allows to create a NodeVM form within the sandbox which could return any host module.

/** File extensions that the internal module resolver should accept. */
sourceExtensions?: string[];
sourceExtensions?: readonly string[];
/**

@@ -234,2 +266,4 @@ * Array of arguments passed to `process.argv`.

timeout?: number;
/** The resolver used to resolve modules */
readonly resolver: Resolver;
/** Runs the code */

@@ -236,0 +270,0 @@ run(js: string | VMScript, options?: string | { filename?: string, wrapper?: "commonjs" | "none", strict?: boolean }): any;

@@ -18,2 +18,8 @@ 'use strict';

} = require('./filesystem');
const {
Resolver
} = require('./resolver');
const {
makeResolverFromLegacyOptions
} = require('./resolver-compat');

@@ -25,1 +31,3 @@ exports.VMError = VMError;

exports.VMFileSystem = VMFileSystem;
exports.Resolver = Resolver;
exports.makeResolverFromLegacyOptions = makeResolverFromLegacyOptions;

143

lib/nodevm.js

@@ -20,2 +20,13 @@ 'use strict';

/**
* This callback will be called to specify the context to use "per" module. Defaults to 'sandbox' if no return value provided.
*
* NOTE: many interoperating modules must live in the same context.
*
* @callback pathContextCallback
* @param {string} modulePath - The full path to the module filename being requested.
* @param {string} extensionType - The module type (node = native, js = cjs/esm module)
* @return {("host"|"sandbox")} The context for this module.
*/
const fs = require('fs');

@@ -42,4 +53,5 @@ const pa = require('path');

const {
resolverFromOptions
makeResolverFromLegacyOptions
} = require('./resolver-compat');
const { Resolver } = require('./resolver');

@@ -91,2 +103,32 @@ const objectDefineProperty = Object.defineProperty;

function makeCustomExtensions(vm, resolver, sourceExtensions) {
const extensions = { __proto__: null };
const loadJS = resolver.makeExtensionHandler(vm, 'loadJS');
for (let i = 0; i < sourceExtensions.length; i++) {
extensions['.' + sourceExtensions[i]] = loadJS;
}
if (!extensions['.json']) extensions['.json'] = resolver.makeExtensionHandler(vm, 'loadJSON');
if (!extensions['.node']) extensions['.node'] = resolver.makeExtensionHandler(vm, 'loadNode');
return extensions;
}
function makeSafePaths(unsafePaths) {
if (unsafePaths === undefined) return undefined;
if (!Array.isArray(unsafePaths)) return true;
const paths = [...unsafePaths];
if (paths.some(path => typeof path !== 'string')) return true;
return paths;
}
function makeSafeOptions(unsafeOptions) {
if (unsafeOptions === undefined || unsafeOptions == null) return unsafeOptions;
if (typeof unsafeOptions !== 'object' && typeof unsafeOptions !== 'function') return unsafeOptions;
return {
unsafeOptions,
paths: makeSafePaths(unsafeOptions.paths)
};
}
/**

@@ -174,3 +216,3 @@ * Event caused by a <code>console.debug</code> call if <code>options.console="redirect"</code> is specified.

* <code>inherit</code> to enable console, <code>redirect</code> to redirect to events, <code>off</code> to disable console.
* @param {Object|boolean} [options.require=false] - Allow require inside the sandbox.
* @param {Object|boolean|Resolver} [options.require=false] - Allow require inside the sandbox.
* @param {(boolean|string[]|Object)} [options.require.external=false] - <b>WARNING: When allowing require the option <code>options.require.root</code>

@@ -184,4 +226,6 @@ * should be set to restrict the script from requiring any module. Values can be true, an array of allowed external modules or an object.

* @param {Object} [options.require.mock] - Collection of mock modules (both external or built-in).
* @param {("host"|"sandbox")} [options.require.context="host"] - <code>host</code> to require modules in host and proxy them to sandbox.
* @param {("host"|"sandbox"|pathContextCallback)} [options.require.context="host"] -
* <code>host</code> to require modules in host and proxy them to sandbox.
* <code>sandbox</code> to load, compile and require modules in sandbox.
* <code>pathContext(modulePath, ext)</code> to choose a mode per module (full path provided).
* Builtin modules except <code>events</code> always required in host and proxied to sandbox.

@@ -192,3 +236,3 @@ * @param {string[]} [options.require.import] - Array of modules to be loaded into NodeVM on start.

* @param {customRequire} [options.require.customRequire=require] - Custom require to require host and built-in modules.
* @param {boolean} [option.require.strict=true] - Load required modules in strict mode.
* @param {boolean} [options.require.strict=true] - Load required modules in strict mode.
* @param {boolean} [options.nesting=false] -

@@ -230,2 +274,5 @@ * <b>WARNING: Allowing this is a security risk as scripts can create a NodeVM which can require any host module.</b>

const customResolver = requireOpts instanceof Resolver;
const resolver = customResolver ? requireOpts : makeResolverFromLegacyOptions(requireOpts, nesting && NESTING_OVERRIDE, this._compiler);
// This is only here for backwards compatibility.

@@ -241,6 +288,4 @@ objectDefineProperty(this, 'options', {__proto__: null, value: {

const resolver = resolverFromOptions(this, requireOpts, nesting && NESTING_OVERRIDE, this._compiler);
objectDefineProperty(this, 'resolver', {__proto__: null, value: resolver, enumerable: true});
objectDefineProperty(this, '_resolver', {__proto__: null, value: resolver});
if (!cacheSandboxScript) {

@@ -253,19 +298,5 @@ cacheSandboxScript = compileScript(`${__dirname}/setup-node-sandbox.js`,

const extensions = {
__proto__: null
};
const extensions = makeCustomExtensions(this, resolver, sourceExtensions);
const loadJS = (mod, filename) => resolver.loadJS(this, mod, filename);
for (let i = 0; i < sourceExtensions.length; i++) {
extensions['.' + sourceExtensions[i]] = loadJS;
}
if (!extensions['.json']) extensions['.json'] = (mod, filename) => resolver.loadJSON(this, mod, filename);
if (!extensions['.node']) extensions['.node'] = (mod, filename) => resolver.loadNode(this, mod, filename);
this.readonly(HOST);
this.readonly(resolver);
this.readonly(this);

@@ -282,5 +313,37 @@ const {

console: consoleType,
vm: this,
resolver,
extensions
extensions,
emitArgs: (event, args) => {
if (typeof event !== 'string' && typeof event !== 'symbol') throw new Error('Event is not a string');
return this.emit(event, ...args);
},
globalPaths: [...resolver.globalPaths],
getLookupPathsFor: (path) => {
if (typeof path !== 'string') return [];
return [...resolver.genLookupPaths(path)];
},
resolve: (mod, id, opt, ext, direct) => {
if (typeof id !== 'string') throw new Error('Id is not a string');
const extList = Object.getOwnPropertyNames(ext);
return resolver.resolve(mod, id, makeSafeOptions(opt), extList, !!direct);
},
lookupPaths: (mod, id) => {
if (typeof id !== 'string') throw new Error('Id is not a string');
return [...resolver.lookupPaths(mod, id)];
},
loadBuiltinModule: (id) => {
if (typeof id !== 'string') throw new Error('Id is not a string');
return resolver.loadBuiltinModule(this, id);
},
registerModule: (mod, filename, path, parent, direct) => {
return resolver.registerModule(mod, filename, path, parent, direct);
},
builtinModules: [...resolver.getBuiltinModulesList(this)],
dirname: (path) => {
if (typeof path !== 'string') return path;
return resolver.fs.dirname(path);
},
basename: (path) => {
if (typeof path !== 'string') return path;
return resolver.fs.basename(path);
}
});

@@ -305,3 +368,3 @@

if (requireOpts && requireOpts.import) {
if (!customResolver && requireOpts && requireOpts.import) {
if (Array.isArray(requireOpts.import)) {

@@ -319,2 +382,10 @@ for (let i = 0, l = requireOpts.import.length; i < l; i++) {

* @ignore
* @deprecated
*/
get _resolver() {
return this.resolver;
}
/**
* @ignore
* @deprecated Just call the method yourself like <code>method(args);</code>

@@ -346,8 +417,8 @@ * @param {function} method - Function to invoke.

require(module) {
const path = this._resolver.pathResolve('.');
const path = this.resolver.fs.resolve('.');
let mod = this._cacheRequireModule;
if (!mod || mod.path !== path) {
const filename = this._resolver.pathConcat(path, '/vm.js');
const filename = this.resolver.fs.join(path, '/vm.js');
mod = new (this._Module)(filename, path);
this._resolver.registerModule(mod, filename, path, null, false);
this.resolver.registerModule(mod, filename, path, null, false);
this._cacheRequireModule = mod;

@@ -407,6 +478,6 @@ }

if (!sandboxModule) {
const resolvedFilename = this._resolver.pathResolve(code.filename);
dirname = this._resolver.pathDirname(resolvedFilename);
const resolvedFilename = this.resolver.fs.resolve(code.filename);
dirname = this.resolver.fs.dirname(resolvedFilename);
sandboxModule = new (this._Module)(resolvedFilename, dirname);
this._resolver.registerModule(sandboxModule, resolvedFilename, dirname, null, false);
this.resolver.registerModule(sandboxModule, resolvedFilename, dirname, null, false);
}

@@ -417,6 +488,6 @@ } else {

if (filename) {
const resolvedFilename = this._resolver.pathResolve(filename);
dirname = this._resolver.pathDirname(resolvedFilename);
const resolvedFilename = this.resolver.fs.resolve(filename);
dirname = this.resolver.fs.dirname(resolvedFilename);
sandboxModule = new (this._Module)(resolvedFilename, dirname);
this._resolver.registerModule(sandboxModule, resolvedFilename, dirname, null, false);
this.resolver.registerModule(sandboxModule, resolvedFilename, dirname, null, false);
} else {

@@ -511,3 +582,3 @@ sandboxModule = new (this._Module)(null, null);

function vm2NestingLoader(resolver, vm, id) {
function vm2NestingLoader(vm) {
if (!cacheMakeNestingScript) {

@@ -514,0 +585,0 @@ cacheMakeNestingScript = compileScript('nesting.js', '(vm, nodevm) => ({VM: vm, NodeVM: nodevm})');

'use strict';
// Translate the old options to the new Resolver functionality.
const fs = require('fs');
const nmod = require('module');
const {EventEmitter} = require('events');
const util = require('util');
const {

@@ -14,6 +8,6 @@ Resolver,

} = require('./resolver');
const {VMScript} = require('./script');
const {VM} = require('./vm');
const {VMError} = require('./bridge');
const {DefaultFileSystem} = require('./filesystem');
const {makeBuiltinsFromLegacyOptions} = require('./builtin');
const {jsCompiler} = require('./compiler');

@@ -48,7 +42,62 @@ /**

class LegacyResolver extends DefaultResolver {
class CustomResolver extends DefaultResolver {
constructor(fileSystem, builtinModules, checkPath, globalPaths, pathContext, customResolver, hostRequire, compiler, strict, externals, allowTransitive) {
super(fileSystem, builtinModules, checkPath, globalPaths, pathContext, customResolver, hostRequire, compiler, strict);
this.externals = externals;
constructor(fileSystem, globalPaths, builtinModules, rootPaths, pathContext, customResolver, hostRequire, compiler, strict) {
super(fileSystem, globalPaths, builtinModules);
this.rootPaths = rootPaths;
this.pathContext = pathContext;
this.customResolver = customResolver;
this.hostRequire = hostRequire;
this.compiler = compiler;
this.strict = strict;
}
isPathAllowed(filename) {
return this.rootPaths === undefined || this.rootPaths.some(path => {
if (!filename.startsWith(path)) return false;
const len = path.length;
if (filename.length === len || (len > 0 && this.fs.isSeparator(path[len-1]))) return true;
return this.fs.isSeparator(filename[len]);
});
}
loadJS(vm, mod, filename) {
if (this.pathContext(filename, 'js') !== 'host') return super.loadJS(vm, mod, filename);
const m = this.hostRequire(filename);
mod.exports = vm.readonly(m);
}
loadNode(vm, mod, filename) {
if (this.pathContext(filename, 'node') !== 'host') return super.loadNode(vm, mod, filename);
const m = this.hostRequire(filename);
mod.exports = vm.readonly(m);
}
customResolve(x, path, extList) {
if (this.customResolver === undefined) return undefined;
const resolved = this.customResolver(x, path);
if (!resolved) return undefined;
if (typeof resolved === 'string') {
return this.loadAsFileOrDirectory(resolved, extList);
}
const {module=x, path: resolvedPath} = resolved;
return this.loadNodeModules(module, [resolvedPath], extList);
}
getCompiler(filename) {
return this.compiler;
}
isStrict(filename) {
return this.strict;
}
}
class LegacyResolver extends CustomResolver {
constructor(fileSystem, globalPaths, builtinModules, rootPaths, pathContext, customResolver, hostRequire, compiler, strict, externals, allowTransitive) {
super(fileSystem, globalPaths, builtinModules, rootPaths, pathContext, customResolver, hostRequire, compiler, strict);
this.externals = externals.map(makeExternalMatcher);
this.externalCache = externals.map(pattern => new RegExp(makeExternalMatcherRegex(pattern)));
this.currMod = undefined;

@@ -86,19 +135,17 @@ this.trustedMods = new WeakMap();

resolveFull(mod, x, options, ext, direct) {
resolveFull(mod, x, options, extList, direct) {
this.currMod = undefined;
if (!direct) return super.resolveFull(mod, x, options, ext, false);
if (!direct) return super.resolveFull(mod, x, options, extList, false);
const trustedMod = this.trustedMods.get(mod);
if (!trustedMod || mod.path !== trustedMod.path) return super.resolveFull(mod, x, options, ext, false);
if (!trustedMod || mod.path !== trustedMod.path) return super.resolveFull(mod, x, options, extList, false);
const paths = [...mod.paths];
if (paths.length === trustedMod.length) {
for (let i = 0; i < paths.length; i++) {
if (paths[i] !== trustedMod.paths[i]) {
return super.resolveFull(mod, x, options, ext, false);
}
if (paths.length !== trustedMod.paths.length) return super.resolveFull(mod, x, options, extList, false);
for (let i = 0; i < paths.length; i++) {
if (paths[i] !== trustedMod.paths[i]) {
return super.resolveFull(mod, x, options, extList, false);
}
}
const extCopy = Object.assign({__proto__: null}, ext);
try {
this.currMod = trustedMod;
return super.resolveFull(trustedMod, x, undefined, extCopy, true);
return super.resolveFull(trustedMod, x, options, extList, true);
} finally {

@@ -117,8 +164,6 @@ this.currMod = undefined;

loadJS(vm, mod, filename) {
filename = this.pathResolve(filename);
this.checkAccess(mod, filename);
if (this.pathContext(filename, 'js') === 'sandbox') {
if (this.pathContext(filename, 'js') !== 'host') {
const trustedMod = this.trustedMods.get(mod);
const script = this.readScript(filename);
vm.run(script, {filename, strict: true, module: mod, wrapper: 'none', dirname: trustedMod ? trustedMod.path : mod.path});
vm.run(script, {filename, strict: this.isStrict(filename), module: mod, wrapper: 'none', dirname: trustedMod ? trustedMod.path : mod.path});
} else {

@@ -130,154 +175,29 @@ const m = this.hostRequire(filename);

}
function defaultBuiltinLoader(resolver, vm, id) {
const mod = resolver.hostRequire(id);
return vm.readonly(mod);
}
const eventsModules = new WeakMap();
function defaultBuiltinLoaderEvents(resolver, vm, id) {
return eventsModules.get(vm);
}
let cacheBufferScript;
function defaultBuiltinLoaderBuffer(resolver, vm, id) {
if (!cacheBufferScript) {
cacheBufferScript = new VMScript('return buffer=>({Buffer: buffer});', {__proto__: null, filename: 'buffer.js'});
}
const makeBuffer = vm.run(cacheBufferScript, {__proto__: null, strict: true, wrapper: 'none'});
return makeBuffer(Buffer);
}
let cacheUtilScript;
function defaultBuiltinLoaderUtil(resolver, vm, id) {
if (!cacheUtilScript) {
cacheUtilScript = new VMScript(`return function inherits(ctor, superCtor) {
ctor.super_ = superCtor;
Object.setPrototypeOf(ctor.prototype, superCtor.prototype);
}`, {__proto__: null, filename: 'util.js'});
}
const inherits = vm.run(cacheUtilScript, {__proto__: null, strict: true, wrapper: 'none'});
const copy = Object.assign({}, util);
copy.inherits = inherits;
return vm.readonly(copy);
}
const BUILTIN_MODULES = (nmod.builtinModules || Object.getOwnPropertyNames(process.binding('natives'))).filter(s=>!s.startsWith('internal/'));
let EventEmitterReferencingAsyncResourceClass = null;
if (EventEmitter.EventEmitterAsyncResource) {
// eslint-disable-next-line global-require
const {AsyncResource} = require('async_hooks');
const kEventEmitter = Symbol('kEventEmitter');
class EventEmitterReferencingAsyncResource extends AsyncResource {
constructor(ee, type, options) {
super(type, options);
this[kEventEmitter] = ee;
customResolve(x, path, extList) {
if (this.customResolver === undefined) return undefined;
if (!(this.pathIsAbsolute(x) || this.pathIsRelative(x))) {
if (!this.externalCache.some(regex => regex.test(x))) return undefined;
}
get eventEmitter() {
return this[kEventEmitter];
const resolved = this.customResolver(x, path);
if (!resolved) return undefined;
if (typeof resolved === 'string') {
this.externals.push(new RegExp('^' + escapeRegExp(resolved)));
return this.loadAsFileOrDirectory(resolved, extList);
}
const {module=x, path: resolvedPath} = resolved;
this.externals.push(new RegExp('^' + escapeRegExp(resolvedPath)));
return this.loadNodeModules(module, [resolvedPath], extList);
}
EventEmitterReferencingAsyncResourceClass = EventEmitterReferencingAsyncResource;
}
let cacheEventsScript;
const SPECIAL_MODULES = {
events(vm) {
if (!cacheEventsScript) {
const eventsSource = fs.readFileSync(`${__dirname}/events.js`, 'utf8');
cacheEventsScript = new VMScript(`(function (fromhost) { const module = {}; module.exports={};{ ${eventsSource}
} return module.exports;})`, {filename: 'events.js'});
}
const closure = VM.prototype.run.call(vm, cacheEventsScript);
const eventsInstance = closure(vm.readonly({
kErrorMonitor: EventEmitter.errorMonitor,
once: EventEmitter.once,
on: EventEmitter.on,
getEventListeners: EventEmitter.getEventListeners,
EventEmitterReferencingAsyncResource: EventEmitterReferencingAsyncResourceClass
}));
eventsModules.set(vm, eventsInstance);
vm._addProtoMapping(EventEmitter.prototype, eventsInstance.EventEmitter.prototype);
return defaultBuiltinLoaderEvents;
},
buffer(vm) {
return defaultBuiltinLoaderBuffer;
},
util(vm) {
return defaultBuiltinLoaderUtil;
}
};
function addDefaultBuiltin(builtins, key, vm) {
if (builtins[key]) return;
const special = SPECIAL_MODULES[key];
builtins[key] = special ? special(vm) : defaultBuiltinLoader;
}
function genBuiltinsFromOptions(vm, builtinOpt, mockOpt, override) {
const builtins = {__proto__: null};
if (mockOpt) {
const keys = Object.getOwnPropertyNames(mockOpt);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
builtins[key] = (resolver, tvm, id) => tvm.readonly(mockOpt[key]);
}
}
if (override) {
const keys = Object.getOwnPropertyNames(override);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
builtins[key] = override[key];
}
}
if (Array.isArray(builtinOpt)) {
const def = builtinOpt.indexOf('*') >= 0;
if (def) {
for (let i = 0; i < BUILTIN_MODULES.length; i++) {
const name = BUILTIN_MODULES[i];
if (builtinOpt.indexOf(`-${name}`) === -1) {
addDefaultBuiltin(builtins, name, vm);
}
}
} else {
for (let i = 0; i < BUILTIN_MODULES.length; i++) {
const name = BUILTIN_MODULES[i];
if (builtinOpt.indexOf(name) !== -1) {
addDefaultBuiltin(builtins, name, vm);
}
}
}
} else if (builtinOpt) {
for (let i = 0; i < BUILTIN_MODULES.length; i++) {
const name = BUILTIN_MODULES[i];
if (builtinOpt[name]) {
addDefaultBuiltin(builtins, name, vm);
}
}
}
return builtins;
}
function defaultCustomResolver() {
return undefined;
}
const DEFAULT_FS = new DefaultFileSystem();
const DENY_RESOLVER = new Resolver(DEFAULT_FS, {__proto__: null}, [], id => {
throw new VMError(`Access denied to require '${id}'`, 'EDENIED');
});
const DENY_RESOLVER = new Resolver(DEFAULT_FS, [], new Map());
function resolverFromOptions(vm, options, override, compiler) {
function makeResolverFromLegacyOptions(options, override, compiler) {
if (!options) {
if (!override) return DENY_RESOLVER;
const builtins = genBuiltinsFromOptions(vm, undefined, undefined, override);
return new Resolver(DEFAULT_FS, builtins, [], defaultRequire);
const builtins = makeBuiltinsFromLegacyOptions(undefined, defaultRequire, undefined, override);
return new Resolver(DEFAULT_FS, [], builtins);
}

@@ -297,50 +217,18 @@

const builtins = genBuiltinsFromOptions(vm, builtinOpt, mockOpt, override);
const builtins = makeBuiltinsFromLegacyOptions(builtinOpt, hostRequire, mockOpt, override);
if (!externalOpt) return new Resolver(fsOpt, builtins, [], hostRequire);
if (!externalOpt) return new Resolver(fsOpt, [], builtins);
let checkPath;
if (rootPaths) {
const checkedRootPaths = (Array.isArray(rootPaths) ? rootPaths : [rootPaths]).map(f => fsOpt.resolve(f));
checkPath = (filename) => {
return checkedRootPaths.some(path => {
if (!filename.startsWith(path)) return false;
const len = path.length;
if (filename.length === len || (len > 0 && fsOpt.isSeparator(path[len-1]))) return true;
return fsOpt.isSeparator(filename[len]);
});
};
} else {
checkPath = () => true;
}
if (!compiler) compiler = jsCompiler;
let newCustomResolver = defaultCustomResolver;
let externals = undefined;
let external = undefined;
if (customResolver) {
let externalCache;
newCustomResolver = (resolver, x, path, extList) => {
if (external && !(resolver.pathIsAbsolute(x) || resolver.pathIsRelative(x))) {
if (!externalCache) {
externalCache = external.map(ext => new RegExp(makeExternalMatcherRegex(ext)));
}
if (!externalCache.some(regex => regex.test(x))) return undefined;
}
const resolved = customResolver(x, path);
if (!resolved) return undefined;
if (typeof resolved === 'string') {
if (externals) externals.push(new RegExp('^' + escapeRegExp(resolved)));
return resolver.loadAsFileOrDirectory(resolved, extList);
}
const {module=x, path: resolvedPath} = resolved;
if (externals) externals.push(new RegExp('^' + escapeRegExp(resolvedPath)));
return resolver.loadNodeModules(module, [resolvedPath], extList);
};
}
const checkedRootPaths = rootPaths ? (Array.isArray(rootPaths) ? rootPaths : [rootPaths]).map(f => fsOpt.resolve(f)) : undefined;
const pathContext = typeof context === 'function' ? context : (() => context);
if (typeof externalOpt !== 'object') {
return new DefaultResolver(fsOpt, builtins, checkPath, [], () => context, newCustomResolver, hostRequire, compiler, strict);
return new CustomResolver(fsOpt, [], builtins, checkedRootPaths, pathContext, customResolver, hostRequire, compiler, strict);
}
let transitive = false;
let external = undefined;
if (Array.isArray(externalOpt)) {

@@ -350,8 +238,7 @@ external = externalOpt;

external = externalOpt.modules;
transitive = context === 'sandbox' && externalOpt.transitive;
transitive = context !== 'host' && externalOpt.transitive;
}
externals = external.map(makeExternalMatcher);
return new LegacyResolver(fsOpt, builtins, checkPath, [], () => context, newCustomResolver, hostRequire, compiler, strict, externals, transitive);
return new LegacyResolver(fsOpt, [], builtins, checkedRootPaths, pathContext, customResolver, hostRequire, compiler, strict, external, transitive);
}
exports.resolverFromOptions = resolverFromOptions;
exports.makeResolverFromLegacyOptions = makeResolverFromLegacyOptions;

@@ -9,2 +9,3 @@ 'use strict';

const { VMScript } = require('./script');
const { jsCopmiler } = require('./compiler');

@@ -25,7 +26,6 @@ // This should match. Note that '\', '%' are invalid characters

constructor(fs, builtinModules, globalPaths, hostRequire) {
constructor(fs, globalPaths, builtins) {
this.fs = fs;
this.builtinModules = builtinModules;
this.globalPaths = globalPaths;
this.hostRequire = hostRequire;
this.builtins = builtins;
}

@@ -37,6 +37,12 @@

pathResolve(path) {
return this.fs.resolve(path);
isPathAllowed(path) {
return false;
}
checkAccess(mod, filename) {
if (!this.isPathAllowed(filename)) {
throw new VMError(`Module '${filename}' is not allowed to be required. The path is outside the border!`, 'EDENIED');
}
}
pathIsRelative(path) {

@@ -54,29 +60,43 @@ if (path === '' || path[0] !== '.') return false;

pathConcat(...paths) {
return this.fs.join(...paths);
lookupPaths(mod, id) {
if (this.pathIsRelative(id)) return [mod.path || '.'];
return [...mod.paths, ...this.globalPaths];
}
pathBasename(path) {
return this.fs.basename(path);
getBuiltinModulesList(vm) {
if (this.builtins === undefined) return [];
const res = [];
this.builtins.forEach((value, key) => {
if (typeof value === 'object') value.init(vm);
res.push(key);
});
return res;
}
pathDirname(path) {
return this.fs.dirname(path);
loadBuiltinModule(vm, id) {
if (this.builtins === undefined) return undefined;
const builtin = this.builtins.get(id);
if (!builtin) return undefined;
if (typeof builtin === 'function') return builtin(vm);
return builtin.load(vm);
}
lookupPaths(mod, id) {
if (typeof id === 'string') throw new Error('Id is not a string');
if (this.pathIsRelative(id)) return [mod.path || '.'];
return [...mod.paths, ...this.globalPaths];
makeExtensionHandler(vm, name) {
return (mod, filename) => {
filename = this.fs.resolve(filename);
this.checkAccess(mod, filename);
this[name](vm, mod, filename);
};
}
getBuiltinModulesList() {
return Object.getOwnPropertyNames(this.builtinModules);
getExtensions(vm) {
return {
// eslint-disable-next-line quote-props
__proto__: null,
'.js': this.makeExtensionHandler(vm, 'loadJS'),
'.json': this.makeExtensionHandler(vm, 'loadJSON'),
' .node': this.makeExtensionHandler(vm, 'loadNode'),
};
}
loadBuiltinModule(vm, id) {
const handler = this.builtinModules[id];
return handler && handler(this, vm, id);
}
loadJS(vm, mod, filename) {

@@ -98,6 +118,4 @@ throw new VMError(`Access denied to require '${filename}'`, 'EDENIED');

resolve(mod, x, options, ext, direct) {
if (typeof x !== 'string') throw new Error('Id is not a string');
if (x.startsWith('node:') || this.builtinModules[x]) {
resolve(mod, x, options, extList, direct) {
if (x.startsWith('node:') || this.builtins.has(x)) {
// a. return the core module

@@ -108,6 +126,6 @@ // b. STOP

return this.resolveFull(mod, x, options, ext, direct);
return this.resolveFull(mod, x, options, extList, direct);
}
resolveFull(mod, x, options, ext, direct) {
resolveFull(mod, x, options, extList, direct) {
// 7. THROW "not found"

@@ -125,3 +143,3 @@ throw new VMError(`Cannot find module '${x}'`, 'ENOTFOUND');

while (true) {
const name = this.pathBasename(path);
const name = this.fs.basename(path);
// a. if PARTS[I] = "node_modules" CONTINUE

@@ -131,5 +149,5 @@ if (name !== 'node_modules') {

// c. DIRS = DIR + DIRS // Note: this seems wrong. Should be DIRS + DIR
dirs.push(this.pathConcat(path, 'node_modules'));
dirs.push(this.fs.join(path, 'node_modules'));
}
const dir = this.pathDirname(path);
const dir = this.fs.dirname(path);
if (dir == path) break;

@@ -147,50 +165,49 @@ // d. let I = I - 1

class DefaultResolver extends Resolver {
constructor(fs, builtinModules, checkPath, globalPaths, pathContext, customResolver, hostRequire, compiler, strict) {
super(fs, builtinModules, globalPaths, hostRequire);
this.checkPath = checkPath;
this.pathContext = pathContext;
this.customResolver = customResolver;
this.compiler = compiler;
this.strict = strict;
this.packageCache = {__proto__: null};
this.scriptCache = {__proto__: null};
function pathTestIsDirectory(fs, path) {
try {
const stat = fs.statSync(path, {__proto__: null, throwIfNoEntry: false});
return stat && stat.isDirectory();
} catch (e) {
return false;
}
}
isPathAllowed(path) {
return this.checkPath(path);
function pathTestIsFile(fs, path) {
try {
const stat = fs.statSync(path, {__proto__: null, throwIfNoEntry: false});
return stat && stat.isFile();
} catch (e) {
return false;
}
}
pathTestIsDirectory(path) {
try {
const stat = this.fs.statSync(path, {__proto__: null, throwIfNoEntry: false});
return stat && stat.isDirectory();
} catch (e) {
return false;
}
}
function readFile(fs, path) {
return fs.readFileSync(path, {encoding: 'utf8'});
}
pathTestIsFile(path) {
try {
const stat = this.fs.statSync(path, {__proto__: null, throwIfNoEntry: false});
return stat && stat.isFile();
} catch (e) {
return false;
}
function readFileWhenExists(fs, path) {
return pathTestIsFile(fs, path) ? readFile(fs, path) : undefined;
}
class DefaultResolver extends Resolver {
constructor(fs, globalPaths, builtins) {
super(fs, globalPaths, builtins);
this.packageCache = new Map();
this.scriptCache = new Map();
}
readFile(path) {
return this.fs.readFileSync(path, {encoding: 'utf8'});
getCompiler(filename) {
return jsCopmiler;
}
readFileWhenExists(path) {
return this.pathTestIsFile(path) ? this.readFile(path) : undefined;
isStrict(filename) {
return true;
}
readScript(filename) {
let script = this.scriptCache[filename];
let script = this.scriptCache.get(filename);
if (!script) {
script = new VMScript(this.readFile(filename), {filename, compiler: this.compiler});
this.scriptCache[filename] = script;
script = new VMScript(readFile(this.fs, filename), {filename, compiler: this.getCompiler(filename)});
this.scriptCache.set(filename, script);
}

@@ -200,14 +217,8 @@ return script;

checkAccess(mod, filename) {
if (!this.isPathAllowed(filename)) {
throw new VMError(`Module '${filename}' is not allowed to be required. The path is outside the border!`, 'EDENIED');
}
}
loadJS(vm, mod, filename) {
filename = this.pathResolve(filename);
this.checkAccess(mod, filename);
if (this.pathContext(filename, 'js') === 'sandbox') {
if (this.pathContext(filename, 'js') !== 'host') {
const script = this.readScript(filename);
vm.run(script, {filename, strict: this.strict, module: mod, wrapper: 'none', dirname: mod.path});
vm.run(script, {filename, strict: this.isStrict(filename), module: mod, wrapper: 'none', dirname: mod.path});
} else {

@@ -220,5 +231,3 @@ const m = this.hostRequire(filename);

loadJSON(vm, mod, filename) {
filename = this.pathResolve(filename);
this.checkAccess(mod, filename);
const json = this.readFile(filename);
const json = readFile(this.fs, filename);
mod.exports = vm._jsonParse(json);

@@ -230,3 +239,3 @@ }

this.checkAccess(mod, filename);
if (this.pathContext(filename, 'node') === 'sandbox') throw new VMError('Native modules can be required only with context set to \'host\'.');
if (this.pathContext(filename, 'node') !== 'host') throw new VMError('Native modules can be required only with context set to \'host\'.');
const m = this.hostRequire(filename);

@@ -236,7 +245,10 @@ mod.exports = vm.readonly(m);

customResolve(x, path, extList) {
return undefined;
}
// require(X) from module at path Y
resolveFull(mod, x, options, ext, direct) {
resolveFull(mod, x, options, extList, direct) {
// Note: core module handled by caller
const extList = Object.getOwnPropertyNames(ext);
const path = mod.path || '.';

@@ -272,3 +284,3 @@

// b. LOAD_AS_DIRECTORY(Y + X)
f = this.loadAsFileOrDirectory(this.pathConcat(paths[i], x), extList);
f = this.loadAsFileOrDirectory(this.fs.join(paths[i], x), extList);
if (f) return f;

@@ -279,3 +291,3 @@ }

// b. LOAD_AS_DIRECTORY(Y + X)
f = this.loadAsFileOrDirectory(this.pathConcat(path, x), extList);
f = this.loadAsFileOrDirectory(this.fs.join(path, x), extList);
if (f) return f;

@@ -288,3 +300,3 @@ } else {

// b. LOAD_AS_DIRECTORY(Y + X)
f = this.loadAsFileOrDirectory(this.pathConcat(path, x), extList);
f = this.loadAsFileOrDirectory(this.fs.join(path, x), extList);
if (f) return f;

@@ -328,6 +340,6 @@ }

f = this.customResolver(this, x, path, extList);
f = this.customResolve(x, path, extList);
if (f) return f;
return super.resolveFull(mod, x, options, ext, direct);
return super.resolveFull(mod, x, options, extList, direct);
}

@@ -344,4 +356,4 @@

tryFile(x) {
x = this.pathResolve(x);
return this.isPathAllowed(x) && this.pathTestIsFile(x) ? x : undefined;
x = this.fs.resolve(x);
return this.isPathAllowed(x) && pathTestIsFile(this.fs, x) ? x : undefined;
}

@@ -352,3 +364,3 @@

const ext = extList[i];
if (ext !== this.pathBasename(ext)) continue;
if (ext !== this.fs.basename(ext)) continue;
const f = this.tryFile(x + ext);

@@ -361,11 +373,11 @@ if (f) return f;

readPackage(path) {
const packagePath = this.pathResolve(this.pathConcat(path, 'package.json'));
const packagePath = this.fs.resolve(this.fs.join(path, 'package.json'));
const cache = this.packageCache[packagePath];
const cache = this.packageCache.get(packagePath);
if (cache !== undefined) return cache;
if (!this.isPathAllowed(packagePath)) return undefined;
const content = this.readFileWhenExists(packagePath);
const content = readFileWhenExists(this.fs, packagePath);
if (!content) {
this.packageCache[packagePath] = false;
this.packageCache.set(packagePath, false);
return false;

@@ -390,3 +402,3 @@ }

};
this.packageCache[packagePath] = filtered;
this.packageCache.set(packagePath, filtered);
return filtered;

@@ -397,5 +409,5 @@ }

while (true) {
const dir = this.pathDirname(path);
const dir = this.fs.dirname(path);
if (dir === path) break;
const basename = this.pathBasename(dir);
const basename = this.fs.basename(dir);
if (basename === 'node_modules') break;

@@ -425,3 +437,3 @@ const pack = this.readPackage(dir);

// 3. If X/index.node is a file, load X/index.node as binary addon. STOP
return this.tryWithExtension(this.pathConcat(x, 'index'), extList);
return this.tryWithExtension(this.fs.join(x, 'index'), extList);
}

@@ -438,3 +450,3 @@

// c. let M = X + (json main field)
const m = this.pathConcat(x, pack.main);
const m = this.fs.join(x, pack.main);
// d. LOAD_AS_FILE(M)

@@ -534,3 +546,3 @@ let f = this.loadAsFile(m, extList);

if (!res) return undefined;
const scope = this.pathConcat(dir, res[1]);
const scope = this.fs.join(dir, res[1]);
const pack = this.readPackage(scope);

@@ -735,3 +747,3 @@ if (!pack) return undefined;

// b. Return PACKAGE_RESOLVE(target + subpath, packageURL + "/").
return this.packageResolve(this.pathConcat(target, subpath), packageURL, conditions, extList);
return this.packageResolve(this.fs.join(target, subpath), packageURL, conditions, extList);
}

@@ -749,3 +761,3 @@ }

// d. Let resolvedTarget be the URL resolution of the concatenation of packageURL and target.
const resolvedTarget = this.pathConcat(packageURL, target);
const resolvedTarget = this.fs.join(packageURL, target);
// e. Assert: resolvedTarget is contained in packageURL.

@@ -765,3 +777,3 @@ subpath = decodeURI(subpath);

// 1. Return the URL resolution of the concatenation of subpath and resolvedTarget.
return this.pathConcat(resolvedTarget, subpath);
return this.fs.join(resolvedTarget, subpath);
// 3. Otherwise, if target is an Array, then

@@ -837,3 +849,3 @@ } else if (Array.isArray(target)) {

// 3. If packageSpecifier is a Node.js builtin module name, then
if (this.builtinModules[packageSpecifier]) {
if (this.builtins.has(packageSpecifier)) {
// a. Return the string "node:" concatenated with packageSpecifier.

@@ -877,7 +889,7 @@ return 'node:' + packageSpecifier;

// a. Let packageURL be the URL resolution of "node_modules/" concatenated with packageSpecifier, relative to parentURL.
packageURL = this.pathResolve(this.pathConcat(parentURL, 'node_modules', packageSpecifier));
packageURL = this.fs.resolve(this.fs.join(parentURL, 'node_modules', packageSpecifier));
// b. Set parentURL to the parent folder URL of parentURL.
const parentParentURL = this.pathDirname(parentURL);
const parentParentURL = this.fs.dirname(parentURL);
// c. If the folder at packageURL does not exist, then
if (this.isPathAllowed(packageURL) && this.pathTestIsDirectory(packageURL)) break;
if (this.isPathAllowed(packageURL) && pathTestIsDirectory(this.fs, packageURL)) break;
// 1. Continue the next loop iteration.

@@ -905,3 +917,3 @@ if (parentParentURL === parentURL) {

// 1. Return the URL resolution of packageSubpath in packageURL.
return this.pathConcat(packageURL, packageSubpath);
return this.fs.join(packageURL, packageSubpath);
}

@@ -908,0 +920,0 @@

@@ -40,5 +40,13 @@ /* global host, data, VMError */

console: optionConsole,
vm,
resolver,
extensions
extensions,
emitArgs,
globalPaths,
getLookupPathsFor,
resolve: resolve0,
lookupPaths,
loadBuiltinModule,
registerModule,
builtinModules,
dirname,
basename
} = data;

@@ -50,4 +58,2 @@

const globalPaths = ensureSandboxArray(resolver.globalPaths);
class Module {

@@ -61,3 +67,3 @@

this.loaded = false;
this.paths = path ? ensureSandboxArray(resolver.genLookupPaths(path)) : [];
this.paths = path ? ensureSandboxArray(getLookupPathsFor(path)) : [];
this.children = [];

@@ -87,3 +93,3 @@ this.exports = {};

}
const filename = resolver.resolve(mod, id, undefined, Module._extensions, direct);
const filename = resolve0(mod, id, undefined, Module._extensions, direct);
if (localStringPrototypeStartsWith(filename, 'node:')) {

@@ -93,3 +99,3 @@ id = localStringPrototypeSlice(filename, 5);

if (!nmod) {
nmod = resolver.loadBuiltinModule(vm, id);
nmod = loadBuiltinModule(id);
if (!nmod) throw new VMError(`Cannot find module '${filename}'`, 'ENOTFOUND');

@@ -109,3 +115,3 @@ cacheBuiltins[id] = nmod;

if (nmod) return nmod;
nmod = resolver.loadBuiltinModule(vm, id);
nmod = loadBuiltinModule(id);
if (nmod) {

@@ -116,5 +122,5 @@ cacheBuiltins[id] = nmod;

const path = resolver.fs.dirname(filename);
const path = dirname(filename);
const module = new Module(filename, path, mod);
resolver.registerModule(module, filename, path, mod, direct);
registerModule(module, filename, path, mod, direct);
mod._updateChildren(module, true);

@@ -141,4 +147,4 @@ try {

Module.builtinModules = ensureSandboxArray(resolver.getBuiltinModulesList());
Module.globalPaths = globalPaths;
Module.builtinModules = ensureSandboxArray(builtinModules);
Module.globalPaths = ensureSandboxArray(globalPaths);
Module._extensions = {__proto__: null};

@@ -157,3 +163,3 @@ Module._cache = {__proto__: null};

function findBestExtensionHandler(filename) {
const name = resolver.fs.basename(filename);
const name = basename(filename);
for (let i = 0; (i = localStringPrototypeIndexOf(name, '.', i + 1)) !== -1;) {

@@ -177,7 +183,7 @@ const ext = localStringPrototypeSlice(name, i);

function resolve(id, options) {
return resolver.resolve(mod, id, options, Module._extensions, true);
return resolve0(mod, id, options, Module._extensions, true);
}
require.resolve = resolve;
function paths(id) {
return ensureSandboxArray(resolver.lookupPaths(mod, id));
return ensureSandboxArray(lookupPaths(mod, id));
}

@@ -290,16 +296,2 @@ resolve.paths = paths;

function vmEmitArgs(event, args) {
const allargs = [event];
for (let i = 0; i < args.length; i++) {
if (!localReflectDefineProperty(allargs, i + 1, {
__proto__: null,
value: args[i],
writable: true,
enumerable: true,
configurable: true
})) throw new LocalError('Unexpected');
}
return localReflectApply(vm.emit, vm, allargs);
}
const LISTENERS = new LocalWeakMap();

@@ -449,18 +441,18 @@ const LISTENER_HANDLER = new LocalWeakMap();

debug(...args) {
vmEmitArgs('console.debug', args);
emitArgs('console.debug', args);
},
log(...args) {
vmEmitArgs('console.log', args);
emitArgs('console.log', args);
},
info(...args) {
vmEmitArgs('console.info', args);
emitArgs('console.info', args);
},
warn(...args) {
vmEmitArgs('console.warn', args);
emitArgs('console.warn', args);
},
error(...args) {
vmEmitArgs('console.error', args);
emitArgs('console.error', args);
},
dir(...args) {
vmEmitArgs('console.dir', args);
emitArgs('console.dir', args);
},

@@ -470,3 +462,3 @@ time() {},

trace(...args) {
vmEmitArgs('console.trace', args);
emitArgs('console.trace', args);
}

@@ -473,0 +465,0 @@ };

@@ -24,2 +24,3 @@ /* global host, bridge, data, context */

apply: localReflectApply,
construct: localReflectConstruct,
deleteProperty: localReflectDeleteProperty,

@@ -436,2 +437,59 @@ has: localReflectHas,

function makeSafeHandlerArgs(args) {
const sArgs = ensureThis(args);
if (sArgs === args) return args;
const a = [];
for (let i=0; i < sArgs.length; i++) {
localReflectDefineProperty(a, i, {
__proto__: null,
value: sArgs[i],
enumerable: true,
configurable: true,
writable: true
});
}
return a;
}
const makeSafeArgs = Object.freeze({
__proto__: null,
apply(target, thiz, args) {
return localReflectApply(target, thiz, makeSafeHandlerArgs(args));
},
construct(target, args, newTarget) {
return localReflectConstruct(target, makeSafeHandlerArgs(args), newTarget);
}
});
const proxyHandlerHandler = Object.freeze({
__proto__: null,
get(target, name, receiver) {
const value = target.handler[name];
if (typeof value !== 'function') return value;
return new LocalProxy(value, makeSafeArgs);
}
});
function wrapProxyHandler(args) {
if (args.length < 2) return args;
const handler = args[1];
args[1] = new LocalProxy({__proto__: null, handler}, proxyHandlerHandler);
return args;
}
const proxyHandler = Object.freeze({
__proto__: null,
apply(target, thiz, args) {
return localReflectApply(target, thiz, wrapProxyHandler(args));
},
construct(target, args, newTarget) {
return localReflectConstruct(target, wrapProxyHandler(args), newTarget);
}
});
const proxiedProxy = new LocalProxy(LocalProxy, proxyHandler);
overrideWithProxy(LocalProxy, 'revocable', LocalProxy.revocable, proxyHandler);
global.Proxy = proxiedProxy;
global.Function = proxiedFunction;

@@ -438,0 +496,0 @@ global.eval = new LocalProxy(localEval, EvalHandler);

@@ -47,2 +47,5 @@ 'use strict';

} = require('./script');
const {
inspect
} = require('util');

@@ -369,2 +372,4 @@ const objectDefineProperties = Object.defineProperties;

this.readonly(inspect);
// prepare global sandbox

@@ -371,0 +376,0 @@ if (sandbox) {

@@ -16,3 +16,3 @@ {

],
"version": "3.9.17",
"version": "3.9.18",
"main": "index.js",

@@ -19,0 +19,0 @@ "sideEffects": false,

@@ -137,3 +137,3 @@ # vm2 [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![Package Quality][quality-image]][quality-url] [![Node.js CI](https://github.com/patriksimek/vm2/actions/workflows/node-test.yml/badge.svg)](https://github.com/patriksimek/vm2/actions/workflows/node-test.yml) [![Known Vulnerabilities][snyk-image]][snyk-url]

* `sourceExtensions` - Array of file extensions to treat as source code (default: `['js']`).
* `require` - `true` or object to enable `require` method (default: `false`).
* `require` - `true`, an object or a Resolver to enable `require` method (default: `false`).
* `require.external` - Values can be `true`, an array of allowed external modules, or an object (default: `false`). All paths matching `/node_modules/${any_allowed_external_module}/(?!/node_modules/)` are allowed to be required.

@@ -145,3 +145,3 @@ * `require.external.modules` - Array of allowed external modules. Also supports wildcards, so specifying `['@scope/*-ver-??]`, for instance, will allow using all modules having a name of the form `@scope/something-ver-aa`, `@scope/other-ver-11`, etc. The `*` wildcard does not match path separators.

* `require.mock` - Collection of mock modules (both external or built-in).
* `require.context` - `host` (default) to require modules in the host and proxy them into the sandbox. `sandbox` to load, compile, and require modules in the sandbox. Except for `events`, built-in modules are always required in the host and proxied into the sandbox.
* `require.context` - `host` (default) to require modules in the host and proxy them into the sandbox. `sandbox` to load, compile, and require modules in the sandbox. `callback(moduleFilename, ext)` to dynamically choose a context per module. The default will be sandbox is nothing is specified. Except for `events`, built-in modules are always required in the host and proxied into the sandbox.
* `require.import` - An array of modules to be loaded into NodeVM on start.

@@ -216,2 +216,24 @@ * `require.resolve` - An additional lookup function in case a module wasn't found in one of the traditional node lookup paths.

### Resolver
A resolver can be created via `makeResolverFromLegacyOptions` and be used for multiple `NodeVM` instances allowing to share compiled module code potentially speeding up load times. The first example of `NodeVM` can be rewritten using `makeResolverFromLegacyOptions` as follows.
```js
const resolver = makeResolverFromLegacyOptions({
external: true,
builtin: ['fs', 'path'],
root: './',
mock: {
fs: {
readFileSync: () => 'Nice try!'
}
}
});
const vm = new NodeVM({
console: 'inherit',
sandbox: {},
require: resolver
});
```
## VMScript

@@ -218,0 +240,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc