UNPKG

baset-vm

Version:

VM package for BaseT project.

165 lines 6.37 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const vm_1 = __importDefault(require("vm")); const VMError_1 = require("./VMError"); exports.VMError = VMError_1.VMError; const VMScript_1 = require("./VMScript"); exports.VMScript = VMScript_1.VMScript; const sb = fs_1.default.readFileSync(`${__dirname}${path_1.default.sep}sandbox.js`, 'utf8'); function compileToJS(code, compiler, filename = '') { if (typeof compiler === 'function') return compiler(code, filename); switch (compiler) { case 'javascript': case 'java-script': case 'js': case 'text/javascript': return code; default: throw new VMError_1.VMError(`Unsupported compiler '${compiler}'.`); } } /** * Class NodeVM. */ class NodeVM extends events_1.EventEmitter { /** * Create NodeVM instance. * Unlike VM, NodeVM lets you use require same way like in regular node. * @param options VM options. */ constructor(options = {}) { super(); // defaults this.options = { sandbox: options.sandbox || {}, console: options.console || 'inherit', require: options.require || false, compiler: options.compiler || 'javascript', nesting: options.nesting || false, wrapper: options.wrapper || 'commonjs', sourceExtensions: options.sourceExtensions || ['.js'], resolveFilename: options.resolveFilename || false, timeout: options.timeout || 0, }; const nesting = (this.options.nesting) ? { NodeVM, } : {}; const host = Object.assign({ require, process, console, setTimeout, setInterval, setImmediate, clearTimeout, clearInterval, clearImmediate, String, Number, Buffer, Boolean, Array, Date, Error, RangeError, ReferenceError, SyntaxError, TypeError, RegExp, Object, VMError: VMError_1.VMError, Proxy, Reflect, Map, WeakMap, Set, WeakSet, Promise }, nesting, this.options.sandbox); this.context = vm_1.default.createContext(); const closure = vm_1.default.runInContext(sb, this.context, { filename: `${__dirname}${path_1.default.sep}sandbox.js`, displayErrors: false, }); Object.setPrototypeOf(host, global); this.prepareRequire = closure.call(this.context, this, host); if (this.options.require && this.options.require !== true && this.options.require.import) { if (!Array.isArray(this.options.require.import)) { this.options.require.import = [this.options.require.import]; } for (let i = 0, l = this.options.require.import.length; i < l; i++) { this.require(this.options.require.import[i]); } } } /** * Require a module in VM and return it's exports. * @param module Module name. * @returns Exported module. */ require(module) { return this.run(`module.exports = require('${module}');`, 'vm.js'); } /** * Run the code in NodeVM. * First time you run this method, code is executed same way like in node's regular `require` - * it's executed with `module`, `require`, `exports`, `__dirname`, `__filename` variables and expect result in `module.exports'. * @param code Code to run. * @param filename Filename that shows up in any stack traces produced from this script. * @returns Result of executed code. */ run(codeInput, filenameInput) { const code = (this.options.compiler !== 'javascript') ? compileToJS(codeInput, this.options.compiler, filenameInput) : ''; const filename = filenameInput && path_1.default.resolve(filenameInput); const dirname = filename && path_1.default.dirname(filename); const module = vm_1.default.runInContext('({exports: {}})', this.context, { displayErrors: false, }); const script = new VMScript_1.VMScript(code, filename); script.wrap('(function (exports, require, module, __filename, __dirname) { ', ' \n})'); const closure = script.compile().runInContext(this.context, { filename: script.filename, displayErrors: false, }); const returned = closure.call(this.context, module.exports, this.prepareRequire(dirname), module, filename, dirname); return (this.options.wrapper === 'commonjs') ? module.exports : returned; } static code(script, filename, options) { const resultFilename = (filename !== undefined && typeof filename === 'string') ? path_1.default.resolve(filename) : ''; const resultOptions = (filename !== undefined && typeof filename === 'object') ? filename : options; return new NodeVM(resultOptions).run(script, resultFilename); } /** * Create NodeVM and run script from file inside it. * @param filename File name (used in stack traces only). * @param options VM options. * @returns VM. */ static file(filename, options) { const resolvedFilename = path_1.default.resolve(filename); if (!fs_1.default.existsSync(resolvedFilename)) { throw new VMError_1.VMError(`Script '${resolvedFilename}' not found.`); } if (fs_1.default.statSync(resolvedFilename).isDirectory()) { throw new VMError_1.VMError('Script must be file, got directory.'); } return new NodeVM(options).run(fs_1.default.readFileSync(resolvedFilename, 'utf8'), resolvedFilename); } } exports.NodeVM = NodeVM; //# sourceMappingURL=index.js.map