You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					540 lines
				
				14 KiB
			
		
		
			
		
	
	
					540 lines
				
				14 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * 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 modulusedRequiree 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');
							 | 
						||
| 
								 | 
							
								const pa = require('path');
							 | 
						||
| 
								 | 
							
								const {
							 | 
						||
| 
								 | 
							
									Script,
							 | 
						||
| 
								 | 
							
									createContext
							 | 
						||
| 
								 | 
							
								} = require('vm');
							 | 
						||
| 
								 | 
							
								const {
							 | 
						||
| 
								 | 
							
									EventEmitter
							 | 
						||
| 
								 | 
							
								} = require('events');
							 | 
						||
| 
								 | 
							
								const {
							 | 
						||
| 
								 | 
							
									INSPECT_MAX_BYTES
							 | 
						||
| 
								 | 
							
								} = require('buffer');
							 | 
						||
| 
								 | 
							
								const {
							 | 
						||
| 
								 | 
							
									createBridge,
							 | 
						||
| 
								 | 
							
									VMError
							 | 
						||
| 
								 | 
							
								} = require('./bridge');
							 | 
						||
| 
								 | 
							
								const {
							 | 
						||
| 
								 | 
							
									transformer,
							 | 
						||
| 
								 | 
							
									INTERNAL_STATE_NAME
							 | 
						||
| 
								 | 
							
								} = require('./transformer');
							 | 
						||
| 
								 | 
							
								const {
							 | 
						||
| 
								 | 
							
									lookupCompiler
							 | 
						||
| 
								 | 
							
								} = require('./compiler');
							 | 
						||
