baset-vm
Version:
VM package for BaseT project.
165 lines • 6.37 kB
JavaScript
;
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