Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

vm2

Package Overview
Dependencies
Maintainers
2
Versions
65
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vm2 - npm Package Compare versions

Comparing version 3.8.4 to 3.9.0

lib/fixasync.js

10

CHANGELOG.md

@@ -0,1 +1,11 @@

v3.9.0 (2020-03-21)
-------------------
[new] Added vm.Script `lineOffset` and `columnOffset` options (azu)
[new] Allow to specify a compiler per VMScript (XmiliaH)
[new] Add option to disable async (XmiliaH)
[new] Added allot of jsdoc (XmiliaH)
[fix] Fix access to frozen or unconfigurable properties (XmiliaH)
[fix] Double wrap Objects to prevent breakout via inspect (XmiliaH)
[fix] Compile now compiles VM code (XmiliaH)
v3.8.4 (2019-09-13)

@@ -2,0 +12,0 @@ -------------------

97

index.d.ts

@@ -72,19 +72,40 @@ import {EventEmitter} from 'events';

/** File extensions that the internal module resolver should accept. */
sourceExtensions?: string[]
sourceExtensions?: string[];
}
/**
* A VM with behavior more similar to running inside Node.
* VM is a simple sandbox, without `require` feature, to synchronously run an untrusted code.
* Only JavaScript built-in objects + Buffer are available. Scheduling functions
* (`setInterval`, `setTimeout` and `setImmediate`) are not available by default.
*/
export class NodeVM extends EventEmitter {
constructor(options?: NodeVMOptions);
export class VM {
constructor(options?: VMOptions);
/** Direct access to the global sandbox object */
readonly sandbox: any;
/** Timeout to use for the run methods */
timeout?: number;
/** Runs the code */
run(js: string, path: string): any;
run(js: string, path?: string): any;
/** Runs the VMScript object */
run(script: VMScript, path?: string): any;
run(script: VMScript): any;
/** Runs the code in the specific file */
runFile(filename: string): any;
/** Loads all the values into the global object with the same names */
setGlobals(values: any): this;
/** Make a object visible as a global with a specific name */
setGlobal(name: string, value: any): this;
/** Get the global object with the specific name */
getGlobal(name: string): any;
/** Freezes the object inside VM making it read-only. Not available for primitive values. */
freeze(object: any, name?: string): any;
/** Protects the object inside VM making impossible to set functions as it's properties. Not available for primitive values */
protect(object: any, name?: string): any;
}
/** Freezes the object inside VM making it read-only. Not available for primitive values. */
freeze(object: any, name: string): any;
/** Protects the object inside VM making impossible to set functions as it's properties. Not available for primitive values. */
protect(object: any, name: string): any;
/**
* A VM with behavior more similar to running inside Node.
*/
export class NodeVM extends EventEmitter implements VM {
constructor(options?: NodeVMOptions);
/** Require a module in VM and return it's exports. */

@@ -100,3 +121,3 @@ require(module: string): any;

*/
static code(script: string, filename: string, options: NodeVMOptions): NodeVM;
static code(script: string, filename?: string, options?: NodeVMOptions): any;

@@ -109,20 +130,24 @@ /**

*/
static file(filename: string, options: NodeVMOptions): NodeVM
}
static file(filename: string, options?: NodeVMOptions): any;
/**
* VM is a simple sandbox, without `require` feature, to synchronously run an untrusted code.
* Only JavaScript built-in objects + Buffer are available. Scheduling functions
* (`setInterval`, `setTimeout` and `setImmediate`) are not available by default.
*/
export class VM {
constructor(options?: VMOptions);
/** Direct access to the global sandbox object */
readonly sandbox: any;
/** Only here because of implements VM. Does nothing. */
timeout?: number;
/** Runs the code */
run(js: string): any;
run(js: string, path?: string): any;
/** Runs the VMScript object */
run(script: VMScript): any;
/** Runs the code in the specific file */
runFile(filename: string): any;
/** Loads all the values into the global object with the same names */
setGlobals(values: any): this;
/** Make a object visible as a global with a specific name */
setGlobal(name: string, value: any): this;
/** Get the global object with the specific name */
getGlobal(name: string): any;
/** Freezes the object inside VM making it read-only. Not available for primitive values. */
freeze(object: any, name: string): any;
freeze(object: any, name?: string): any;
/** Protects the object inside VM making impossible to set functions as it's properties. Not available for primitive values */
protect(object: any, name: string): any;
protect(object: any, name?: string): any;
}

@@ -136,7 +161,25 @@

export class VMScript {
constructor(code: string, path?: string);
/** Wraps the code */
wrap(prefix: string, postfix: string): VMScript;
constructor(code: string, path: string, options?: {
lineOffset?: number;
columnOffset?: number;
compiler?: "javascript" | "coffeescript" | CompilerFunction;
});
constructor(code: string, options?: {
filename?: string,
lineOffset?: number;
columnOffset?: number;
compiler?: "javascript" | "coffeescript" | CompilerFunction;
});
readonly code: string;
readonly filename: string;
readonly lineOffset: number;
readonly columnOffset: number;
readonly compiler: "javascript" | "coffeescript" | CompilerFunction;
/**
* Wraps the code
* @deprecated
*/
wrap(prefix: string, postfix: string): this;
/** Compiles the code. If called multiple times, the code is only compiled once. */
compile(): any;
compile(): this;
}

@@ -143,0 +186,0 @@

@@ -60,2 +60,27 @@ /* global host */

const SHARED_ARROW = ()=>{};
function SHARED_FUNC() {}
const SHARED_ARRAY = [];
const SHARED_OBJECT = {__proto__: null};
function getBaseObject(obj) {
if (typeof obj === 'function') {
try {
// eslint-disable-next-line no-new
new new host.Proxy(obj, {
__proto__: null,
construct() {
return this;
}
})();
} catch (e) {
return SHARED_ARROW;
}
return SHARED_FUNC;
} else if (host.Array.isArray(obj)) {
return SHARED_ARRAY;
}
return SHARED_OBJECT;
}
/**

@@ -88,64 +113,2 @@ * VMError definition.

/*
* Proxy Helper
*
* Here we track Proxy creations so that we know for every proxy in the VM the
* target. If the Proxy is given to decontextify we are going to lookup
* the target and unsing this non proxy as target for the decontextify proxy.
*
*/
const ProxyHelper = host.Object.create(null);
// Marker for revoked proxy objects
ProxyHelper.revoked = 'Revoked';
// Tracks for every proxy the target.
ProxyHelper.tracker = new host.WeakMap();
// Gets the target of a proxy recursively until target is not any more a proxy
ProxyHelper.getTarget = (proxy) => {
let obj = proxy;
let next;
while ((next = ProxyHelper.tracker.get(obj))!==undefined) {
obj = next;
}
// Target could be revoked.
if (obj === ProxyHelper.revoked) {
obj = host.Object.create(null);
}
return obj;
};
// This is not so nice, I would prefer globalThis.Proxy but globalThis is relatively new
Proxy = ((ProxyFunc) => {
// Handle Proxy.revocable()
const ProxyRevocableHandler = host.Object.create(null);
ProxyRevocableHandler.apply = (target, thiz, args) => {
const proxyTarget = args[0];
const ret = local.Reflect.apply(target, thiz, args);
const proxy = ret.proxy;
ProxyHelper.tracker.set(proxy, proxyTarget);
const revokeHandler = host.Object.create(null);
revokeHandler.apply = (rTarget, rThiz, rArgs) => {
const rRet = local.Reflect.apply(rTarget, rThiz, rArgs);
ProxyHelper.tracker.set(proxy, ProxyHelper.revoked);
return rRet;
};
ret.revoke = new host.Proxy(ret.revoke, revokeHandler);
return ret;
};
ProxyFunc.revocable = new host.Proxy(Proxy.revocable, ProxyRevocableHandler);
// Handle new Proxy()
const ProxyHandler = host.Object.create(null);
ProxyHandler.construct = (target, args, newTarget) => {
const proxyTarget = args[0];
const proxy = local.Reflect.construct(target, args, newTarget);
ProxyHelper.tracker.set(proxy, proxyTarget);
return proxy;
};
return new host.Proxy(ProxyFunc, ProxyHandler);
})(Proxy);
/**

@@ -402,6 +365,37 @@ * Decontextify.

const proxy = new host.Proxy(ProxyHelper.getTarget(object), host.Object.assign(base, traps, deepTraps));
Decontextify.proxies.set(object, proxy);
host.Object.assign(base, traps, deepTraps);
let shallow;
if (host.Array.isArray(object)) {
const origGet = base.get;
shallow = {
__proto__: null,
ownKeys: base.ownKeys,
get: origGet
};
base.ownKeys = target => {
try {
const keys = local.Reflect.ownKeys(object);
// Do this hack so that console.log(decontextify([1,2,3])) doesn't write the properties twice
// a la [1,2,3,'0':1,'1':2,'2':3]
return Decontextify.value(keys.filter(key=>typeof key!=='string' || !key.match(/^\d+$/)));
} catch (e) {
throw Decontextify.value(e);
}
};
base.get = (target, key, receiver) => {
if (key === host.Symbol.toStringTag) return;
return origGet(target, key, receiver);
};
} else {
shallow = SHARED_OBJECT;
}
const proxy = new host.Proxy(getBaseObject(object), base);
Decontextified.set(proxy, object);
return proxy;
// We need two proxys since nodes inspect just removes one.
const proxy2 = new host.Proxy(proxy, shallow);
Decontextify.proxies.set(object, proxy2);
Decontextified.set(proxy2, object);
return proxy2;
};

@@ -422,5 +416,5 @@ Decontextify.value = (value, traps, deepTraps, flags, mock) => {

return null;
} else if (instanceOf(value, Number)) { return host.Number(value);
} else if (instanceOf(value, String)) { return host.String(value);
} else if (instanceOf(value, Boolean)) { return host.Boolean(value);
} else if (instanceOf(value, Number)) { return Decontextify.instance(value, host.Number, deepTraps, flags, 'Number');
} else if (instanceOf(value, String)) { return Decontextify.instance(value, host.String, deepTraps, flags, 'String');
} else if (instanceOf(value, Boolean)) { return Decontextify.instance(value, host.Boolean, deepTraps, flags, 'Boolean');
} else if (instanceOf(value, Date)) { return Decontextify.instance(value, host.Date, deepTraps, flags, 'Date');

@@ -731,3 +725,3 @@ } else if (instanceOf(value, RangeError)) { return Decontextify.instance(value, host.RangeError, deepTraps, flags, 'Error');

const proxy = new host.Proxy(object, host.Object.assign(base, traps, deepTraps));
const proxy = new host.Proxy(getBaseObject(object), host.Object.assign(base, traps, deepTraps));
Contextify.proxies.set(object, proxy);

@@ -751,5 +745,5 @@ Contextified.set(proxy, object);

return null;
} else if (instanceOf(value, host.Number)) { return host.Number(value);
} else if (instanceOf(value, host.String)) { return host.String(value);
} else if (instanceOf(value, host.Boolean)) { return host.Boolean(value);
} else if (instanceOf(value, host.Number)) { return Contextify.instance(value, Number, deepTraps, flags, 'Number');
} else if (instanceOf(value, host.String)) { return Contextify.instance(value, String, deepTraps, flags, 'String');
} else if (instanceOf(value, host.Boolean)) { return Contextify.instance(value, Boolean, deepTraps, flags, 'Boolean');
} else if (instanceOf(value, host.Date)) { return Contextify.instance(value, Date, deepTraps, flags, 'Date');

@@ -791,5 +785,18 @@ } else if (instanceOf(value, host.RangeError)) { return Contextify.instance(value, RangeError, deepTraps, flags, 'Error');

};
Contextify.globalValue = (value, name) => {
return (global[name] = Contextify.value(value));
Contextify.setGlobal = (name, value) => {
const prop = Contextify.value(name);
try {
global[prop] = Contextify.value(value);
} catch (e) {
throw Decontextify.value(e);
}
};
Contextify.getGlobal = (name) => {
const prop = Contextify.value(name);
try {
return Decontextify.value(global[prop]);
} catch (e) {
throw Decontextify.value(e);
}
};
Contextify.readonly = (value, mock) => {

@@ -801,2 +808,7 @@ return Contextify.value(value, null, FROZEN_TRAPS, null, mock);

};
Contextify.connect = (outer, inner) => {
Decontextified.set(outer, inner);
Contextified.set(inner, outer);
};
Contextify.makeModule = ()=>({exports: {}});

@@ -810,5 +822,16 @@ const BufferMock = host.Object.create(null);

};
const BufferOverride = host.Object.create(null);
BufferOverride.inspect = function inspect(recurseTimes, ctx) {
// Mimic old behavior, could throw but didn't pass a test.
const max = host.INSPECT_MAX_BYTES;
const actualMax = Math.min(max, this.length);
const remaining = this.length - max;
let str = this.hexSlice(0, actualMax).replace(/(.{2})/g, '$1 ').trim();
if (remaining > 0) str += ` ... ${remaining} more byte${remaining > 1 ? 's' : ''}`;
return `<${this.constructor.name} ${str}>`;
};
const LocalBuffer = global.Buffer = Contextify.readonly(host.Buffer, BufferMock);
Contextify.connect(host.Buffer.prototype.inspect, BufferOverride.inspect);
const exportsMap = host.Object.create(null);

@@ -818,3 +841,4 @@ exportsMap.Contextify = Contextify;

exportsMap.Buffer = LocalBuffer;
exportsMap.sandbox = Decontextify.value(global);
return exportsMap;

@@ -5,2 +5,20 @@ /* eslint-disable global-require, no-use-before-define */

/**
* This callback will be called to transform a script to JavaScript.
*
* @callback compileCallback
* @param {string} code - Script code to transform to JavaScript.
* @param {string} filename - Filename of this script.
* @return {string} JavaScript code that represents the script code.
*/
/**
* This callback will be called to resolve a module if it couldn't be found.
*
* @callback resolveCallback
* @param {string} moduleName - Name of the module to resolve.
* @param {string} dirname - Name of the current directory.
* @return {(string|undefined)} The file or directory to use to load the requested module.
*/
const fs = require('fs');

@@ -10,6 +28,96 @@ const vm = require('vm');

const {EventEmitter} = require('events');
const {INSPECT_MAX_BYTES} = require('buffer');
const _compileToJS = function compileToJS(code, compiler, filename) {
if ('function' === typeof compiler) return compiler(code, filename);
/**
* Load a script from a file and compile it.
*
* @private
* @param {string} filename - File to load and compile to a script.
* @param {string} prefix - Prefix for the script.
* @param {string} suffix - Suffix for the script.
* @return {vm.Script} The compiled script.
*/
function loadAndCompileScript(filename, prefix, suffix) {
const data = fs.readFileSync(filename, 'utf8');
return new vm.Script(prefix + data + suffix, {
filename,
displayErrors: false
});
}
/**
* Cache where we can cache some things
*
* @private
* @property {?compileCallback} coffeeScriptCompiler - The coffee script compiler or null if not yet used.
* @property {?Object} timeoutContext - The context used for the timeout functionality of null if not yet used.
* @property {?vm.Script} timeoutScript - The compiled script used for the timeout functionality of null if not yet used.
* @property {vm.Script} contextifyScript - The compiled script used to setup a sandbox.
* @property {?vm.Script} sandboxScript - The compiled script used to setup the NodeVM require mechanism of null if not yet used.
*/
const CACHE = {
coffeeScriptCompiler: null,
timeoutContext: null,
timeoutScript: null,
contextifyScript: loadAndCompileScript(`${__dirname}/contextify.js`, '(function(require, host) { ', '\n})'),
sandboxScript: null,
fixAsyncScript: null,
getGlobalScript: null,
getGeneratorFunctionScript: null,
getAsyncFunctionScript: null,
getAsyncGeneratorFunctionScript: null,
};
/**
* Default run options for vm.Script.runInContext
*
* @private
*/
const DEFAULT_RUN_OPTIONS = {displayErrors: false};
/**
* Returns the cached coffee script compiler or loads it
* if it is not found in the cache.
*
* @private
* @return {compileCallback} The coffee script compiler.
* @throws {VMError} If the coffee-script module can't be found.
*/
function getCoffeeScriptCompiler() {
if (!CACHE.coffeeScriptCompiler) {
try {
const coffeeScript = require('coffee-script');
CACHE.coffeeScriptCompiler = (code, filename) => {
return coffeeScript.compile(code, {header: false, bare: true});
};
} catch (e) {
throw new VMError('Coffee-Script compiler is not installed.');
}
}
return CACHE.coffeeScriptCompiler;
}
/**
* The JavaScript compiler, just a identity function.
*
* @private
* @type {compileCallback}
* @param {string} code - The JavaScript code.
* @param {string} filename - Filename of this script.
* @return {string} The code.
*/
function jsCompiler(code, filename) {
return code;
}
/**
* Look up the compiler for a specific name.
*
* @private
* @param {(string|compileCallback)} compiler - A compile callback or the name of the compiler.
* @return {compileCallback} The resolved compiler.
* @throws {VMError} If the compiler is unknown or the coffee script module was needed and couldn't be found.
*/
function lookupCompiler(compiler) {
if ('function' === typeof compiler) return compiler;
switch (compiler) {

@@ -20,8 +128,3 @@ case 'coffeescript':

case 'text/coffeescript':
try {
return require('coffee-script').compile(code, {header: false, bare: true});
} catch (ex) {
throw new VMError('Coffee-Script compiler is not installed.');
}
return getCoffeeScriptCompiler();
case 'javascript':

@@ -31,8 +134,7 @@ case 'java-script':

case 'text/javascript':
return code;
return jsCompiler;
default:
throw new VMError(`Unsupported compiler '${compiler}'.`);
}
};
}

@@ -42,33 +144,219 @@ /**

*
* @class
* @public
*/
class VMScript {
class VMScript {
/**
* The script code with wrapping. If set will invalidate the cache.<br>
* Writable only for backwards compatibility.
*
* @public
* @readonly
* @member {string} code
* @memberOf VMScript#
*/
/**
* The filename used for this script.
*
* @public
* @readonly
* @since v3.8.5
* @member {string} filename
* @memberOf VMScript#
*/
/**
* The line offset use for stack traces.
*
* @public
* @readonly
* @since v3.8.5
* @member {number} lineOffset
* @memberOf VMScript#
*/
/**
* The column offset use for stack traces.
*
* @public
* @readonly
* @since v3.8.5
* @member {number} columnOffset
* @memberOf VMScript#
*/
/**
* The compiler to use to get the JavaScript code.
*
* @public
* @readonly
* @since v3.8.5
* @member {(string|compileCallback)} compiler
* @memberOf VMScript#
*/
/**
* The prefix for the script.
*
* @private
* @member {string} _prefix
* @memberOf VMScript#
*/
/**
* The suffix for the script.
*
* @private
* @member {string} _suffix
* @memberOf VMScript#
*/
/**
* The compiled vm.Script for the VM or if not compiled <code>null</code>.
*
* @private
* @member {?vm.Script} _compiledVM
* @memberOf VMScript#
*/
/**
* The compiled vm.Script for the NodeVM or if not compiled <code>null</code>.
*
* @private
* @member {?vm.Script} _compiledNodeVM
* @memberOf VMScript#
*/
/**
* The resolved compiler to use to get the JavaScript code.
*
* @private
* @readonly
* @member {compileCallback} _compiler
* @memberOf VMScript#
*/
/**
* The script to run without wrapping.
*
* @private
* @member {string} _code
* @memberOf VMScript#
*/
/**
* Create VMScript instance.
*
* @param {String} code Code to run.
* @param {String} [filename] Filename that shows up in any stack traces produced from this script.
* @return {VMScript}
* @public
* @param {string} code - Code to run.
* @param {(string|Object)} [options] - Options map or filename.
* @param {string} [options.filename="vm.js"] - Filename that shows up in any stack traces produced from this script.
* @param {number} [options.lineOffset=0] - Passed to vm.Script options.
* @param {number} [options.columnOffset=0] - Passed to vm.Script options.
* @param {(string|compileCallback)} [options.compiler="javascript"] - The compiler to use.
* @throws {VMError} If the compiler is unknown or if coffee-script was requested but the module not found.
*/
constructor(code, options) {
const sCode = `${code}`;
let useFileName;
let useOptions;
if (arguments.length === 2) {
if (typeof options === 'object' && options.toString === Object.prototype.toString) {
useOptions = options || {};
useFileName = useOptions.filename;
} else {
useOptions = {};
useFileName = options;
}
} else if (arguments.length > 2) {
// We do it this way so that there are no more arguments in the function.
// eslint-disable-next-line prefer-rest-params
useOptions = arguments[2] || {};
useFileName = options || useOptions.filename;
} else {
useOptions = {};
}
constructor(code, filename) {
this._code = String(code);
this.filename = filename || 'vm.js';
this._prefix = '';
this._suffix = '';
this._compiledVM = null;
this._compiledNodeVM = null;
const {
compiler = 'javascript',
lineOffset = 0,
columnOffset = 0
} = useOptions;
// Throw if the compiler is unknown.
const resolvedCompiler = lookupCompiler(compiler);
Object.defineProperties(this, {
code: {
// Put this here so that it is enumerable, and looks like a property.
get() {
return this._prefix + this._code + this._suffix;
},
set(value) {
const strNewCode = String(value);
if (strNewCode === this._code && this._prefix === '' && this._suffix === '') return;
this._code = strNewCode;
this._prefix = '';
this._suffix = '';
this._compiledVM = null;
this._compiledNodeVM = null;
},
enumerable: true
},
filename: {
value: useFileName || 'vm.js',
enumerable: true
},
lineOffset: {
value: lineOffset,
enumerable: true
},
columnOffset: {
value: columnOffset,
enumerable: true
},
compiler: {
value: compiler,
enumerable: true
},
_code: {
value: sCode,
writable: true
},
_prefix: {
value: '',
writable: true
},
_suffix: {
value: '',
writable: true
},
_compiledVM: {
value: null,
writable: true
},
_compiledNodeVM: {
value: null,
writable: true
},
_compiler: {value: resolvedCompiler}
});
}
/**
* Wraps the code.
* Wraps the code.<br>
* This will replace the old wrapping.<br>
* Will invalidate the code cache.
*
* @return {VMScript}
* @public
* @deprecated Since v3.8.5. Wrap your code before passing it into the VMScript object.
* @param {string} prefix - String that will be appended before the script code.
* @param {script} suffix - String that will be appended behind the script code.
* @return {this} This for chaining.
* @throws {TypeError} If prefix or suffix is a Symbol.
*/
wrap(prefix, suffix) {
const strPrefix = String(prefix);
const strSuffix = String(suffix);
const strPrefix = `${prefix}`;
const strSuffix = `${suffix}`;
if (this._prefix === strPrefix && this._suffix === strSuffix) return this;

@@ -83,11 +371,11 @@ this._prefix = strPrefix;

/**
* Noop.
* We need to change the code depending whether it is run in VM or NodeVM.
* This function cannot decide for which to compile.
* Compile this script. <br>
* This is useful to detect syntax errors in the script.
*
* @deprecated Will be done on first run
* @return {VMScript}
* @public
* @return {this} This for chaining.
* @throws {SyntaxError} If there is a syntax error in the script.
*/
compile() {
this._compileVM();
return this;

@@ -97,168 +385,340 @@ }

/**
* For backwards compatibility.
* Compiles this script to a vm.Script.
*
* @return {String} The wrapped code
* @private
* @param {string} prefix - JavaScript code that will be used as prefix.
* @param {string} suffix - JavaScript code that will be used as suffix.
* @return {vm.Script} The compiled vm.Script.
* @throws {SyntaxError} If there is a syntax error in the script.
*/
get code() {
return this._prefix + this._code + this._suffix;
_compile(prefix, suffix) {
return new vm.Script(prefix + this._compiler(this._prefix + this._code + this._suffix, this.filename) + suffix, {
filename: this.filename,
displayErrors: false,
lineOffset: this.lineOffset,
columnOffset: this.columnOffset
});
}
/**
* For backwards compatibility.
* Will invalidate the code cache.
* Will return the cached version of the script intended for VM or compile it.
*
* @param {String} newCode The new code to run.
* @private
* @return {vm.Script} The compiled script
* @throws {SyntaxError} If there is a syntax error in the script.
*/
set code(newCode) {
const strNewCode = String(newCode);
if (strNewCode === this._prefix + this._code + this._suffix) return;
this._code = strNewCode;
this._prefix = '';
this._suffix = '';
this._compiledVM = null;
this._compiledNodeVM = null;
_compileVM() {
let script = this._compiledVM;
if (!script) {
this._compiledVM = script = this._compile('', '');
}
return script;
}
/**
* Will compile the code for VM and cache it
* Will return the cached version of the script intended for NodeVM or compile it.
*
* @return {VMScript}
* @private
* @return {vm.Script} The compiled script
* @throws {SyntaxError} If there is a syntax error in the script.
*/
_compileVM() {
if (this._compiledVM) return this;
_compileNodeVM() {
let script = this._compiledNodeVM;
if (!script) {
this._compiledNodeVM = script = this._compile('(function (exports, require, module, __filename, __dirname) { ', '\n})');
}
return script;
}
this._compiledVM = new vm.Script(this._prefix + this._code + this._suffix, {
filename: this.filename,
displayErrors: false
});
}
return this;
}
/**
*
* This callback will be called and has a specific time to finish.<br>
* No parameters will be supplied.<br>
* If parameters are required, use a closure.
*
* @private
* @callback runWithTimeout
* @return {*}
*
*/
/**
* Will compile the code for NodeVM and cache it
*
* @return {VMScript}
*/
_compileNodeVM() {
if (this._compiledNodeVM) return this;
this._compiledNodeVM = new vm.Script('(function (exports, require, module, __filename, __dirname) { ' +
this._prefix + this._code + this._suffix + '\n})', {
filename: this.filename,
/**
* Run a function with a specific timeout.
*
* @private
* @param {runWithTimeout} fn - Function to run with the specific timeout.
* @param {number} timeout - The amount of time to give the function to finish.
* @return {*} The value returned by the function.
* @throws {Error} If the function took to long.
*/
function doWithTimeout(fn, timeout) {
let ctx = CACHE.timeoutContext;
let script = CACHE.timeoutScript;
if (!ctx) {
CACHE.timeoutContext = ctx = vm.createContext();
CACHE.timeoutScript = script = new vm.Script('fn()', {
filename: 'timeout_bridge.js',
displayErrors: false
});
return this;
}
ctx.fn = fn;
try {
return script.runInContext(ctx, {
displayErrors: false,
timeout
});
} finally {
ctx.fn = null;
}
}
function loadScript(filename) {
const data = fs.readFileSync(filename, 'utf8');
return new VMScript(data, filename);
}
const SCRIPT_CACHE = {
cf: loadScript(`${__dirname}/contextify.js`).wrap('(function(require, host) { ', '\n})')._compileVM(),
sb: loadScript(`${__dirname}/sandbox.js`).wrap('(function (vm, host, Contextify, Decontextify, Buffer) { ', '\n})')._compileVM(),
exp: new VMScript('({exports: {}})')._compileVM()
};
/**
* Class VM.
*
* @property {Object} options VM options.
* @public
*/
class VM extends EventEmitter {
class VM extends EventEmitter {
/**
* Create VM instance.
* The timeout for {@link VM#run} calls.
*
* @param {Object} [options] VM options.
* @return {VM}
* @public
* @since v3.8.5
* @member {number} timeout
* @memberOf VM#
*/
/**
* Get the global sandbox object.
*
* @public
* @readonly
* @since v3.8.5
* @member {Object} sandbox
* @memberOf VM#
*/
/**
* The compiler to use to get the JavaScript code.
*
* @public
* @readonly
* @since v3.8.5
* @member {(string|compileCallback)} compiler
* @memberOf VM#
*/
/**
* The context for this sandbox.
*
* @private
* @readonly
* @member {Object} _context
* @memberOf VM#
*/
/**
* The internal methods for this sandbox.
*
* @private
* @readonly
* @member {{Contextify: Object, Decontextify: Object, Buffer: Object, sandbox:Object}} _internal
* @memberOf VM#
*/
/**
* The resolved compiler to use to get the JavaScript code.
*
* @private
* @readonly
* @member {compileCallback} _compiler
* @memberOf VM#
*/
/**
* Create a new VM instance.
*
* @public
* @param {Object} [options] - VM options.
* @param {number} [options.timeout] - The amount of time until a call to {@link VM#run} will timeout.
* @param {Object} [options.sandbox] - Objects that will be copied into the global object of the sandbox.
* @param {(string|compileCallback)} [options.compiler="javascript"] - The compiler to use.
* @param {boolean} [options.eval=true] - Allow the dynamic evaluation of code via eval(code) or Function(code)().<br>
* Only available for node v10+.
* @param {boolean} [options.wasm=true] - Allow to run wasm code.<br>
* Only available for node v10+.
* @param {boolean} [options.fixAsync=false] - Filters for async functions.
* @throws {VMError} If the compiler is unknown.
*/
constructor(options = {}) {
super();
// defaults
this.options = {
timeout: options.timeout,
sandbox: options.sandbox,
compiler: options.compiler || 'javascript',
eval: options.eval === false ? false : true,
wasm: options.wasm === false ? false : true
};
// Read all options
const {
timeout,
sandbox,
compiler = 'javascript'
} = options;
const allowEval = options.eval !== false;
const allowWasm = options.wasm !== false;
const fixAsync = !!options.fixAsync;
const host = {
version: parseInt(process.versions.node.split('.')[0]),
console,
String,
Number,
Buffer,
Boolean,
Array,
Date,
Error,
EvalError,
RangeError,
ReferenceError,
SyntaxError,
TypeError,
URIError,
RegExp,
Function,
Object,
VMError,
Proxy,
Reflect,
Map,
WeakMap,
Set,
WeakSet,
Promise,
Symbol
};
// Early error if sandbox is not an object.
if (sandbox && 'object' !== typeof sandbox) {
throw new VMError('Sandbox must be object.');
}
this._context = vm.createContext(undefined, {
// Early error if compiler can't be found.
const resolvedCompiler = lookupCompiler(compiler);
// Create a new context for this vm.
const _context = vm.createContext(undefined, {
codeGeneration: {
strings: this.options.eval,
wasm: this.options.wasm
strings: allowEval,
wasm: allowWasm
}
});
Reflect.defineProperty(this, '_internal', {
value: SCRIPT_CACHE.cf._compiledVM.runInContext(this._context, {
filename: SCRIPT_CACHE.cf.filename,
displayErrors: false
}).call(this._context, require, host)
// Create the bridge between the host and the sandbox.
const _internal = CACHE.contextifyScript.runInContext(_context, DEFAULT_RUN_OPTIONS).call(_context, require, HOST);
// Define the properties of this object.
// Use Object.defineProperties here to be able to
// hide and set properties write only.
Object.defineProperties(this, {
timeout: {
value: timeout,
writable: true,
enumerable: true
},
compiler: {
value: compiler,
enumerable: true
},
sandbox: {
value: _internal.sandbox,
enumerable: true
},
_context: {value: _context},
_internal: {value: _internal},
_compiler: {value: resolvedCompiler},
_fixAsync: {value: fixAsync}
});
// prepare global sandbox
if (this.options.sandbox) {
if ('object' !== typeof this.options.sandbox) {
throw new VMError('Sandbox must be object.');
if (fixAsync) {
if (!CACHE.fixAsyncScript) {
CACHE.fixAsyncScript = loadAndCompileScript(`${__dirname}/fixasync.js`, '(function() { ', '\n})');
CACHE.getGlobalScript = new vm.Script('this', {
filename: 'get_global.js',
displayErrors: false
});
try {
CACHE.getGeneratorFunctionScript = new vm.Script('(function*(){}).constructor', {
filename: 'get_generator_function.js',
displayErrors: false
});
} catch (ex) {}
try {
CACHE.getAsyncFunctionScript = new vm.Script('(async function(){}).constructor', {
filename: 'get_async_function.js',
displayErrors: false
});
} catch (ex) {}
try {
CACHE.getAsyncGeneratorFunctionScript = new vm.Script('(async function*(){}).constructor', {
filename: 'get_async_generator_function.js',
displayErrors: false
});
} catch (ex) {}
}
const internal = {
__proto__: null,
global: CACHE.getGlobalScript.runInContext(this._context, DEFAULT_RUN_OPTIONS),
Contextify: this._internal.Contextify,
host: HOST
};
if (CACHE.getGeneratorFunctionScript) {
try {
internal.GeneratorFunction = CACHE.getGeneratorFunctionScript.runInContext(this._context, DEFAULT_RUN_OPTIONS);
} catch (ex) {}
}
if (CACHE.getAsyncFunctionScript) {
try {
internal.AsyncFunction = CACHE.getAsyncFunctionScript.runInContext(this._context, DEFAULT_RUN_OPTIONS);
} catch (ex) {}
}
if (CACHE.getAsyncGeneratorFunctionScript) {
try {
internal.AsyncGeneratorFunction = CACHE.getAsyncGeneratorFunctionScript.runInContext(this._context, DEFAULT_RUN_OPTIONS);
} catch (ex) {}
}
CACHE.fixAsyncScript.runInContext(this._context, DEFAULT_RUN_OPTIONS).call(internal);
}
for (const name in this.options.sandbox) {
if (Object.prototype.hasOwnProperty.call(this.options.sandbox, name)) {
this._internal.Contextify.globalValue(this.options.sandbox[name], name);
}
// prepare global sandbox
if (sandbox) {
this.setGlobals(sandbox);
}
}
/**
* Adds all the values to the globals.
*
* @public
* @since v3.8.5
* @param {Object} values - All values that will be added to the globals.
* @return {this} This for chaining.
* @throws {*} If the setter of a global throws an exception it is propagated. And the remaining globals will not be written.
*/
setGlobals(values) {
for (const name in values) {
if (Object.prototype.hasOwnProperty.call(values, name)) {
this._internal.Contextify.setGlobal(name, values[name]);
}
}
return this;
}
/**
* Set a global value.
*
* @public
* @since v3.8.5
* @param {string} name - The name of the global.
* @param {*} value - The value of the global.
* @return {this} This for chaining.
* @throws {*} If the setter of the global throws an exception it is propagated.
*/
setGlobal(name, value) {
this._internal.Contextify.setGlobal(name, value);
return this;
}
/**
* Get a global value.
*
* @public
* @since v3.8.5
* @param {string} name - The name of the global.
* @return {*} The value of the global.
* @throws {*} If the getter of the global throws an exception it is propagated.
*/
getGlobal(name) {
return this._internal.Contextify.getGlobal(name);
}
/**
* Freezes the object inside VM making it read-only. Not available for primitive values.
*
* @static
* @param {*} object Object to freeze.
* @param {String} [globalName] Whether to add the object to global.
* @public
* @param {*} value - Object to freeze.
* @param {string} [globalName] - Whether to add the object to global.
* @return {*} Object to freeze.
* @throws {*} If the setter of the global throws an exception it is propagated.
*/
freeze(value, globalName) {
this._internal.Contextify.readonly(value);
if (globalName) this._internal.Contextify.globalValue(value, globalName);
if (globalName) this._internal.Contextify.setGlobal(globalName, value);
return value;

@@ -270,11 +730,11 @@ }

*
* @static
* @param {*} object Object to protect.
* @param {String} [globalName] Whether to add the object to global.
* @public
* @param {*} value - Object to protect.
* @param {string} [globalName] - Whether to add the object to global.
* @return {*} Object to protect.
* @throws {*} If the setter of the global throws an exception it is propagated.
*/
protect(value, globalName) {
this._internal.Contextify.protected(value);
if (globalName) this._internal.Contextify.globalValue(value, globalName);
if (globalName) this._internal.Contextify.setGlobal(globalName, value);
return value;

@@ -286,147 +746,222 @@ }

*
* @param {String} code Code to run.
* @public
* @param {(string|VMScript)} code - Code to run.
* @param {string} [filename="vm.js"] - Filename that shows up in any stack traces produced from this script.<br>
* This is only used if code is a String.
* @return {*} Result of executed code.
* @throws {SyntaxError} If there is a syntax error in the script.
* @throws {Error} An error is thrown when the script took to long and there is a timeout.
* @throws {*} If the script execution terminated with an exception it is propagated.
*/
run(code, filename) {
let script;
if (code instanceof VMScript) {
if (this._fixAsync && /\basync\b/.test(code.code)) {
throw new VMError('Async not available');
}
script = code._compileVM();
} else {
if (this._fixAsync && /\basync\b/.test(code)) {
throw new VMError('Async not available');
}
const useFileName = filename || 'vm.js';
// Compile the script here so that we don't need to create a instance of VMScript.
script = new vm.Script(this._compiler(code, useFileName), {
filename: useFileName,
displayErrors: false
});
}
run(code) {
if (this.options.compiler !== 'javascript') {
code = _compileToJS(code, this.options.compiler);
if (!this.timeout) {
// If no timeout is given, directly run the script.
try {
return this._internal.Decontextify.value(script.runInContext(this._context, DEFAULT_RUN_OPTIONS));
} catch (e) {
throw this._internal.Decontextify.value(e);
}
}
const script = code instanceof VMScript ? code : new VMScript(code);
script._compileVM();
return doWithTimeout(()=>{
try {
return this._internal.Decontextify.value(script.runInContext(this._context, DEFAULT_RUN_OPTIONS));
} catch (e) {
throw this._internal.Decontextify.value(e);
}
}, this.timeout);
}
try {
return this._internal.Decontextify.value(script._compiledVM.runInContext(this._context, {
filename: script.filename,
displayErrors: false,
timeout: this.options.timeout
}));
} catch (e) {
throw this._internal.Decontextify.value(e);
/**
* Run the code in VM.
*
* @public
* @since v3.8.5
* @param {string} filename - Filename of file to load and execute in a NodeVM.
* @return {*} Result of executed code.
* @throws {Error} If filename is not a valid filename.
* @throws {SyntaxError} If there is a syntax error in the script.
* @throws {Error} An error is thrown when the script took to long and there is a timeout.
* @throws {*} If the script execution terminated with an exception it is propagated.
*/
runFile(filename) {
const resolvedFilename = pa.resolve(filename);
if (!fs.existsSync(resolvedFilename)) {
throw new VMError(`Script '${filename}' not found.`);
}
if (fs.statSync(resolvedFilename).isDirectory()) {
throw new VMError('Script must be file, got directory.');
}
return this.run(fs.readFileSync(resolvedFilename, 'utf8'), resolvedFilename);
}
}
/**
* Event caused by a <code>console.debug</code> call if <code>options.console="redirect"</code> is specified.
*
* @public
* @event NodeVM."console.debug"
* @type {...*}
*/
/**
* Event caused by a <code>console.log</code> call if <code>options.console="redirect"</code> is specified.
*
* @public
* @event NodeVM."console.log"
* @type {...*}
*/
/**
* Event caused by a <code>console.info</code> call if <code>options.console="redirect"</code> is specified.
*
* @public
* @event NodeVM."console.info"
* @type {...*}
*/
/**
* Event caused by a <code>console.warn</code> call if <code>options.console="redirect"</code> is specified.
*
* @public
* @event NodeVM."console.warn"
* @type {...*}
*/
/**
* Event caused by a <code>console.error</code> call if <code>options.console="redirect"</code> is specified.
*
* @public
* @event NodeVM."console.error"
* @type {...*}
*/
/**
* Event caused by a <code>console.dir</code> call if <code>options.console="redirect"</code> is specified.
*
* @public
* @event NodeVM."console.dir"
* @type {...*}
*/
/**
* Event caused by a <code>console.trace</code> call if <code>options.console="redirect"</code> is specified.
*
* @public
* @event NodeVM."console.trace"
* @type {...*}
*/
/**
* Class NodeVM.
*
* @class
* @public
* @extends {VM}
* @extends {EventEmitter}
* @property {Object} module Pointer to main module.
*/
class NodeVM extends VM {
class NodeVM extends EventEmitter {
/**
* Create NodeVM instance.
* Create a new NodeVM instance.<br>
*
* Unlike VM, NodeVM lets you use require same way like in regular node.
* Unlike VM, NodeVM lets you use require same way like in regular node.<br>
*
* However, it does not use the timeout.
*
* @param {Object} [options] VM options.
* @return {NodeVM}
* @public
* @param {Object} [options] - VM options.
* @param {Object} [options.sandbox] - Objects that will be copied into the global object of the sandbox.
* @param {(string|compileCallback)} [options.compiler="javascript"] - The compiler to use.
* @param {boolean} [options.eval=true] - Allow the dynamic evaluation of code via eval(code) or Function(code)().<br>
* Only available for node v10+.
* @param {boolean} [options.wasm=true] - Allow to run wasm code.<br>
* Only available for node v10+.
* @param {("inherit"|"redirect"|"off")} [options.console="inherit"] - Sets the behavior of the console in the sandbox.
* <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 {(boolean|string[]|Object)} [options.require.external=false] - true, an array of allowed external modules or an object.
* @param {(string[])} [options.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.
* @param {boolean} [options.require.external.transitive=false] - Boolean which indicates if transitive dependencies of external modules are allowed.
* @param {string[]} [options.require.builtin=[]] - Array of allowed builtin modules, accepts ["*"] for all.
* @param {(string|string[])} [options.require.root] - Restricted path(s) where local modules can be required. If omitted every path is allowed.
* @param {Object} [options.require.mock] - Collection of mock modules (both external or builtin).
* @param {("host"|"sandbox")} [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.
* Builtin modules except <code>events</code> always required in host and proxied to sandbox.
* @param {string[]} [options.require.import] - Array of modules to be loaded into NodeVM on start.
* @param {resolveCallback} [options.require.resolve] - An additional lookup function in case a module wasn't
* found in one of the traditional node lookup paths.
* @param {boolean} [options.nesting=false] - Allow nesting of VMs.
* @param {("commonjs"|"none")} [options.wrapper="commonjs"] - <code>commonjs</code> to wrap script into CommonJS wrapper,
* <code>none</code> to retrieve value returned by the script.
* @param {string[]} [options.sourceExtensions=["js"]] - Array of file extensions to treat as source code.
* @throws {VMError} If the compiler is unknown.
*/
constructor(options = {}) {
super();
const sandbox = options.sandbox;
// Throw this early
if (sandbox && 'object' !== typeof sandbox) {
throw new VMError('Sandbox must be object.');
}
super({compiler: options.compiler, eval: options.eval, wasm: options.wasm});
// defaults
this.options = {
sandbox: options.sandbox,
Object.defineProperty(this, 'options', {value: {
console: options.console || 'inherit',
require: options.require || false,
compiler: options.compiler || 'javascript',
eval: options.eval === false ? false : true,
wasm: options.wasm === false ? false : true,
nesting: options.nesting || false,
wrapper: options.wrapper || 'commonjs',
sourceExtensions: options.sourceExtensions || ['js']
};
}});
const host = {
version: parseInt(process.versions.node.split('.')[0]),
require,
process,
console,
setTimeout,
setInterval,
setImmediate,
clearTimeout,
clearInterval,
clearImmediate,
String,
Number,
Buffer,
Boolean,
Array,
Date,
Error,
EvalError,
RangeError,
ReferenceError,
SyntaxError,
TypeError,
URIError,
RegExp,
Function,
Object,
VMError,
Proxy,
Reflect,
Map,
WeakMap,
Set,
WeakSet,
Promise,
Symbol
};
if (this.options.nesting) {
host.VM = VM;
host.NodeVM = NodeVM;
let sandboxScript = CACHE.sandboxScript;
if (!sandboxScript) {
CACHE.sandboxScript = sandboxScript = loadAndCompileScript(`${__dirname}/sandbox.js`,
'(function (vm, host, Contextify, Decontextify, Buffer) { ', '\n})');
}
this._context = vm.createContext(undefined, {
codeGeneration: {
strings: this.options.eval,
wasm: this.options.wasm
}
});
const closure = sandboxScript.runInContext(this._context, DEFAULT_RUN_OPTIONS);
Object.defineProperty(this, '_internal', {
value: SCRIPT_CACHE.cf._compiledVM.runInContext(this._context, {
filename: SCRIPT_CACHE.cf.filename,
displayErrors: false
}).call(this._context, require, host)
});
const closure = SCRIPT_CACHE.sb._compiledVM.runInContext(this._context, {
filename: SCRIPT_CACHE.sb.filename,
displayErrors: false
});
Object.defineProperty(this, '_prepareRequire', {
value: closure.call(this._context, this, host, this._internal.Contextify, this._internal.Decontextify, this._internal.Buffer)
value: closure.call(this._context, this, HOST, this._internal.Contextify, this._internal.Decontextify, this._internal.Buffer)
});
// prepare global sandbox
if (this.options.sandbox) {
if ('object' !== typeof this.options.sandbox) {
throw new VMError('Sandbox must be object.');
}
for (const name in this.options.sandbox) {
if (Object.prototype.hasOwnProperty.call(this.options.sandbox, name)) {
this._internal.Contextify.globalValue(this.options.sandbox[name], name);
}
}
if (sandbox) {
this.setGlobals(sandbox);
}
if (this.options.require && this.options.require.import) {
if (!Array.isArray(this.options.require.import)) {
this.options.require.import = [this.options.require.import];
if (Array.isArray(this.options.require.import)) {
for (let i = 0, l = this.options.require.import.length; i < l; i++) {
this.require(this.options.require.import[i]);
}
} else {
this.require(this.options.require.import);
}
for (let i = 0, l = this.options.require.import.length; i < l; i++) {
this.require(this.options.require.import[i]);
}
}

@@ -436,9 +971,15 @@ }

/**
* @deprecated
* @ignore
* @deprecated Just call the method yourself like <code>method(args);</code>
* @param {function} method - Function to invoke.
* @param {...*} args - Arguments to pass to the function.
* @return {*} Return value of the function.
* @todo Can we remove this function? It even had a bug that would use args as this parameter.
* @throws {*} Rethrows anything the method throws.
* @throws {VMError} If method is not a function.
* @throws {Error} If method is a class.
*/
call(method, ...args) {
if ('function' === typeof method) {
return method.apply(args);
return method(...args);
} else {

@@ -450,38 +991,9 @@ throw new VMError('Unrecognized method type.');

/**
* Freezes the object inside VM making it read-only. Not available for primitive values.
*
* @static
* @param {*} object Object to freeze.
* @param {String} [globalName] Whether to add the object to global.
* @return {*} Object to freeze.
*/
freeze(value, globalName) {
this._internal.Contextify.readonly(value);
if (global) this._internal.Contextify.globalValue(value, globalName);
return value;
}
/**
* Protects the object inside VM making impossible to set functions as it's properties. Not available for primitive values.
*
* @static
* @param {*} object Object to protect.
* @param {String} [globalName] Whether to add the object to global.
* @return {*} Object to protect.
*/
protect(value, globalName) {
this._internal.Contextify.protected(value);
if (global) this._internal.Contextify.globalValue(value, globalName);
return value;
}
/**
* Require a module in VM and return it's exports.
*
* @param {String} module Module name.
* @public
* @param {string} module - Module name.
* @return {*} Exported module.
* @throws {*} If the module couldn't be found or loading it threw an error.
*/
require(module) {

@@ -497,37 +1009,50 @@ return this.run(`module.exports = require('${module}');`, 'vm.js');

*
* @param {String} code Code to run.
* @param {String} [filename] Filename that shows up in any stack traces produced from this script.
* @param {(string|VMScript)} code - Code to run.
* @param {string} [filename] - Filename that shows up in any stack traces produced from this script.<br>
* This is only used if code is a String.
* @return {*} Result of executed code.
* @throws {SyntaxError} If there is a syntax error in the script.
* @throws {*} If the script execution terminated with an exception it is propagated.
* @fires NodeVM."console.debug"
* @fires NodeVM."console.log"
* @fires NodeVM."console.info"
* @fires NodeVM."console.warn"
* @fires NodeVM."console.error"
* @fires NodeVM."console.dir"
* @fires NodeVM."console.trace"
*/
run(code, filename) {
if (this.options.compiler !== 'javascript') {
code = _compileToJS(code, this.options.compiler, filename);
}
let dirname;
let returned;
let resolvedFilename;
let script;
if (filename) {
filename = pa.resolve(filename);
dirname = pa.dirname(filename);
if (code instanceof VMScript) {
script = code._compileNodeVM();
resolvedFilename = pa.resolve(code.filename);
dirname = pa.dirname(resolvedFilename);
} else {
filename = null;
dirname = null;
const unresolvedFilename = filename || 'vm.js';
if (filename) {
resolvedFilename = pa.resolve(filename);
dirname = pa.dirname(resolvedFilename);
} else {
resolvedFilename = null;
dirname = null;
}
script = new vm.Script('(function (exports, require, module, __filename, __dirname) { ' +
this._compiler(code, unresolvedFilename) + '\n})', {
filename: unresolvedFilename,
displayErrors: false
});
}
const module = SCRIPT_CACHE.exp._compiledVM.runInContext(this._context, {
displayErrors: false
});
const wrapper = this.options.wrapper;
const module = this._internal.Contextify.makeModule();
const script = code instanceof VMScript ? code : new VMScript(code, filename);
script._compileNodeVM();
try {
const closure = script._compiledNodeVM.runInContext(this._context, {
filename: script.filename,
displayErrors: false
});
const closure = script.runInContext(this._context, DEFAULT_RUN_OPTIONS);
returned = closure.call(this._context, module.exports, this._prepareRequire(dirname), module, filename, dirname);
const returned = closure.call(this._context, module.exports, this._prepareRequire(dirname), module, resolvedFilename, dirname);
return this._internal.Decontextify.value(wrapper === 'commonjs' ? module.exports : returned);
} catch (e) {

@@ -537,7 +1062,2 @@ throw this._internal.Decontextify.value(e);

if (this.options.wrapper === 'commonjs') {
return this._internal.Decontextify.value(module.exports);
} else {
return this._internal.Decontextify.value(returned);
}
}

@@ -548,18 +1068,26 @@

*
* @param {String} script Javascript code.
* @param {String} [filename] File name (used in stack traces only).
* @param {Object} [options] VM options.
* @return {NodeVM} VM.
* @public
* @static
* @param {string} script - Code to execute.
* @param {string} [filename] - File name (used in stack traces only).
* @param {Object} [options] - VM options.
* @param {string} [options.filename] - File name (used in stack traces only). Used if <code>filename</code> is omitted.
* @return {*} Result of executed code.
* @see {@link NodeVM} for the options.
* @throws {SyntaxError} If there is a syntax error in the script.
* @throws {*} If the script execution terminated with an exception it is propagated.
*/
static code(script, filename, options) {
let unresolvedFilename;
if (filename != null) {
if ('object' === typeof filename) {
options = filename;
filename = null;
unresolvedFilename = options.filename;
} else if ('string' === typeof filename) {
filename = pa.resolve(filename);
unresolvedFilename = filename;
} else {
throw new VMError('Invalid arguments.');
}
} else if ('object' === typeof options) {
unresolvedFilename = options.filename;
}

@@ -571,3 +1099,5 @@

return new NodeVM(options).run(script, filename);
const resolvedFilename = typeof unresolvedFilename === 'string' ? pa.resolve(unresolvedFilename) : undefined;
return new NodeVM(options).run(script, resolvedFilename);
}

@@ -578,19 +1108,24 @@

*
* @param {String} [filename] File name (used in stack traces only).
* @param {Object} [options] VM options.
* @return {NodeVM} VM.
* @public
* @static
* @param {string} filename - Filename of file to load and execute in a NodeVM.
* @param {Object} [options] - NodeVM options.
* @return {*} Result of executed code.
* @see {@link NodeVM} for the options.
* @throws {Error} If filename is not a valid filename.
* @throws {SyntaxError} If there is a syntax error in the script.
* @throws {*} If the script execution terminated with an exception it is propagated.
*/
static file(filename, options) {
filename = pa.resolve(filename);
const resolvedFilename = pa.resolve(filename);
if (!fs.existsSync(filename)) {
if (!fs.existsSync(resolvedFilename)) {
throw new VMError(`Script '${filename}' not found.`);
}
if (fs.statSync(filename).isDirectory()) {
if (fs.statSync(resolvedFilename).isDirectory()) {
throw new VMError('Script must be file, got directory.');
}
return new NodeVM(options).run(fs.readFileSync(filename, 'utf8'), filename);
return new NodeVM(options).run(fs.readFileSync(resolvedFilename, 'utf8'), resolvedFilename);
}

@@ -602,16 +1137,13 @@ }

*
* @class
* @public
* @extends {Error}
* @property {String} stack Call stack.
* @property {String} message Error message.
*/
class VMError extends Error {
class VMError extends Error {
/**
* Create VMError instance.
*
* @param {String} message Error message.
* @return {VMError}
* @public
* @param {string} message - Error message.
*/
constructor(message) {

@@ -626,2 +1158,48 @@ super(message);

/**
* Host objects
*
* @private
*/
const HOST = {
version: parseInt(process.versions.node.split('.')[0]),
require,
process,
console,
setTimeout,
setInterval,
setImmediate,
clearTimeout,
clearInterval,
clearImmediate,
String,
Number,
Buffer,
Boolean,
Array,
Date,
Error,
EvalError,
RangeError,
ReferenceError,
SyntaxError,
TypeError,
URIError,
RegExp,
Function,
Object,
VMError,
Proxy,
Reflect,
Map,
WeakMap,
Set,
WeakSet,
Promise,
Symbol,
INSPECT_MAX_BYTES,
VM,
NodeVM
};
exports.VMError = VMError;

@@ -628,0 +1206,0 @@ exports.NodeVM = NodeVM;

@@ -62,5 +62,3 @@ /* eslint-disable no-shadow, no-invalid-this */

let contents = fs.readFileSync(filename, 'utf8');
if (typeof vm.options.compiler === 'function') {
contents = vm.options.compiler(contents, filename);
}
contents = vm._compiler(contents, filename);

@@ -67,0 +65,0 @@ const code = `(function (exports, require, module, __filename, __dirname) { 'use strict'; ${contents} \n});`;

const match = (wildcard, s) => {
const regexString = wildcard.replace(/\*/, '\\S*').replace(/\?/g, '.');
const regexString = wildcard.replace(/\*/g, '\\S*').replace(/\?/g, '.');
const regex = new RegExp(regexString);

@@ -4,0 +4,0 @@ return regex.test(s);

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

],
"version": "3.8.4",
"version": "3.9.0",
"main": "index.js",

@@ -25,3 +25,3 @@ "repository": "github:patriksimek/vm2",

"eslint-config-integromat": "^1.5.0",
"mocha": "^5.2.0"
"mocha": "^6.2.2"
},

@@ -28,0 +28,0 @@ "engines": {

@@ -13,3 +13,2 @@ # vm2 [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![Package Quality][quality-image]][quality-url] [![Travis CI][travis-image]][travis-url] [![Known Vulnerabilities][snyk-image]][snyk-url]

* You can securely call methods and exchange data and callbacks between sandboxes
* Is immune to `while (true) {}` (see docs)
* Is immune to all known methods of attacks

@@ -101,3 +100,3 @@ * Transpilers support

**IMPORTANT**: Timeout is only effective on synchronous code you run through `run`. Timeout is NOT effective on any method returned by VM.
**IMPORTANT**: Timeout is only effective on synchronous code you run through `run`. Timeout is NOT effective on any method returned by VM. There're some situations when timeout doesn't work - see [#244](https://github.com/patriksimek/vm2/pull/244).

@@ -104,0 +103,0 @@ ```javascript

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc