eslint-plugin-chrome-extensions 2.7.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +266 -43
  2. package/package.json +9 -8
  3. package/src/data/apis.js +74 -88
  4. package/src/data/manifest.js +3 -1
  5. package/src/data/regex.js +2 -1
  6. package/src/index.d.ts +42 -0
  7. package/src/rules/css-spacing.js +29 -21
  8. package/src/rules/manifest-key-order.js +1 -1
  9. package/src/rules/manifest-spacing.js +1 -1
  10. package/src/rules/no-content-style-leak.js +12 -11
  11. package/src/rules/no-deprecated-apis.js +2 -2
  12. package/src/rules/no-host-page-selectors.js +5 -6
  13. package/src/rules/no-inline-event-handlers.js +116 -0
  14. package/src/rules/no-inline-scripts.js +65 -0
  15. package/src/rules/no-insecure-fetch.js +7 -6
  16. package/src/rules/no-insecure-messaging.js +4 -4
  17. package/src/rules/no-invalid-locales.js +3 -5
  18. package/src/rules/no-missing-default-locale.js +1 -1
  19. package/src/rules/no-missing-permissions.js +2 -3
  20. package/src/rules/no-missing-web-accessible-resources.js +2 -2
  21. package/src/rules/no-remote-code.js +77 -28
  22. package/src/rules/no-remote-objects.js +55 -0
  23. package/src/rules/no-remote-urls.js +20 -20
  24. package/src/rules/no-scrollbar-styling.js +8 -10
  25. package/src/rules/no-unrestricted-host-permissions.js +1 -1
  26. package/src/rules/no-unused-permissions.js +5 -4
  27. package/src/rules/require-license-file.js +1 -1
  28. package/src/rules/require-mv3.js +3 -2
  29. package/src/rules/valid-filenames.js +3 -3
  30. package/src/rules/valid-manifest-icons.js +2 -2
  31. package/src/rules/valid-mv3-schema.js +5 -5
  32. package/src/rules/valid-url-fields.js +2 -2
  33. package/src/rules/valid-version-field.js +2 -2
  34. package/src/utils/api.js +20 -0
  35. package/src/utils/extension.js +1 -3
  36. package/src/utils/html.js +29 -0
  37. package/src/utils/i18n.js +2 -2
  38. package/src/utils/init.js +11 -3
  39. package/src/utils/license.js +1 -1
  40. package/src/utils/node.js +3 -1
  41. package/src/utils/pkg.js +19 -2
  42. package/src/utils/rules.js +1 -1
@@ -6,7 +6,7 @@ import regex from '../data/regex.js'
6
6
  const re = {
7
7
  absoluteURL: /^[a-z][a-z0-9+.-]*:\/\/[^\s/]+/i,
8
8
  match: /^(\*|https?|file|ftp):\/\/([^/]*)(\/.*)$/,
9
- template: /\{[^}]+\}/g
9
+ template: /\{[^{}]+\}/g
10
10
  }
11
11
 
12
12
  function collectStrings(obj, path = [], out = []) {
@@ -15,7 +15,7 @@ function collectStrings(obj, path = [], out = []) {
15
15
  else if (Array.isArray(obj))
16
16
  for (const elem of obj) collectStrings(elem, path, out)
17
17
  else if (typeof obj == 'object')
18
- for (const key of Object.keys(obj)) // eslint-disable-next-line security/detect-object-injection
18
+ for (const key of Object.keys(obj)) // oxlint-disable-next-line security/detect-object-injection
19
19
  collectStrings(obj[key], [...path, key], out)
20
20
  return out
21
21
  }
@@ -1,11 +1,11 @@
1
1
  import extension from '../utils/extension.js'
2
2
  import regex from '../data/regex.js'
3
3
 
4
- const re = { /* eslint-disable security/detect-unsafe-regex */
4
+ const re = { // oxlint-disable security/detect-unsafe-regex
5
5
  chromeParts: /^\d+(?:\.\d+){0,3}$/,
6
6
  date: /^\d{4}\.(?:0?[1-9]|1[0-2])\.(?:0?[1-9]|[12]\d|3[01])(?:\.\d+)?$/,
7
7
  semver: /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/
8
- } /* eslint-enable security/detect-unsafe-regex */
8
+ } // oxlint-enable security/detect-unsafe-regex
9
9
 
10
10
  export default {
11
11
  meta: {
@@ -0,0 +1,20 @@
1
+ import apis from '../data/apis.js'
2
+
3
+ export default {
4
+ findDeprecated(api) {
5
+ for (const [deprecated, replacement] of apis.deprecated)
6
+ if (api == deprecated || api.startsWith(`${deprecated}.`))
7
+ return { deprecated, replacement }
8
+ return null
9
+ },
10
+
11
+ findPermission(api) {
12
+ if (apis.permless.has(api)) return null
13
+ for (const [prefix, permission] of apis.permissionMap)
14
+ if (api == prefix || api.startsWith(`${prefix}.`))
15
+ return permission
16
+ return null
17
+ },
18
+
19
+ isExternalMsgListener(api) { return apis.externalMsgListeners.has(api) }
20
+ }
@@ -1,4 +1,4 @@
1
- /* eslint-disable security/detect-non-literal-fs-filename -- lint-time test fixtures only */
1
+ // oxlint-disable security/detect-non-literal-fs-filename
2
2
 
3
3
  import fs from 'node:fs'
4
4
  import path from 'node:path'
@@ -40,8 +40,6 @@ const utils = {
40
40
 
41
41
  manifest: {
42
42
  permission: {
43
- valid: permissions.valid,
44
- sensitive: permissions.sensitive,
45
43
  isValid(name, target = 'mv3') {
46
44
  if (permissions.valid.has(name)) return true
47
45
  if (target == 'mv3') return false
@@ -0,0 +1,29 @@
1
+ import regex from '../data/regex.js'
2
+
3
+ export default {
4
+ isHTML(text) { return text.trimStart().startsWith('<') },
5
+
6
+ isRemote(src, allowedPrefixes) {
7
+ if (!src || allowedPrefixes.some(prefix => src.startsWith(prefix))) return false
8
+ return regex.scheme.test(src)
9
+ },
10
+
11
+ offsetToLoc(text, offset) {
12
+ let line = 1, column = 1
13
+ for (let i = 0; i < offset && i < text.length; i++) {
14
+ if (text[i] == '\n') { line++ ; column = 1 } // oxlint-disable-line security/detect-object-injection
15
+ else column++
16
+ }
17
+ return { line, column }
18
+ },
19
+
20
+ pickAttrVal(match) { return match ? (match[1] ?? match[2] ?? match[3] ?? '') : null },
21
+
22
+ re: {
23
+ attr: {
24
+ data: /\sdata\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i,
25
+ src: /\ssrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i,
26
+ type: /\stype\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i
27
+ }
28
+ }
29
+ }
package/src/utils/i18n.js CHANGED
@@ -1,4 +1,4 @@
1
- /* eslint-disable security/detect-non-literal-fs-filename -- lint-time test fixtures only */
1
+ // oxlint-disable security/detect-non-literal-fs-filename
2
2
 
3
3
  import fs from 'node:fs'
4
4
  import path from 'node:path'
@@ -7,7 +7,7 @@ import i18n from '../data/i18n.js'
7
7
 
8
8
  function templateToPattern(body) {
9
9
  const parts = body.split(i18n.re.templateExpr).map(part => part.replace(i18n.re.specialChars, '\\$&'))
10
- return new RegExp(`^${parts.join('.*')}$`) // eslint-disable-line security/detect-non-literal-regexp
10
+ return new RegExp(`^${parts.join('.*')}$`) // oxlint-disable-line security/detect-non-literal-regexp
11
11
  }
12
12
 
13
13
  export default {
package/src/utils/init.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import css from '@eslint/css'
2
2
  import html from '@html-eslint/eslint-plugin'
3
3
  import json from '@eslint/json'
4
+ import pkg from './pkg.js'
4
5
  import rulesUtils from './rules.js'
5
6
  import text from './text.js'
6
7
 
@@ -12,6 +13,8 @@ import noDeprecatedAPIs from '../rules/no-deprecated-apis.js'
12
13
  import noDuplicateManifestKeys from '../rules/no-duplicate-manifest-keys.js'
13
14
  import noGlobalKeyframes from '../rules/no-global-keyframes.js'
14
15
  import noHostPageSelectors from '../rules/no-host-page-selectors.js'
16
+ import noInlineEventHandlers from '../rules/no-inline-event-handlers.js'
17
+ import noInlineScripts from '../rules/no-inline-scripts.js'
15
18
  import noInsecureFetch from '../rules/no-insecure-fetch.js'
16
19
  import noInsecureMessaging from '../rules/no-insecure-messaging.js'
17
20
  import noInvalidLocales from '../rules/no-invalid-locales.js'
@@ -21,6 +24,7 @@ import noMissingI18nKeys from '../rules/no-missing-i18n-keys.js'
21
24
  import noMissingPermissions from '../rules/no-missing-permissions.js'
22
25
  import noMissingWebAccessibleResources from '../rules/no-missing-web-accessible-resources.js'
23
26
  import noRemoteCode from '../rules/no-remote-code.js'
27
+ import noRemoteObjects from '../rules/no-remote-objects.js'
24
28
  import noRemoteURLs from '../rules/no-remote-urls.js'
25
29
  import noScrollbarStyling from '../rules/no-scrollbar-styling.js'
26
30
  import noSensitivePermissions from '../rules/no-sensitive-permissions.js'
@@ -38,7 +42,8 @@ import validServiceWorker from '../rules/valid-service-worker.js'
38
42
  import validURLfields from '../rules/valid-url-fields.js'
39
43
  import validVersionField from '../rules/valid-version-field.js'
40
44
 
41
- const namespace = 'chrome-extensions'
45
+ const pkgName = pkg.getName(),
46
+ namespace = pkgName.replace(/^@[^/]+\//, '').replace(/^eslint-plugin-/, '')
42
47
 
43
48
  export default {
44
49
  configs(plugin) {
@@ -67,7 +72,7 @@ export default {
67
72
  }
68
73
  },
69
74
 
70
- plugin() { return { rules: {}, configs: {} }},
75
+ plugin() { return { meta: { name: pkgName }, rules: {}, configs: {} }},
71
76
 
72
77
  rules(plugin) {
73
78
  plugin.rules = {
@@ -79,6 +84,8 @@ export default {
79
84
  'no-duplicate-manifest-keys': noDuplicateManifestKeys,
80
85
  'no-global-keyframes': noGlobalKeyframes,
81
86
  'no-host-page-selectors': noHostPageSelectors,
87
+ 'no-inline-event-handlers': noInlineEventHandlers,
88
+ 'no-inline-scripts': noInlineScripts,
82
89
  'no-insecure-fetch': noInsecureFetch,
83
90
  'no-insecure-messaging': noInsecureMessaging,
84
91
  'no-invalid-locales': noInvalidLocales,
@@ -88,6 +95,7 @@ export default {
88
95
  'no-missing-permissions': noMissingPermissions,
89
96
  'no-missing-web-accessible-resources': noMissingWebAccessibleResources,
90
97
  'no-remote-code': noRemoteCode,
98
+ 'no-remote-objects': noRemoteObjects,
91
99
  'no-remote-urls': noRemoteURLs,
92
100
  'no-scrollbar-styling': noScrollbarStyling,
93
101
  'no-sensitive-permissions': noSensitivePermissions,
@@ -106,6 +114,6 @@ export default {
106
114
  'valid-version-field': validVersionField
107
115
  }
108
116
  for (const [ruleId, rule] of Object.entries(plugin.rules))
109
- rule.meta.docs.url = `https://codeberg.org/adamlui/eslint-plugin-${namespace}/#${ruleId}`
117
+ rule.meta.docs.url = `https://codeberg.org/adamlui/${pkgName}/#${ruleId}`
110
118
  }
111
119
  }
@@ -1,4 +1,4 @@
1
- /* eslint-disable security/detect-non-literal-fs-filename -- lint-time test fixtures only */
1
+ // oxlint-disable security/detect-non-literal-fs-filename
2
2
 
3
3
  import { createRequire } from 'node:module'
4
4
  import fs from 'node:fs'
package/src/utils/node.js CHANGED
@@ -19,5 +19,7 @@ export default {
19
19
  ancestor = ancestor.parent
20
20
  }
21
21
  return false
22
- }
22
+ },
23
+
24
+ plainType(node, prefix) { return node?.type?.replace(prefix, '') ?? '' }
23
25
  }
package/src/utils/pkg.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { fileURLToPath } from 'node:url'
1
2
  import fs from 'node:fs'
2
3
  import path from 'node:path'
3
4
 
@@ -11,11 +12,27 @@ export default {
11
12
  current = parent
12
13
  continue
13
14
  }
14
- if ( // eslint-disable-next-line security/detect-non-literal-fs-filename
15
+ if ( // oxlint-disable-next-line security/detect-non-literal-fs-filename
15
16
  fs.existsSync(path.join(current, 'package.json'))) return current
16
17
  const parent = path.dirname(current)
17
18
  if (parent === current) return null
18
19
  current = parent
19
20
  }
20
- }
21
+ },
22
+
23
+ getName() { // oxlint-disable security/detect-non-literal-fs-filename
24
+ let current = path.dirname(fileURLToPath(import.meta.url))
25
+ while (current) {
26
+ const pkgPath = path.join(current, 'package.json')
27
+ if (fs.existsSync(pkgPath))
28
+ try {
29
+ const { name } = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
30
+ if (name) return name
31
+ } catch { /* malformed, keep walking */ }
32
+ const parent = path.dirname(current)
33
+ if (parent === current) return null
34
+ current = parent
35
+ }
36
+ return null
37
+ } // oxlint-enable security/detect-non-literal-fs-filename
21
38
  }
@@ -1,7 +1,7 @@
1
1
  export default {
2
2
  bundle(rules, predicate, pluginName) {
3
3
  return Object.fromEntries(Object.entries(rules)
4
- .filter(([, rule]) => predicate(rule))
4
+ .filter(([, rule]) => !rule.meta.deprecated && predicate(rule))
5
5
  .map(([ruleId]) => [`${pluginName}/${ruleId}`, 'error']))
6
6
  }
7
7
  }