.eslintrc.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. const { readGlobalsFile } = require('./scripts/webpack-util');
  2. const ovr = makeOverrides();
  3. module.exports = {
  4. root: true,
  5. extends: [
  6. require.resolve('@gera2ld/plaid/eslint'),
  7. require.resolve('@gera2ld/plaid-common-vue/eslint/vue3-js'),
  8. ],
  9. plugins: ['jest'],
  10. rules: {
  11. 'prettier/prettier': 'off',
  12. 'no-use-before-define': ['error', {
  13. 'functions': false,
  14. 'classes': true,
  15. 'variables': true,
  16. 'allowNamedExports': true,
  17. }],
  18. // copied from airbnb-base, replaced 4 with 8
  19. 'object-curly-newline': ['error', {
  20. ObjectExpression: { minProperties: 8, multiline: true, consistent: true },
  21. ObjectPattern: { minProperties: 8, multiline: true, consistent: true },
  22. ImportDeclaration: { minProperties: 8, multiline: true, consistent: true },
  23. ExportDeclaration: { minProperties: 8, multiline: true, consistent: true },
  24. }],
  25. 'semi': ['error'],
  26. },
  27. overrides: [{
  28. // `browser` is a local variable since we remove the global `chrome` and `browser` in injected*
  29. // to prevent exposing them to userscripts with `@inject-into content`
  30. files: ['*'],
  31. excludedFiles: [...ovr.FILES_INJECTED, ...ovr.FILES_SHARED],
  32. globals: {
  33. browser: false,
  34. ...ovr.GLOBALS_COMMON,
  35. },
  36. }, {
  37. files: ovr.FILES_SHARED,
  38. globals: ovr.GLOBALS_COMMON,
  39. }, {
  40. files: ovr.FILES_WEB,
  41. globals: ovr.GLOBALS_WEB,
  42. }, {
  43. files: ovr.FILES_CONTENT,
  44. globals: ovr.GLOBALS_CONTENT,
  45. }, {
  46. files: ovr.FILES_INJECTED,
  47. excludedFiles: [...ovr.FILES_CONTENT, ...ovr.FILES_WEB],
  48. // intersection of globals in CONTENT and WEB
  49. globals: Object.keys(ovr.GLOBALS_CONTENT).reduce((res, key) => (
  50. Object.assign(res, key in ovr.GLOBALS_WEB && { [key]: false })
  51. ), {}),
  52. }, {
  53. files: [...ovr.FILES_INJECTED, ...ovr.FILES_SHARED],
  54. rules: ovr.INJECTED_RULES,
  55. }, {
  56. files: ovr.FILES_WEB,
  57. rules: {
  58. ...ovr.INJECTED_RULES,
  59. 'no-restricted-syntax': [
  60. ...ovr.INJECTED_RULES['no-restricted-syntax'],
  61. {
  62. selector: '[regex], NewExpression[callee.name="RegExp"]',
  63. message: 'RegExp internally depends on a *ton* of stuff that may be spoofed or broken',
  64. // https://262.ecma-international.org/12.0/#sec-regexpexec
  65. },
  66. ],
  67. },
  68. }, {
  69. // build scripts
  70. files: [
  71. '*.js',
  72. 'scripts/*.js',
  73. 'scripts/*.mjs',
  74. ],
  75. env: { node: true },
  76. rules: {
  77. 'global-require': 0,
  78. 'import/newline-after-import': 0,
  79. 'import/no-extraneous-dependencies': 0, // spits errors in github action
  80. 'import/extensions': 0,
  81. }
  82. }, {
  83. files: ['*.vue'],
  84. rules: {
  85. 'vue/multi-word-component-names': 0,
  86. },
  87. }, {
  88. files: ['test/**'],
  89. env: {
  90. 'jest/globals': true,
  91. },
  92. }],
  93. };
  94. function makeOverrides() {
  95. /* Note that `injected` uses several more `common` files indirectly, but we check just these
  96. * two automatically because they are trivial by design and must always pass the check */
  97. const GLOBALS_SHARED = getGlobals('*');
  98. const GLOBALS_INJECTED = {
  99. ...getGlobals('injected'),
  100. PAGE_MODE_HANDSHAKE: false,
  101. VAULT_ID: false,
  102. };
  103. function getGlobals(path) {
  104. const res = {};
  105. const { ast } = readGlobalsFile(path, { ast: true });
  106. ast.program.body.forEach(body => {
  107. const { declarations } = body.declaration || body;
  108. if (!declarations) return;
  109. declarations.forEach(function processId({
  110. id: {
  111. left,
  112. properties,
  113. name = left && left.name,
  114. },
  115. }) {
  116. if (name) {
  117. // const NAME = whatever
  118. // We consider `let` immutable too to avoid unintentional reassignment
  119. res[name] = false;
  120. } else if (properties) {
  121. // const { NAME1, prototype: { NAME2: ALIAS2 } } = whatever
  122. properties.forEach(({ value }) => processId({ id: value }));
  123. }
  124. });
  125. });
  126. return res;
  127. }
  128. return {
  129. FILES_CONTENT: [
  130. 'src/injected/index.js',
  131. 'src/injected/content/**/*.js',
  132. ],
  133. FILES_INJECTED: [
  134. 'src/injected/**/*.js',
  135. ],
  136. FILES_SHARED: [
  137. 'src/common/browser.js',
  138. 'src/common/consts.js',
  139. 'src/common/safe-globals-shared.js',
  140. ],
  141. FILES_WEB: [
  142. 'src/injected/web/**/*.js',
  143. ],
  144. GLOBALS_INJECTED,
  145. GLOBALS_SHARED,
  146. GLOBALS_COMMON: {
  147. ...GLOBALS_SHARED,
  148. ...getGlobals('common'),
  149. re: false, // transform-modern-regexp with useRe option
  150. },
  151. GLOBALS_CONTENT: {
  152. INIT_FUNC_NAME: false,
  153. ...GLOBALS_SHARED,
  154. ...getGlobals('injected/content'),
  155. ...GLOBALS_INJECTED,
  156. },
  157. GLOBALS_WEB: {
  158. ...GLOBALS_SHARED,
  159. ...getGlobals('injected/web'),
  160. ...GLOBALS_INJECTED,
  161. IS_FIREFOX: false, // passed as a parameter to VMInitInjection in webpack.conf.js
  162. },
  163. INJECTED_RULES: {
  164. 'no-restricted-imports': [
  165. 'error', {
  166. patterns: ['*/common', '*/common/*'],
  167. }
  168. ],
  169. 'no-restricted-syntax': [
  170. 'error', {
  171. selector: 'ObjectExpression > ExperimentalSpreadProperty',
  172. message: 'Object spread adds a polyfill in injected* even if unused by it',
  173. }, {
  174. selector: 'ArrayPattern',
  175. message: 'Destructuring via Symbol.iterator may be spoofed/broken in an unsafe environment',
  176. }, {
  177. selector: ':matches(ArrayExpression, CallExpression) > SpreadElement',
  178. message: 'Spreading via Symbol.iterator may be spoofed/broken in an unsafe environment',
  179. }, {
  180. selector: '[callee.object.name="Object"], MemberExpression[object.name="Object"]',
  181. message: 'Using potentially spoofed methods in an unsafe environment',
  182. // TODO: auto-generate the rule using GLOBALS
  183. }, {
  184. selector: `CallExpression[callee.name="defineProperty"]:not(${[
  185. '[arguments.2.properties.0.key.name="__proto__"]',
  186. ':has(CallExpression[callee.name="nullObjFrom"])'
  187. ].join(',')})`,
  188. message: 'Prototype of descriptor may be spoofed/broken in an unsafe environment',
  189. }
  190. ],
  191. },
  192. };
  193. }