2014-02-01 02:53:27 +04:00
|
|
|
|
2014-02-27 06:44:09 +04:00
|
|
|
var path = require('path'),
|
2014-02-05 12:40:30 +04:00
|
|
|
Module = require('module'),
|
2015-11-12 15:29:45 +03:00
|
|
|
i18n = require('../i18n'),
|
2014-02-05 12:40:30 +04:00
|
|
|
_ = require('lodash');
|
2014-02-01 02:53:27 +04:00
|
|
|
|
|
|
|
function AppSandbox(opts) {
|
|
|
|
this.opts = _.defaults(opts || {}, AppSandbox.defaults);
|
|
|
|
}
|
|
|
|
|
|
|
|
AppSandbox.prototype.loadApp = function loadAppSandboxed(appPath) {
|
|
|
|
var appFile = require.resolve(appPath),
|
|
|
|
appBase = path.dirname(appFile);
|
|
|
|
|
|
|
|
this.opts.appRoot = appBase;
|
|
|
|
|
|
|
|
return this.loadModule(appPath);
|
|
|
|
};
|
|
|
|
|
|
|
|
AppSandbox.prototype.loadModule = function loadModuleSandboxed(modulePath) {
|
|
|
|
// Set loaded modules parent to this
|
|
|
|
var self = this,
|
|
|
|
moduleDir = path.dirname(modulePath),
|
|
|
|
parentModulePath = self.opts.parent || module.parent,
|
|
|
|
appRoot = self.opts.appRoot || moduleDir,
|
|
|
|
currentModule,
|
|
|
|
nodeRequire;
|
|
|
|
|
|
|
|
// Resolve the modules path
|
|
|
|
modulePath = Module._resolveFilename(modulePath, parentModulePath);
|
|
|
|
|
|
|
|
// Instantiate a Node Module class
|
|
|
|
currentModule = new Module(modulePath, parentModulePath);
|
|
|
|
|
2016-04-11 16:58:41 +03:00
|
|
|
if (this.opts.internal) {
|
|
|
|
currentModule.load(currentModule.id);
|
|
|
|
|
|
|
|
return currentModule.exports;
|
|
|
|
}
|
|
|
|
|
2014-02-01 02:53:27 +04:00
|
|
|
// Grab the original modules require function
|
|
|
|
nodeRequire = currentModule.require;
|
|
|
|
|
|
|
|
// Set a new proxy require function
|
|
|
|
currentModule.require = function requireProxy(module) {
|
|
|
|
// check whitelist, plugin config, etc.
|
2016-06-11 21:23:27 +03:00
|
|
|
if (_.includes(self.opts.blacklist, module)) {
|
2015-11-12 15:29:45 +03:00
|
|
|
throw new Error(i18n.t('errors.apps.unsafeAppRequire.error', {msg: module}));
|
2014-02-01 02:53:27 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
var firstTwo = module.slice(0, 2),
|
|
|
|
resolvedPath,
|
|
|
|
relPath,
|
|
|
|
innerBox,
|
|
|
|
newOpts;
|
|
|
|
|
|
|
|
// Load relative modules with their own sandbox
|
|
|
|
if (firstTwo === './' || firstTwo === '..') {
|
|
|
|
// Get the path relative to the modules directory
|
|
|
|
resolvedPath = path.resolve(moduleDir, module);
|
|
|
|
|
|
|
|
// Check relative path from the appRoot for outside requires
|
|
|
|
relPath = path.relative(appRoot, resolvedPath);
|
|
|
|
if (relPath.slice(0, 2) === '..') {
|
2015-11-12 15:29:45 +03:00
|
|
|
throw new Error(i18n.t('errors.apps.unsafeAppRequire.error', {msg: relPath}));
|
2014-02-01 02:53:27 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Assign as new module path
|
|
|
|
module = resolvedPath;
|
|
|
|
|
|
|
|
// Pass down the same options
|
|
|
|
newOpts = _.extend({}, self.opts);
|
|
|
|
|
|
|
|
// Make sure the appRoot and parent are appropriate
|
|
|
|
newOpts.appRoot = appRoot;
|
|
|
|
newOpts.parent = currentModule.parent;
|
|
|
|
|
|
|
|
// Create the inner sandbox for loading this module.
|
|
|
|
innerBox = new AppSandbox(newOpts);
|
|
|
|
|
|
|
|
return innerBox.loadModule(module);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call the original require method for white listed named modules
|
|
|
|
return nodeRequire.call(currentModule, module);
|
|
|
|
};
|
|
|
|
|
|
|
|
currentModule.load(currentModule.id);
|
|
|
|
|
|
|
|
return currentModule.exports;
|
|
|
|
};
|
|
|
|
|
|
|
|
AppSandbox.defaults = {
|
2016-09-16 12:40:23 +03:00
|
|
|
blacklist: ['knex', 'fs', 'http', 'sqlite3', 'mysql', 'ghost']
|
2014-02-01 02:53:27 +04:00
|
|
|
};
|
|
|
|
|
2014-02-27 06:44:09 +04:00
|
|
|
module.exports = AppSandbox;
|