| 
								 | 
							
								const {
							 | 
						||
| 
								 | 
							
									VMScript
							 | 
						||
| 
								 | 
							
								} = require('./script');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const objectDefineProperties = Object.defineProperties;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Host objects
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								const HOST = Object.freeze({
							 | 
						||
| 
								 | 
							
									Buffer,
							 | 
						||
| 
								 | 
							
									Function,
							 | 
						||
| 
								 | 
							
									Object,
							 | 
						||
| 
								 | 
							
									transformAndCheck,
							 | 
						||
| 
								 | 
							
									INSPECT_MAX_BYTES,
							 | 
						||
| 
								 | 
							
									INTERNAL_STATE_NAME
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Compile a script.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @param {string} filename - Filename of the script.
							 | 
						||
| 
								 | 
							
								 * @param {string} script - Script.
							 | 
						||
| 
								 | 
							
								 * @return {vm.Script} The compiled script.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function compileScript(filename, script) {
							 | 
						||
| 
								 | 
							
									return new Script(script, {
							 | 
						||
| 
								 | 
							
										__proto__: null,
							 | 
						||
| 
								 | 
							
										filename,
							 | 
						||
| 
								 | 
							
										displayErrors: false
							 | 
						||
| 
								 | 
							
									});
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Default run options for vm.Script.runInContext
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								const DEFAULT_RUN_OPTIONS = Object.freeze({__proto__: null, displayErrors: false});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function checkAsync(allow) {
							 | 
						||
| 
								 | 
							
									if (!allow) throw new VMError('Async not available');
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function transformAndCheck(args, code, isAsync, isGenerator, allowAsync) {
							 | 
						||
| 
								 | 
							
									const ret = transformer(args, code, isAsync, isGenerator, undefined);
							 | 
						||
| 
								 | 
							
									checkAsync(allowAsync || !ret.hasAsync);
							 | 
						||
| 
								 | 
							
									return ret.code;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * 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 {*}
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								let cacheTimeoutContext = null;
							 | 
						||
| 
								 | 
							
								let cacheTimeoutScript = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * 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) {
							 | 
						||
| 
								 | 
							
									if (!cacheTimeoutContext) {
							 | 
						||
| 
								 | 
							
										cacheTimeoutContext = createContext();
							 | 
						||
| 
								 | 
							
										cacheTimeoutScript = new Script('fn()', {
							 | 
						||
| 
								 | 
							
											__proto__: null,
							 | 
						||
| 
								 | 
							
											filename: 'timeout_bridge.js',
							 | 
						||
| 
								 | 
							
											displayErrors: false
							 | 
						||
| 
								 | 
							
										});
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									cacheTimeoutContext.fn = fn;
							 | 
						||
| 
								 | 
							
									try {
							 | 
						||
| 
								 | 
							
										return cacheTimeoutScript.runInContext(cacheTimeoutContext, {
							 | 
						||
| 
								 | 
							
											__proto__: null,
							 | 
						||
| 
								 | 
							
											displayErrors: false,
							 | 
						||
| 
								 | 
							
											timeout
							 | 
						||
| 
								 | 
							
										});
							 | 
						||
| 
								 | 
							
									} finally {
							 | 
						||
| 
								 | 
							
										cacheTimeoutContext.fn = null;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const bridgeScript = compileScript(`${__dirname}/bridge.js`,
							 | 
						||
| 
								 | 
							
									`(function(global) {"use strict"; const exports = {};${fs.readFileSync(`${__dirname}/bridge.js`, 'utf8')}\nreturn exports;})`);
							 | 
						||
| 
								 | 
							
								const setupSandboxScript = compileScript(`${__dirname}/setup-sandbox.js`,
							 | 
						||
| 
								 | 
							
									`(function(global, host, bridge, data, context) { ${fs.readFileSync(`${__dirname}/setup-sandbox.js`, 'utf8')}\n})`);
							 | 
						||
| 
								 | 
							
								const getGlobalScript = compileScript('get_global.js', 'this');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								let getGeneratorFunctionScript = null;
							 | 
						||
| 
								 | 
							
								let getAsyncFunctionScript = null;
							 | 
						||
| 
								 | 
							
								let getAsyncGeneratorFunctionScript = null;
							 | 
						||
| 
								 | 
							
								try {
							 | 
						||
| 
								 | 
							
									getGeneratorFunctionScript = compileScript('get_generator_function.js', '(function*(){}).constructor');
							 | 
						||
| 
								 | 
							
								} catch (ex) {}
							 | 
						||
| 
								 | 
							
								try {
							 | 
						||
| 
								 | 
							
									getAsyncFunctionScript = compileScript('get_async_function.js', '(async function(){}).constructor');
							 | 
						||
| 
								 | 
							
								} catch (ex) {}
							 | 
						||
| 
								 | 
							
								try {
							 | 
						||
| 
								 | 
							
									getAsyncGeneratorFunctionScript = compileScript('get_async_generator_function.js', '(async function*(){}).constructor');
							 | 
						||
| 
								 | 
							
								} catch (ex) {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Class VM.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class VM extends EventEmitter {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
									 * The timeout for {@link VM#run} calls.
							 | 
						||
| 
								 | 
							
									 *
							 | 
						||
| 
								 | 
							
									 * @public
							 | 
						||
| 
								 | 
							
									 * @since v3.9.0
							 | 
						||
| 
								 | 
							
									 * @member {number} timeout
							 | 
						||
| 
								 | 
							
									 * @memberOf VM#
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
									 * Get the global sandbox object.
							 | 
						||
| 
								 | 
							
									 *
							 | 
						||
| 
								 | 
							
									 * @public
							 | 
						||
| 
								 | 
							
									 * @readonly
							 | 
						||
| 
								 | 
							
									 * @since v3.9.0
							 | 
						||
| 
								 | 
							
									 * @member {Object} sandbox
							 | 
						||
| 
								 | 
							
									 * @memberOf VM#
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
									 * The compiler to use to get the JavaScript code.
							 | 
						||
| 
								 | 
							
									 *
							 | 
						||
| 
								 | 
							
									 * @public
							 | 
						||
| 
								 | 
							
									 * @readonly
							 | 
						||
| 
								 | 
							
									 * @since v3.9.0
							 | 
						||
| 
								 | 
							
									 * @member {(string|compileCallback)} compiler
							 | 
						||
| 
								 | 
							
									 * @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.allowAsync=true] - Allows for async functions.
							 | 
						||
| 
								 | 
							
									 * @throws {VMError} If the compiler is unknown.
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
									constructor(options = {}) {
							 | 
						||
| 
								 | 
							
										super();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Read all options
							 | 
						||
| 
								 | 
							
										const {
							 | 
						||
| 
								 | 
							
											timeout,
							 | 
						||
| 
								 | 
							
											sandbox,
							 | 
						||
| 
								 | 
							
											compiler = 'javascript',
							 | 
						||
| 
								 | 
							
											allowAsync: optAllowAsync = true
							 | 
						||
| 
								 | 
							
										} = options;
							 | 
						||
| 
								 | 
							
										const allowEval = options.eval !== false;
							 | 
						||
| 
								 | 
							
										const allowWasm = options.wasm !== false;
							 | 
						||
| 
								 | 
							
										const allowAsync = optAllowAsync && !options.fixAsync;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Early error if sandbox is not an object.
							 | 
						||
| 
								 | 
							
										if (sandbox && 'object' !== typeof sandbox) {
							 | 
						||
| 
								 | 
							
											throw new VMError('Sandbox must be object.');
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Early error if compiler can't be found.
							 | 
						||
| 
								 | 
							
										const resolvedCompiler = lookupCompiler(compiler);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Create a new context for this vm.
							 | 
						||
| 
								 | 
							
										const _context = createContext(undefined, {
							 | 
						||
| 
								 | 
							
											__proto__: null,
							 | 
						||
| 
								 | 
							
											codeGeneration: {
							 | 
						||
| 
								 | 
							
												__proto__: null,
							 | 
						||
| 
								 | 
							
												strings: allowEval,
							 | 
						||
| 
								 | 
							
												wasm: allowWasm
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const sandboxGlobal = getGlobalScript.runInContext(_context, DEFAULT_RUN_OPTIONS);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Initialize the sandbox bridge
							 | 
						||
| 
								 | 
							
										const {
							 | 
						||
| 
								 | 
							
											createBridge: sandboxCreateBridge
							 | 
						||
| 
								 | 
							
										} = bridgeScript.runInContext(_context, DEFAULT_RUN_OPTIONS)(sandboxGlobal);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Initialize the bridge
							 | 
						||
| 
								 | 
							
										const bridge = createBridge(sandboxCreateBridge, () => {});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const data = {
							 | 
						||
| 
								 | 
							
											__proto__: null,
							 | 
						||
| 
								 | 
							
											allowAsync
							 | 
						||
| 
								 | 
							
										};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (getGeneratorFunctionScript) {
							 | 
						||
| 
								 | 
							
											data.GeneratorFunction = getGeneratorFunctionScript.runInContext(_context, DEFAULT_RUN_OPTIONS);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if (getAsyncFunctionScript) {
							 | 
						||
| 
								 | 
							
											data.AsyncFunction = getAsyncFunctionScript.runInContext(_context, DEFAULT_RUN_OPTIONS);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if (getAsyncGeneratorFunctionScript) {
							 | 
						||
| 
								 | 
							
											data.AsyncGeneratorFunction = getAsyncGeneratorFunctionScript.runInContext(_context, DEFAULT_RUN_OPTIONS);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Create the bridge between the host and the sandbox.
							 | 
						||
| 
								 | 
							
										const internal = setupSandboxScript.runInContext(_context, DEFAULT_RUN_OPTIONS)(sandboxGlobal, HOST, bridge.other, data, _context);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const runScript = (script) => {
							 | 
						||
| 
								 | 
							
											// This closure is intentional to hide _context and bridge since the allow to access the sandbox directly which is unsafe.
							 | 
						||
| 
								 | 
							
											let ret;
							 | 
						||
| 
								 | 
							
											try {
							 | 
						||
| 
								 | 
							
												ret = script.runInContext(_context, DEFAULT_RUN_OPTIONS);
							 | 
						||
| 
								 | 
							
											} catch (e) {
							 | 
						||
| 
								 | 
							
												throw bridge.from(e);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											return bridge.from(ret);
							 | 
						||
| 
								 | 
							
										};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const makeReadonly = (value, mock) => {
							 | 
						||
| 
								 | 
							
											try {
							 | 
						||
| 
								 | 
							
												internal.readonly(value, mock);
							 | 
						||
| 
								 | 
							
											} catch (e) {
							 | 
						||
| 
								 | 
							
												throw bridge.from(e);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											return value;
							 | 
						||
| 
								 | 
							
										};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const makeProtected = (value) => {
							 | 
						||
| 
								 | 
							
											const sandboxBridge = bridge.other;
							 | 
						||
| 
								 | 
							
											try {
							 | 
						||
| 
								 | 
							
												sandboxBridge.fromWithFactory(sandboxBridge.protectedFactory, value);
							 | 
						||
| 
								 | 
							
											} catch (e) {
							 | 
						||
| 
								 | 
							
												throw bridge.from(e);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											return value;
							 | 
						||
| 
								 | 
							
										};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const addProtoMapping = (hostProto, sandboxProto) => {
							 | 
						||
| 
								 | 
							
											const sandboxBridge = bridge.other;
							 | 
						||
| 
								 | 
							
											let otherProto;
							 | 
						||
| 
								 | 
							
											try {
							 | 
						||
| 
								 | 
							
												otherProto = sandboxBridge.from(sandboxProto);
							 | 
						||
| 
								 | 
							
												sandboxBridge.addProtoMapping(otherProto, hostProto);
							 | 
						||
| 
								 | 
							
											} catch (e) {
							 | 
						||
| 
								 | 
							
												throw bridge.from(e);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											bridge.addProtoMapping(hostProto, otherProto);
							 | 
						||
| 
								 | 
							
										};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const addProtoMappingFactory = (hostProto, sandboxProtoFactory) => {
							 | 
						||
| 
								 | 
							
											const sandboxBridge = bridge.other;
							 | 
						||
| 
								 | 
							
											const factory = () => {
							 | 
						||
| 
								 | 
							
												const proto = sandboxProtoFactory(this);
							 | 
						||
| 
								 | 
							
												bridge.addProtoMapping(hostProto, proto);
							 | 
						||
| 
								 | 
							
												return proto;
							 | 
						||
| 
								 | 
							
											};
							 | 
						||
| 
								 | 
							
											try {
							 | 
						||
| 
								 | 
							
												const otherProtoFactory = sandboxBridge.from(factory);
							 | 
						||
| 
								 | 
							
												sandboxBridge.addProtoMappingFactory(otherProtoFactory, hostProto);
							 | 
						||
| 
								 | 
							
											} catch (e) {
							 | 
						||
| 
								 | 
							
												throw bridge.from(e);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Define the properties of this object.
							 | 
						||
| 
								 | 
							
										// Use Object.defineProperties here to be able to
							 | 
						||
| 
								 | 
							
										// hide and set properties read-only.
							 | 
						||
| 
								 | 
							
										objectDefineProperties(this, {
							 | 
						||
| 
								 | 
							
											__proto__: null,
							 | 
						||
| 
								 | 
							
											timeout: {
							 | 
						||
| 
								 | 
							
												__proto__: null,
							 | 
						||
| 
								 | 
							
												value: timeout,
							 | 
						||
| 
								 | 
							
												writable: true,
							 | 
						||
| 
								 | 
							
												enumerable: true
							 | 
						||
| 
								 | 
							
											},
							 | 
						||
| 
								 | 
							
											compiler: {
							 | 
						||
| 
								 | 
							
												__proto__: null,
							 | 
						||
| 
								 | 
							
												value: compiler,
							 | 
						||
| 
								 | 
							
												enumerable: true
							 | 
						||
| 
								 | 
							
											},
							 | 
						||
| 
								 | 
							
											sandbox: {
							 | 
						||
| 
								 | 
							
												__proto__: null,
							 | 
						||
| 
								 | 
							
												value: bridge.from(sandboxGlobal),
							 | 
						||
| 
								 | 
							
												enumerable: true
							 | 
						||
| 
								 | 
							
											},
							 | 
						||
| 
								 | 
							
											_runScript: {__proto__: null, value: runScript},
							 | 
						||
| 
								 | 
							
											_makeReadonly: {__proto__: null, value: makeReadonly},
							 | 
						||
| 
								 | 
							
											_makeProtected: {__proto__: null, value: makeProtected},
							 | 
						||
| 
								 | 
							
											_addProtoMapping: {__proto__: null, value: addProtoMapping},
							 | 
						||
| 
								 | 
							
											_addProtoMappingFactory: {__proto__: null, value: addProtoMappingFactory},
							 | 
						||
| 
								 | 
							
											_compiler: {__proto__: null, value: resolvedCompiler},
							 | 
						||
| 
								 | 
							
											_allowAsync: {__proto__: null, value: allowAsync}
							 | 
						||
| 
								 | 
							
										});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// prepare global sandbox
							 | 
						||
| 
								 | 
							
										if (sandbox) {
							 | 
						||
| 
								 | 
							
											this.setGlobals(sandbox);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
									 * Adds all the values to the globals.
							 | 
						||
| 
								 | 
							
									 *
							 | 
						||
| 
								 | 
							
									 * @public
							 | 
						||
| 
								 | 
							
									 * @since v3.9.0
							 | 
						||
| 
								 | 
							
									 * @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.sandbox[name] = values[name];
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return this;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
									 * Set a global value.
							 | 
						||
| 
								 | 
							
									 *
							 | 
						||
| 
								 | 
							
									 * @public
							 | 
						||
| 
								 | 
							
									 * @since v3.9.0
							 | 
						||
| 
								 | 
							
									 * @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.sandbox[name] = value;
							 | 
						||
| 
								 | 
							
										return this;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
									 * Get a global value.
							 | 
						||
| 
								 | 
							
									 *
							 | 
						||
| 
								 | 
							
									 * @public
							 | 
						||
| 
								 | 
							
									 * @since v3.9.0
							 | 
						||
| 
								 | 
							
									 * @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.sandbox[name];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
									 * Freezes the object inside VM making it read-only. Not available for primitive values.
							 | 
						||
| 
								 | 
							
									 *
							 | 
						||
| 
								 | 
							
									 * @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.readonly(value);
							 | 
						||
| 
								 | 
							
										if (globalName) this.sandbox[globalName] = value;
							 | 
						||
| 
								 | 
							
										return value;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
									 * Freezes the object inside VM making it read-only. Not available for primitive values.
							 | 
						||
| 
								 | 
							
									 *
							 | 
						||
| 
								 | 
							
									 * @public
							 | 
						||
| 
								 | 
							
									 * @param {*} value - Object to freeze.
							 | 
						||
| 
								 | 
							
									 * @param {*} [mock] - When the object does not have a property the mock is used before prototype lookup.
							 | 
						||
| 
								 | 
							
									 * @return {*} Object to freeze.
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
									readonly(value, mock) {
							 | 
						||
| 
								 | 
							
										return this._makeReadonly(value, mock);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
									 * Protects the object inside VM making impossible to set functions as it's properties. Not available for primitive values.
							 | 
						||
| 
								 | 
							
									 *
							 | 
						||
| 
								 | 
							
									 * @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._makeProtected(value);
							 | 
						||
| 
								 | 
							
										if (globalName) this.sandbox[globalName] = value;
							 | 
						||
| 
								 | 
							
										return value;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
									 * Run the code in VM.
							 | 
						||
| 
								 | 
							
									 *
							 | 
						||
| 
								 | 
							
									 * @public
							 | 
						||
| 
								 | 
							
									 * @param {(string|VMScript)} 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.<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, options) {
							 | 
						||
| 
								 | 
							
										let script;
							 | 
						||
| 
								 | 
							
										let filename;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (typeof options === 'object') {
							 | 
						||
| 
								 | 
							
											filename = options.filename;
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											filename = options;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (code instanceof VMScript) {
							 | 
						||
| 
								 | 
							
											script = code._compileVM();
							 | 
						||
| 
								 | 
							
											checkAsync(this._allowAsync || !code._hasAsync);
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											const useFileName = filename || 'vm.js';
							 | 
						||
| 
								 | 
							
											let scriptCode = this._compiler(code, useFileName);
							 | 
						||
| 
								 | 
							
											const ret = transformer(null, scriptCode, false, false, useFileName);
							 | 
						||
| 
								 | 
							
											scriptCode = ret.code;
							 | 
						||
| 
								 | 
							
											checkAsync(this._allowAsync || !ret.hasAsync);
							 | 
						||
| 
								 | 
							
											// Compile the script here so that we don't need to create a instance of VMScript.
							 | 
						||
| 
								 | 
							
											script = new Script(scriptCode, {
							 | 
						||
| 
								 | 
							
												__proto__: null,
							 | 
						||
| 
								 | 
							
												filename: useFileName,
							 | 
						||
| 
								 | 
							
												displayErrors: false
							 | 
						||
| 
								 | 
							
											});
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (!this.timeout) {
							 | 
						||
| 
								 | 
							
											return this._runScript(script);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return doWithTimeout(() => {
							 | 
						||
| 
								 | 
							
											return this._runScript(script);
							 | 
						||
| 
								 | 
							
										}, this.timeout);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
									 * Run the code in VM.
							 | 
						||
| 
								 | 
							
									 *
							 | 
						||
| 
								 | 
							
									 * @public
							 | 
						||
| 
								 | 
							
									 * @since v3.9.0
							 | 
						||
| 
								 | 
							
									 * @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);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.VM = VM;
							 |