Преглед изворни кода

fix: forbid {...objSpread} in injected/* (#1343)

Our .browserslistrc targets old browsers so the compiled code for {...objSpread} uses babel's polyfill that calls methods like `Object.assign` instead of our safe `assign`
tophf пре 4 година
родитељ
комит
4c00c5c1bf
6 измењених фајлова са 41 додато и 11 уклоњено
  1. 21 4
      .eslintrc.js
  2. 4 0
      src/common/consts.js
  3. 4 0
      src/common/index.js
  4. 4 0
      src/common/object.js
  5. 6 2
      src/common/util.js
  6. 2 5
      src/injected/content/index.js

+ 21 - 4
.eslintrc.js

@@ -1,3 +1,12 @@
+const unsafeEnvironment = [
+  'src/injected/**/*.js',
+  // these are used by `injected`
+  'src/common/browser.js',
+  'src/common/consts.js',
+  'src/common/index.js',
+  'src/common/object.js',
+  'src/common/util.js',
+];
 module.exports = {
   root: true,
   extends: [
@@ -13,13 +22,21 @@ module.exports = {
     // `browser` is a local variable since we remove the global `chrome` and `browser` in injected*
     // to prevent exposing them to userscripts with `@inject-into content`
     files: ['*'],
-    excludedFiles: [
-      'src/injected/**/*.js',
-      'src/common/*.js',
-    ],
+    excludedFiles: unsafeEnvironment,
     globals: {
       browser: false,
     },
+  }, {
+    files: unsafeEnvironment,
+    rules: {
+      /* Our .browserslistrc targets old browsers so the compiled code for {...objSpread} uses
+         babel's polyfill that calls methods like `Object.assign` instead of our safe `assign`.
+         Ideally, `eslint-plugin-compat` should be used but I couldn't make it work. */
+      'no-restricted-syntax': ['error', {
+        selector: 'ObjectExpression > ExperimentalSpreadProperty',
+        message: 'Object spread in an unsafe environment',
+      }],
+    },
   }],
   rules: {
     'import/extensions': ['error', 'ignorePackages', {

+ 4 - 0
src/common/consts.js

@@ -1,3 +1,7 @@
+/* SAFETY WARNING! Exports used by `injected` must make ::safe() calls,
+   when accessed after the initial event loop task in `injected/web`
+   or after the first content-mode userscript runs in `injected/content` */
+
 export const INJECT_AUTO = 'auto';
 export const INJECT_PAGE = 'page';
 export const INJECT_CONTENT = 'content';

+ 4 - 0
src/common/index.js

@@ -1,3 +1,7 @@
+/* SAFETY WARNING! Exports used by `injected` must make ::safe() calls,
+   when accessed after the initial event loop task in `injected/web`
+   or after the first content-mode userscript runs in `injected/content` */
+
 import { browser } from '#/common/consts';
 import { deepCopy } from './object';
 import { noop } from './util';

+ 4 - 0
src/common/object.js

@@ -1,3 +1,7 @@
+/* SAFETY WARNING! Exports used by `injected` must make ::safe() calls,
+   when accessed after the initial event loop task in `injected/web`
+   or after the first content-mode userscript runs in `injected/content` */
+
 export const {
   assign,
   defineProperty,

+ 6 - 2
src/common/util.js

@@ -1,9 +1,13 @@
+/* SAFETY WARNING! Exports used by `injected` must make ::safe() calls,
+   when accessed after the initial event loop task in `injected/web`
+   or after the first content-mode userscript runs in `injected/content` */
+
 import { browser } from '#/common/consts';
 
 // used in an unsafe context so we need to save the original functions
 const perfNow = performance.now.bind(performance);
 const { random, floor } = Math;
-export const { toString: numberToString } = Number.prototype;
+export const { toString: numberToString } = 0;
 
 export function i18n(name, args) {
   return browser.i18n.getMessage(name, args) || name;
@@ -173,7 +177,7 @@ export function formatTime(duration) {
 }
 
 // used in an unsafe context so we need to save the original functions
-export const { hasOwnProperty } = Object.prototype;
+export const { hasOwnProperty } = {};
 export function isEmpty(obj) {
   for (const key in obj) {
     if (obj::hasOwnProperty(key)) {

+ 2 - 5
src/injected/content/index.js

@@ -1,6 +1,6 @@
 import { getUniqId, isEmpty, sendCmd } from '#/common';
 import { INJECT_CONTENT } from '#/common/consts';
-import { objectKeys, objectPick } from '#/common/object';
+import { assign, objectKeys, objectPick } from '#/common/object';
 import { bindEvents } from '../utils';
 import {
   forEach, includes, append, createElementNS, document, setAttribute, NS_HTML,
@@ -119,10 +119,7 @@ async function sendSetPopup(isDelayed) {
       await pendingSetPopup;
       pendingSetPopup = null;
     }
-    sendCmd('SetPopup', {
-      menus,
-      ...objectPick(bridge, ['ids', 'failedIds', 'injectInto']),
-    });
+    sendCmd('SetPopup', assign(menus, objectPick(bridge, ['ids', 'failedIds', 'injectInto'])));
   }
 }