123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- 'use strict';
- // Translate the old options to the new Resolver functionality.
- const fs = require('fs');
- const pa = require('path');
- const nmod = require('module');
- const {EventEmitter} = require('events');
- const util = require('util');
- const {
- Resolver,
- DefaultResolver
- } = require('./resolver');
- const {VMScript} = require('./script');
- const {VM} = require('./vm');
- const {VMError} = require('./bridge');
- /**
- * Require wrapper to be able to annotate require with webpackIgnore.
- *
- * @private
- * @param {string} moduleName - Name of module to load.
- * @return {*} Module exports.
- */
- function defaultRequire(moduleName) {
- // Set module.parser.javascript.commonjsMagicComments=true in your webpack config.
- // eslint-disable-next-line global-require
- return require(/* webpackIgnore: true */ moduleName);
- }
- // source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
- function escapeRegExp(string) {
- return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
- }
- function makeExternalMatcherRegex(obj) {
- return escapeRegExp(obj).replace(/\\\\|\//g, '[\\\\/]')
- .replace(/\\\*\\\*/g, '.*').replace(/\\\*/g, '[^\\\\/]*').replace(/\\\?/g, '[^\\\\/]');
- }
- function makeExternalMatcher(obj) {
- const regexString = makeExternalMatcherRegex(obj);
- return new RegExp(`[\\\\/]node_modules[\\\\/]${regexString}(?:[\\\\/](?!(?:.*[\\\\/])?node_modules[\\\\/]).*)?$`);
- }
- class LegacyResolver extends DefaultResolver {
- constructor(builtinModules, checkPath, globalPaths, pathContext, customResolver, hostRequire, compiler, externals, allowTransitive) {
- super(builtinModules, checkPath, globalPaths, pathContext, customResolver, hostRequire, compiler);
- this.externals = externals;
- this.currMod = undefined;
- this.trustedMods = new WeakMap();
- this.allowTransitive = allowTransitive;
- }
- isPathAllowed(path) {
- return this.isPathAllowedForModule(path, this.currMod);
- }
- isPathAllowedForModule(path, mod) {
- if (!super.isPathAllowed(path)) return false;
- if (mod) {
- if (mod.allowTransitive) return true;
- if (path.startsWith(mod.path)) {
- const rem = path.slice(mod.path.length);
- if (!/(?:^|[\\\\/])node_modules(?:$|[\\\\/])/.test(rem)) return true;
- }
- }
- return this.externals.some(regex => regex.test(path));
- }
- registerModule(mod, filename, path, parent, direct) {
- const trustedParent = this.trustedMods.get(parent);
- this.trustedMods.set(mod, {
- filename,
- path,
- paths: this.genLookupPaths(path),
- allowTransitive: this.allowTransitive &&
- ((direct && trustedParent && trustedParent.allowTransitive) || this.externals.some(regex => regex.test(filename)))
- });
- }
- resolveFull(mod, x, options, ext, direct) {
- this.currMod = undefined;
- if (!direct) return super.resolveFull(mod, x, options, ext, false);
- const trustedMod = this.trustedMods.get(mod);
- if (!trustedMod || mod.path !== trustedMod.path) return super.resolveFull(mod, x, options, ext, false);
- const paths = [...mod.paths];
- if (paths.length === trustedMod.length) {
- for (let i = 0; i < paths.length; i++) {
- if (paths[i] !== trustedMod.paths[i]) {
- return super.resolveFull(mod, x, options, ext, false);
- }
- }
- }
- const extCopy = Object.assign({__proto__: null}, ext);
- try {
- this.currMod = trustedMod;
- return super.resolveFull(trustedMod, x, undefined, extCopy, true);
- } finally {
- this.currMod = undefined;
- }
- }
- checkAccess(mod, filename) {
- const trustedMod = this.trustedMods.get(mod);
- if ((!trustedMod || trustedMod.filename !== filename) && !this.isPathAllowedForModule(filename, undefined)) {
- throw new VMError(`Module '${filename}' is not allowed to be required. The path is outside the border!`, 'EDENIED');
- }
- }
- loadJS(vm, mod, filename) {
- filename = this.pathResolve(filename);
- this.checkAccess(mod, filename);
- if (this.pathContext(filename, 'js') === 'sandbox') {
- const trustedMod = this.trustedMods.get(mod);
- const script = this.readScript(filename);
- vm.run(script, {filename, strict: true, module: mod, wrapper: 'none', dirname: trustedMod ? trustedMod.path : mod.path});
- } else {
- const m = this.hostRequire(filename);
- mod.exports = vm.readonly(m);
- }
- }
- }
- function defaultBuiltinLoader(resolver, vm, id) {
- const mod = resolver.hostRequire(id);
- return vm.readonly(mod);
- }
- const eventsModules = new WeakMap();
- function defaultBuiltinLoaderEvents(resolver, vm, id) {
- return eventsModules.get(vm);
- }
- let cacheBufferScript;
- function defaultBuiltinLoaderBuffer(resolver, vm, id) {
- if (!cacheBufferScript) {
- cacheBufferScript = new VMScript('return buffer=>({Buffer: buffer});', {__proto__: null, filename: 'buffer.js'});
- }
- const makeBuffer = vm.run(cacheBufferScript, {__proto__: null, strict: true, wrapper: 'none'});
- return makeBuffer(Buffer);
- }
- let cacheUtilScript;
- function defaultBuiltinLoaderUtil(resolver, vm, id) {
- if (!cacheUtilScript) {
- cacheUtilScript = new VMScript(`return function inherits(ctor, superCtor) {
- ctor.super_ = superCtor;
- Object.setPrototypeOf(ctor.prototype, superCtor.prototype);
- }`, {__proto__: null, filename: 'util.js'});
- }
- const inherits = vm.run(cacheUtilScript, {__proto__: null, strict: true, wrapper: 'none'});
- const copy = Object.assign({}, util);
- copy.inherits = inherits;
- return vm.readonly(copy);
- }
- const BUILTIN_MODULES = (nmod.builtinModules || Object.getOwnPropertyNames(process.binding('natives'))).filter(s=>!s.startsWith('internal/'));
- let EventEmitterReferencingAsyncResourceClass = null;
- if (EventEmitter.EventEmitterAsyncResource) {
- // eslint-disable-next-line global-require
- const {AsyncResource} = require('async_hooks');
- const kEventEmitter = Symbol('kEventEmitter');
- class EventEmitterReferencingAsyncResource extends AsyncResource {
- constructor(ee, type, options) {
- super(type, options);
- this[kEventEmitter] = ee;
- }
- get eventEmitter() {
- return this[kEventEmitter];
- }
- }
- EventEmitterReferencingAsyncResourceClass = EventEmitterReferencingAsyncResource;
- }
- let cacheEventsScript;
- const SPECIAL_MODULES = {
- events(vm) {
- if (!cacheEventsScript) {
- const eventsSource = fs.readFileSync(`${__dirname}/events.js`, 'utf8');
- cacheEventsScript = new VMScript(`(function (fromhost) { const module = {}; module.exports={};{ ${eventsSource}
- } return module.exports;})`, {filename: 'events.js'});
- }
- const closure = VM.prototype.run.call(vm, cacheEventsScript);
- const eventsInstance = closure(vm.readonly({
- kErrorMonitor: EventEmitter.errorMonitor,
- once: EventEmitter.once,
- on: EventEmitter.on,
- getEventListeners: EventEmitter.getEventListeners,
- EventEmitterReferencingAsyncResource: EventEmitterReferencingAsyncResourceClass
- }));
- eventsModules.set(vm, eventsInstance);
- vm._addProtoMapping(EventEmitter.prototype, eventsInstance.EventEmitter.prototype);
- return defaultBuiltinLoaderEvents;
- },
- buffer(vm) {
- return defaultBuiltinLoaderBuffer;
- },
- util(vm) {
- return defaultBuiltinLoaderUtil;
- }
- };
- function addDefaultBuiltin(builtins, key, vm) {
- if (builtins[key]) return;
- const special = SPECIAL_MODULES[key];
- builtins[key] = special ? special(vm) : defaultBuiltinLoader;
- }
- function genBuiltinsFromOptions(vm, builtinOpt, mockOpt, override) {
- const builtins = {__proto__: null};
- if (mockOpt) {
- const keys = Object.getOwnPropertyNames(mockOpt);
- for (let i = 0; i < keys.length; i++) {
- const key = keys[i];
- builtins[key] = (resolver, tvm, id) => tvm.readonly(mockOpt[key]);
- }
- }
- if (override) {
- const keys = Object.getOwnPropertyNames(override);
- for (let i = 0; i < keys.length; i++) {
- const key = keys[i];
- builtins[key] = override[key];
- }
- }
- if (Array.isArray(builtinOpt)) {
- const def = builtinOpt.indexOf('*') >= 0;
- if (def) {
- for (let i = 0; i < BUILTIN_MODULES.length; i++) {
- const name = BUILTIN_MODULES[i];
- if (builtinOpt.indexOf(`-${name}`) === -1) {
- addDefaultBuiltin(builtins, name, vm);
- }
- }
- } else {
- for (let i = 0; i < BUILTIN_MODULES.length; i++) {
- const name = BUILTIN_MODULES[i];
- if (builtinOpt.indexOf(name) !== -1) {
- addDefaultBuiltin(builtins, name, vm);
- }
- }
- }
- } else if (builtinOpt) {
- for (let i = 0; i < BUILTIN_MODULES.length; i++) {
- const name = BUILTIN_MODULES[i];
- if (builtinOpt[name]) {
- addDefaultBuiltin(builtins, name, vm);
- }
- }
- }
- return builtins;
- }
- function defaultCustomResolver() {
- return undefined;
- }
- const DENY_RESOLVER = new Resolver({__proto__: null}, [], id => {
- throw new VMError(`Access denied to require '${id}'`, 'EDENIED');
- });
- function resolverFromOptions(vm, options, override, compiler) {
- if (!options) {
- if (!override) return DENY_RESOLVER;
- const builtins = genBuiltinsFromOptions(vm, undefined, undefined, override);
- return new Resolver(builtins, [], defaultRequire);
- }
- const {
- builtin: builtinOpt,
- mock: mockOpt,
- external: externalOpt,
- root: rootPaths,
- resolve: customResolver,
- customRequire: hostRequire = defaultRequire,
- context = 'host'
- } = options;
- const builtins = genBuiltinsFromOptions(vm, builtinOpt, mockOpt, override);
- if (!externalOpt) return new Resolver(builtins, [], hostRequire);
- let checkPath;
- if (rootPaths) {
- const checkedRootPaths = (Array.isArray(rootPaths) ? rootPaths : [rootPaths]).map(f => pa.resolve(f));
- checkPath = (filename) => {
- return checkedRootPaths.some(path => {
- if (!filename.startsWith(path)) return false;
- const len = path.length;
- if (filename.length === len || (len > 0 && path[len-1] === pa.sep)) return true;
- const sep = filename[len];
- return sep === '/' || sep === pa.sep;
- });
- };
- } else {
- checkPath = () => true;
- }
- let newCustomResolver = defaultCustomResolver;
- let externals = undefined;
- let external = undefined;
- if (customResolver) {
- let externalCache;
- newCustomResolver = (resolver, x, path, extList) => {
- if (external && !(resolver.pathIsAbsolute(x) || resolver.pathIsRelative(x))) {
- if (!externalCache) {
- externalCache = external.map(ext => new RegExp(makeExternalMatcherRegex(ext)));
- }
- if (!externalCache.some(regex => regex.test(x))) return undefined;
- }
- const resolved = customResolver(x, path);
- if (!resolved) return undefined;
- if (externals) externals.push(new RegExp('^' + escapeRegExp(resolved)));
- return resolver.loadAsFileOrDirecotry(resolved, extList);
- };
- }
- if (typeof externalOpt !== 'object') {
- return new DefaultResolver(builtins, checkPath, [], () => context, newCustomResolver, hostRequire, compiler);
- }
- let transitive = false;
- if (Array.isArray(externalOpt)) {
- external = externalOpt;
- } else {
- external = externalOpt.modules;
- transitive = context === 'sandbox' && externalOpt.transitive;
- }
- externals = external.map(makeExternalMatcher);
- return new LegacyResolver(builtins, checkPath, [], () => context, newCustomResolver, hostRequire, compiler, externals, transitive);
- }
- exports.resolverFromOptions = resolverFromOptions;
|