123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- const fs = require('fs');
- const babelCore = require('@babel/core');
- const webpack = require('webpack');
- const entryGlobals = {
- 'common': [],
- 'injected/content': [],
- 'injected/web': [],
- };
- const entryPathToFilename = path => path === '*'
- ? `./src/common/safe-globals-shared.js`
- : `./src/${path}/safe-globals.js`;
- Object.entries(entryGlobals).forEach(([name, val]) => {
- const parts = name.split('/');
- if (parts[1]) parts[1] = name;
- val.push('*', ...parts);
- });
- exports.restrictedSyntax = (
- // Hiding `code` so eslint doesn't complain about invalid schema
- rules => rules.map(r => (
- Object.defineProperty(r, 'code', { enumerable: false, value: r.code })
- ))
- )([{
- selector: 'ArrayPattern',
- message: 'Destructuring via Symbol.iterator may be spoofed/broken in an unsafe environment',
- code: '[window.foo]=[]',
- }, {
- selector: ':matches(ArrayExpression, CallExpression) > SpreadElement',
- message: 'Spreading via Symbol.iterator may be spoofed/broken in an unsafe environment',
- code: 'open([...[]])',
- }, {
- selector: '[callee.object.name="Object"], MemberExpression[object.name="Object"]',
- message: 'Using potentially spoofed methods in an unsafe environment',
- code: 'Object.assign()',
- // TODO: auto-generate the rule using GLOBALS
- }, {
- selector: `CallExpression[callee.name="defineProperty"]:not(${[
- '[arguments.2.properties.0.key.name="__proto__"]',
- ':has(CallExpression[callee.name="nullObjFrom"])'
- ].join(',')})`,
- message: 'Prototype of descriptor may be spoofed/broken in an unsafe environment',
- code: 'defineProperty(open, "foo", {foo:1})',
- }]);
- /**
- * Adds a watcher for files in entryGlobals to properly recompile the project on changes.
- */
- function addWrapperWithGlobals(name, config, defsObj, callback) {
- config.module.rules.push({
- test: new RegExp(`/${name}/.*?\\.js$`.replace(/\//g, /[/\\]/.source)),
- use: [{
- loader: './scripts/fake-dep-loader.js',
- options: { files: entryGlobals[name].map(entryPathToFilename) },
- }],
- });
- const defsRe = new RegExp(`\\b(${
- Object.keys(defsObj)
- .join('|')
- .replace(/\./g, '\\.')
- })\\b`, 'g');
- const reader = () => (
- entryGlobals[name]
- .map(path => readGlobalsFile(path))
- .join('\n')
- .replace(defsRe, s => defsObj[s])
- );
- const { header, footer, test } = callback(reader);
- config.plugins.push(
- new webpack.BannerPlugin({ test, raw: true, banner: header }),
- new webpack.BannerPlugin({ test, raw: true, banner: footer, footer: true })
- );
- }
- function getCodeMirrorThemes() {
- const name = 'neo.css';
- return fs.readdirSync(
- require.resolve(`codemirror/theme/${name}`).slice(0, -name.length),
- { withFileTypes: true },
- ).map(e => e.isFile() && e.name.endsWith('.css') && e.name.slice(0, -4))
- .filter(Boolean);
- }
- function readGlobalsFile(path, babelOpts = {}) {
- const { ast, code = !ast } = babelOpts;
- const filename = entryPathToFilename(path);
- const src = fs.readFileSync(filename, { encoding: 'utf8' })
- .replace(/\bexport\s+(function\s+(\w+))/g, 'const $2 = $1')
- .replace(/\bexport\s+(?=(const|let)\s)/g, '');
- const res = babelCore.transformSync(src, {
- ...babelOpts,
- ast,
- code,
- filename,
- });
- return ast ? res : res.code;
- }
- exports.addWrapperWithGlobals = addWrapperWithGlobals;
- exports.getCodeMirrorThemes = getCodeMirrorThemes;
- exports.readGlobalsFile = readGlobalsFile;
|