Ghost/core/server/services/apps/sandbox.js
2017-12-13 20:57:11 +01:00

97 lines
3.0 KiB
JavaScript

var path = require('path'),
Module = require('module'),
common = require('../../lib/common'),
_ = require('lodash');
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);
if (this.opts.internal) {
currentModule.load(currentModule.id);
return currentModule.exports;
}
// 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.
if (_.includes(self.opts.blacklist, module)) {
throw new Error(common.i18n.t('errors.apps.unsafeAppRequire.error', {msg: module}));
}
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) === '..') {
throw new Error(common.i18n.t('errors.apps.unsafeAppRequire.error', {msg: relPath}));
}
// 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 = {
blacklist: ['knex', 'fs-extra', 'http', 'sqlite3', 'mysql', 'ghost']
};
module.exports = AppSandbox;