eslint-plugin-chrome-extensions 1.0.1 → 2.1.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.
- package/README.md +580 -80
- package/package.json +6 -5
- package/src/data/deprecations.js +10 -0
- package/src/data/manifest.js +24 -5
- package/src/index.js +41 -22
- package/src/rules/manifest-key-order.js +76 -0
- package/src/rules/no-content-style-leak.js +1 -1
- package/src/rules/no-duplicate-manifest-keys.js +36 -0
- package/src/rules/no-insecure-fetch.js +52 -0
- package/src/rules/no-insecure-messaging.js +44 -7
- package/src/rules/no-invalid-permissions.js +26 -5
- package/src/rules/no-missing-web-accessible-resources.js +51 -0
- package/src/rules/no-remote-code.js +11 -9
- package/src/rules/no-unrestricted-host-permissions.js +54 -0
- package/src/rules/no-unused-permissions.js +64 -19
- package/src/rules/require-mv3.js +21 -3
- package/src/rules/valid-manifest-icons.js +85 -0
- package/src/rules/valid-mv3-schema.js +54 -5
- package/src/rules/valid-service-worker.js +1 -1
- package/src/rules/valid-version-field.js +95 -0
- package/src/utils.js +24 -3
package/README.md
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
<a href="#license">
|
|
8
8
|
<img alt="[License: MIT]" src="https://img.shields.io/badge/License-MIT-f1f20a.svg?logo=internetarchive&logoColor=white&labelColor=464646&style=for-the-badge"></a>
|
|
9
9
|
<a target="_blank" href="https://eslint.org/">
|
|
10
|
-
<img alt="[Requires ESLint >=9.
|
|
10
|
+
<img alt="[Requires ESLint >=9.6.0]" src="https://img.shields.io/badge/Requires-ESLint_%3E%3D9.6.0-4B32C3?style=for-the-badge&logo=eslint"></a>
|
|
11
11
|
<br><br>
|
|
12
12
|
|
|
13
13
|
ESLint rules for Chrome extensions.
|
|
14
14
|
|
|
15
|
-
<img src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-chrome-extensions/
|
|
15
|
+
<img src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-chrome-extensions/04d2f53b22/assets/images/screenshots/vs-code/squiggles.png">
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -31,26 +31,35 @@ npm i -D eslint-plugin-chrome-extensions
|
|
|
31
31
|
Usage
|
|
32
32
|
</h2>
|
|
33
33
|
|
|
34
|
-
Add
|
|
34
|
+
Add bundled configs to your `eslint.config.*js`:
|
|
35
35
|
|
|
36
36
|
```js
|
|
37
37
|
...
|
|
38
38
|
import chromeExtensions from 'eslint-plugin-chrome-extensions'
|
|
39
|
+
import json from '@eslint/json'
|
|
39
40
|
|
|
40
41
|
export default [
|
|
41
42
|
// other configs
|
|
42
43
|
{
|
|
43
|
-
files: ['
|
|
44
|
+
files: ['extension/**/*.js'],
|
|
45
|
+
plugins: { 'chrome-extensions': chromeExtensions },
|
|
44
46
|
rules: {
|
|
45
47
|
...chromeExtensions.configs.recommended.rules,
|
|
46
|
-
'chrome-extensions/no-
|
|
47
|
-
|
|
48
|
+
'chrome-extensions/no-deprecated-apis': 'off' // selectively disable rule
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
files: ['extension/manifest.json'], language: 'json/json',
|
|
53
|
+
plugins: { 'chrome-extensions': chromeExtensions, json },
|
|
54
|
+
rules: {
|
|
55
|
+
...chromeExtensions.configs.manifest.rules,
|
|
56
|
+
'chrome-extensions/valid-version-field': ['error', { versionType: 'date' }] // selectively tweak rule
|
|
48
57
|
}
|
|
49
58
|
}
|
|
50
59
|
]
|
|
51
60
|
```
|
|
52
61
|
|
|
53
|
-
Available bundles: `recommended`, `strict` (everything), `problem` (deterministic bugs only)
|
|
62
|
+
Available bundles: `recommended`, `strict` (everything), `problem` (deterministic bugs only), `security`, `manifest`
|
|
54
63
|
|
|
55
64
|
**Note:** Rule config is either a severity string (`'off'`, `'warn'`, `'error'`) or an array `[severity, options]` when the rule accepts options.
|
|
56
65
|
|
|
@@ -61,6 +70,77 @@ Available bundles: `recommended`, `strict` (everything), `problem` (deterministi
|
|
|
61
70
|
Supported rules
|
|
62
71
|
</h2>
|
|
63
72
|
|
|
73
|
+
<h3 id="manifest-key-order">
|
|
74
|
+
<code>manifest-key-order</code>
|
|
75
|
+
<a title="Only works w/ 'json/json' config block" href="#usage">
|
|
76
|
+
<img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-32fcee?style=flat-square" align="middle"></a>
|
|
77
|
+
<a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix">
|
|
78
|
+
<img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-3b5bdb?style=flat-square" align="middle"></a>
|
|
79
|
+
<a title="Doesn't prevent bugs" href="#manifest-key-order">
|
|
80
|
+
<img height=18 alt="[Stylistic]" src="https://img.shields.io/badge/Stylistic-af68ff?style=flat-square" align="middle"></a>
|
|
81
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
82
|
+
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
83
|
+
<a title="Copy rule URL" target="_blank"
|
|
84
|
+
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23manifest-key-order">
|
|
85
|
+
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
86
|
+
</h3>
|
|
87
|
+
|
|
88
|
+
<details>
|
|
89
|
+
|
|
90
|
+
<summary>Enforces a preferred order for manifest keys.</summary>
|
|
91
|
+
|
|
92
|
+
#### Options
|
|
93
|
+
|
|
94
|
+
- `order` (default `[]`) – manifest keys in custom order (e.g. `['name', 'manifest_version']`) to selectively override built-in order with
|
|
95
|
+
|
|
96
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect code
|
|
97
|
+
|
|
98
|
+
```diff
|
|
99
|
+
/* eslint chrome-extensions/manifest-key-order: ['error', { order: ['name', 'manifest_version'] }] */
|
|
100
|
+
|
|
101
|
+
{
|
|
102
|
+
! "manifest_version": 3,
|
|
103
|
+
"name": "My Extension",
|
|
104
|
+
"version": "1.0.0"
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
|
|
109
|
+
|
|
110
|
+
```diff
|
|
111
|
+
/* eslint chrome-extensions/manifest-key-order: ['error', { order: ['name', 'manifest_version'] }] */
|
|
112
|
+
|
|
113
|
+
{
|
|
114
|
+
- "manifest_version": 3,
|
|
115
|
+
"name": "My Extension",
|
|
116
|
+
+ "manifest_version": 3,
|
|
117
|
+
"version": "1.0.0"
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### When not to use it
|
|
122
|
+
|
|
123
|
+
If you don't care about the order of manifest keys.
|
|
124
|
+
|
|
125
|
+
#### Introduced in
|
|
126
|
+
|
|
127
|
+
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.0.0">v2.0.0</a> (October 4, 2026)
|
|
128
|
+
|
|
129
|
+
#### See also
|
|
130
|
+
|
|
131
|
+
- [`require-mv3`](#require-mv3)
|
|
132
|
+
- [`valid-mv3-schema`](#valid-mv3-schema)
|
|
133
|
+
|
|
134
|
+
<div align="center">
|
|
135
|
+
|
|
136
|
+
[Back to index ↑][epce-rules]
|
|
137
|
+
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
</details>
|
|
143
|
+
|
|
64
144
|
<h3 id="no-content-style-leak">
|
|
65
145
|
<code>no-content-style-leak</code>
|
|
66
146
|
<a title="Only included in strict config" href="#usage">
|
|
@@ -83,13 +163,16 @@ Available bundles: `recommended`, `strict` (everything), `problem` (deterministi
|
|
|
83
163
|
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
|
|
84
164
|
|
|
85
165
|
```diff
|
|
166
|
+
+ const host = document.createElement('div'),
|
|
167
|
+
+ root = host.attachShadow({ mode: 'open' })
|
|
168
|
+
+ document.body.append(host)
|
|
86
169
|
- document.head.append(document.createElement('style'))
|
|
87
|
-
+
|
|
170
|
+
+ root.append(document.createElement('style'))
|
|
88
171
|
```
|
|
89
172
|
|
|
90
173
|
#### When not to use it
|
|
91
174
|
|
|
92
|
-
If you
|
|
175
|
+
If you intentionally interact w/ host page styles.
|
|
93
176
|
|
|
94
177
|
#### Introduced in
|
|
95
178
|
|
|
@@ -97,13 +180,12 @@ If you already scope every injected style inside a `shadowRoot` you own.
|
|
|
97
180
|
|
|
98
181
|
#### See also
|
|
99
182
|
|
|
100
|
-
-
|
|
101
|
-
- <a target="_blank" href="https://developer.
|
|
102
|
-
- <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts">Chrome Extensions — Content scripts</a>
|
|
183
|
+
- <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts">Chrome for Developers — Content scripts</a>
|
|
184
|
+
- <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM">MDN — Using shadow DOM</a>
|
|
103
185
|
|
|
104
186
|
<div align="center">
|
|
105
187
|
|
|
106
|
-
[Back to index ↑][
|
|
188
|
+
[Back to index ↑][epce-rules]
|
|
107
189
|
|
|
108
190
|
</div>
|
|
109
191
|
|
|
@@ -138,6 +220,7 @@ If you already scope every injected style inside a `shadowRoot` you own.
|
|
|
138
220
|
|
|
139
221
|
! chrome.pageAction.show(tabId)
|
|
140
222
|
! chrome.extension.getBackgroundPage().doThing()
|
|
223
|
+
! chrome.tabs.executeScript(tabId, { code: 'doThing()' })
|
|
141
224
|
```
|
|
142
225
|
|
|
143
226
|
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
|
|
@@ -149,6 +232,8 @@ If you already scope every injected style inside a `shadowRoot` you own.
|
|
|
149
232
|
+ chrome.action.enable(tabId)
|
|
150
233
|
- chrome.extension.getBackgroundPage().doThing()
|
|
151
234
|
+ await chrome.runtime.getContexts({ contextTypes: ['BACKGROUND'] })
|
|
235
|
+
- chrome.tabs.executeScript(tabId, { code: 'doThing()' })
|
|
236
|
+
+ chrome.scripting.executeScript({ target: { tabId }, func: doThing })
|
|
152
237
|
```
|
|
153
238
|
|
|
154
239
|
#### When not to use it
|
|
@@ -161,13 +246,127 @@ If you don't want deprecation validation at all. For individual deprecated APIs
|
|
|
161
246
|
|
|
162
247
|
#### See also
|
|
163
248
|
|
|
249
|
+
- <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/api">Chrome for Developers — API reference</a>
|
|
250
|
+
|
|
251
|
+
<div align="center">
|
|
252
|
+
|
|
253
|
+
[Back to index ↑][epce-rules]
|
|
254
|
+
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
</details>
|
|
260
|
+
|
|
261
|
+
<h3 id="no-duplicate-manifest-keys">
|
|
262
|
+
<code>no-duplicate-manifest-keys</code>
|
|
263
|
+
<a title="Only works w/ 'json/json' config block" href="#usage">
|
|
264
|
+
<img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-32fcee?style=flat-square" align="middle"></a>
|
|
265
|
+
<a title="Copy rule URL" target="_blank"
|
|
266
|
+
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-duplicate-manifest-keys">
|
|
267
|
+
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
268
|
+
</h3>
|
|
269
|
+
|
|
270
|
+
<details>
|
|
271
|
+
|
|
272
|
+
<summary>Disallows duplicate keys in <code>manifest.json</code>.</summary>
|
|
273
|
+
|
|
274
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect code
|
|
275
|
+
|
|
276
|
+
```diff
|
|
277
|
+
{
|
|
278
|
+
"manifest_version": 3,
|
|
279
|
+
! "name": "Foo",
|
|
280
|
+
! "name": "Bar",
|
|
281
|
+
! "version": "1.0.0",
|
|
282
|
+
! "version": "2.0.0"
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
|
|
287
|
+
|
|
288
|
+
```diff
|
|
289
|
+
{
|
|
290
|
+
"manifest_version": 3,
|
|
291
|
+
- "name": "Foo",
|
|
292
|
+
- "name": "Bar",
|
|
293
|
+
"name": "My Extension",
|
|
294
|
+
- "version": "1.0.0",
|
|
295
|
+
- "version": "2.0.0"
|
|
296
|
+
"version": "1.0.0"
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
#### When not to use it
|
|
301
|
+
|
|
302
|
+
If you rely on a Chromium fork resolving duplicate keys in a specific order.
|
|
303
|
+
|
|
304
|
+
#### Introduced in
|
|
305
|
+
|
|
306
|
+
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.1.0">v2.1.0</a> (October 5, 2026)
|
|
307
|
+
|
|
308
|
+
#### See also
|
|
309
|
+
|
|
310
|
+
- [`manifest-key-order`](#manifest-key-order)
|
|
311
|
+
- [`no-unused-permissions`](#no-unused-permissions)
|
|
312
|
+
|
|
313
|
+
<div align="center">
|
|
314
|
+
|
|
315
|
+
[Back to index ↑][epce-rules]
|
|
316
|
+
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
</details>
|
|
322
|
+
|
|
323
|
+
<h3 id="no-insecure-fetch">
|
|
324
|
+
<code>no-insecure-fetch</code>
|
|
325
|
+
<a title="Included in recommended config" href="#usage">
|
|
326
|
+
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
327
|
+
<a title="Included in security config" href="#usage">
|
|
328
|
+
<img height=18 alt="[Security]" src="https://img.shields.io/badge/Security-e8357a?style=flat-square" align="middle"></a>
|
|
329
|
+
<a title="Copy rule URL" target="_blank"
|
|
330
|
+
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-insecure-fetch">
|
|
331
|
+
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
332
|
+
</h3>
|
|
333
|
+
|
|
334
|
+
<details>
|
|
335
|
+
|
|
336
|
+
<summary>Flags <code>fetch()</code> and <code>XMLHttpRequest.open()</code> calls that use <code>http://</code>.</summary>
|
|
337
|
+
|
|
338
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect code
|
|
339
|
+
|
|
340
|
+
```diff
|
|
341
|
+
! fetch('http://api.example.com/data')
|
|
342
|
+
! xhr.open('GET', 'http://cdn.example.com/lib.js')
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
|
|
346
|
+
|
|
347
|
+
```diff
|
|
348
|
+
- fetch('http://api.example.com/data')
|
|
349
|
+
+ fetch('https://api.example.com/data')
|
|
350
|
+
- xhr.open('GET', 'http://cdn.example.com/lib.js')
|
|
351
|
+
+ xhr.open('GET', 'https://cdn.example.com/lib.js')
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
#### When not to use it
|
|
355
|
+
|
|
356
|
+
If you intentionally target HTTP endpoints.
|
|
357
|
+
|
|
358
|
+
#### Introduced in
|
|
359
|
+
|
|
360
|
+
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.1.0">v2.1.0</a> (October 5, 2026)
|
|
361
|
+
|
|
362
|
+
#### See also
|
|
363
|
+
|
|
364
|
+
- <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/network-requests">Chrome for Developers — Cross-origin network requests</a>
|
|
164
365
|
- [`no-remote-code`](#no-remote-code)
|
|
165
|
-
- [`valid-mv3-schema`](#valid-mv3-schema)
|
|
166
|
-
- <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/api">Chrome Extensions — API reference</a>
|
|
167
366
|
|
|
168
367
|
<div align="center">
|
|
169
368
|
|
|
170
|
-
[Back to index ↑][
|
|
369
|
+
[Back to index ↑][epce-rules]
|
|
171
370
|
|
|
172
371
|
</div>
|
|
173
372
|
|
|
@@ -179,6 +378,10 @@ If you don't want deprecation validation at all. For individual deprecated APIs
|
|
|
179
378
|
<code>no-insecure-messaging</code>
|
|
180
379
|
<a title="Included in recommended config" href="#usage">
|
|
181
380
|
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
381
|
+
<a title="Included in security config" href="#usage">
|
|
382
|
+
<img height=18 alt="[Security]" src="https://img.shields.io/badge/Security-e8357a?style=flat-square" align="middle"></a>
|
|
383
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
384
|
+
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
182
385
|
<a title="Copy rule URL" target="_blank"
|
|
183
386
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-insecure-messaging">
|
|
184
387
|
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
@@ -186,36 +389,55 @@ If you don't want deprecation validation at all. For individual deprecated APIs
|
|
|
186
389
|
|
|
187
390
|
<details>
|
|
188
391
|
|
|
189
|
-
<summary>Requires
|
|
392
|
+
<summary>Requires sender/origin validation + filtered <code>tabs.query()</code> + explicit <code>window.postMessage()</code> target.</summary>
|
|
393
|
+
|
|
394
|
+
#### Options
|
|
395
|
+
|
|
396
|
+
- `disallowWildcardTarget` (default `true`) – flag <code>postMessage()</code> calls that use <code>'*'</code> as <code>targetOrigin</code>
|
|
190
397
|
|
|
191
398
|
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect code
|
|
192
399
|
|
|
193
400
|
```diff
|
|
194
|
-
|
|
195
|
-
! handle(msg)
|
|
401
|
+
chrome.runtime.onMessageExternal.addListener((msg, sender, respond) => {
|
|
402
|
+
! handle(msg)
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
window.addEventListener('message', event => {
|
|
406
|
+
! handle(event.data)
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
! chrome.tabs.query({})
|
|
410
|
+
.then(tabs => tabs.forEach(tab =>
|
|
411
|
+
chrome.tabs.sendMessage(tab.id, { action: 'ping' })))
|
|
196
412
|
|
|
197
|
-
!
|
|
198
|
-
! chrome.tabs.sendMessage(tab.id, { action: 'ping' })))
|
|
413
|
+
! window.postMessage(payload, '*')
|
|
199
414
|
```
|
|
200
415
|
|
|
201
416
|
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
|
|
202
417
|
|
|
203
418
|
```diff
|
|
204
|
-
|
|
205
|
-
- handle(msg) })
|
|
206
|
-
+ chrome.runtime.onMessageExternal.addListener((msg, sender, respond) => {
|
|
419
|
+
chrome.runtime.onMessageExternal.addListener((msg, sender, respond) => {
|
|
207
420
|
+ if (sender.id != 'allowed-extension-id') return
|
|
208
|
-
|
|
209
|
-
|
|
421
|
+
handle(msg)
|
|
422
|
+
})
|
|
210
423
|
|
|
211
|
-
|
|
212
|
-
+
|
|
213
|
-
|
|
424
|
+
window.addEventListener('message', event => {
|
|
425
|
+
+ if (event.origin != 'https://allowed-origin.com') return
|
|
426
|
+
handle(event.data)
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
- chrome.tabs.query({})
|
|
430
|
+
+ chrome.tabs.query({ url: 'https://allowed-origin.com/*' })
|
|
431
|
+
.then(tabs => tabs.forEach(tab =>
|
|
432
|
+
chrome.tabs.sendMessage(tab.id, { action: 'ping' })))
|
|
433
|
+
|
|
434
|
+
- window.postMessage(payload, '*')
|
|
435
|
+
+ window.postMessage(payload, 'https://allowed-origin.com')
|
|
214
436
|
```
|
|
215
437
|
|
|
216
438
|
#### When not to use it
|
|
217
439
|
|
|
218
|
-
If you trust every external caller and open tab.
|
|
440
|
+
If you trust every external caller and open tab, or you intentionally broadcast to any origin.
|
|
219
441
|
|
|
220
442
|
#### Introduced in
|
|
221
443
|
|
|
@@ -223,12 +445,13 @@ If you trust every external caller and open tab.
|
|
|
223
445
|
|
|
224
446
|
#### See also
|
|
225
447
|
|
|
226
|
-
-
|
|
227
|
-
-
|
|
448
|
+
- <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/api/runtime#event-onMessageExternal">Chrome for Developers — `runtime.onMessageExternal`</a>
|
|
449
|
+
- <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/api/tabs#method-query">Chrome for Developers — `tabs.query()`</a>
|
|
450
|
+
- <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#security_concerns">MDN — `window.postMessage()` security concerns</a>
|
|
228
451
|
|
|
229
452
|
<div align="center">
|
|
230
453
|
|
|
231
|
-
[Back to index ↑][
|
|
454
|
+
[Back to index ↑][epce-rules]
|
|
232
455
|
|
|
233
456
|
</div>
|
|
234
457
|
|
|
@@ -240,6 +463,8 @@ If you trust every external caller and open tab.
|
|
|
240
463
|
<code>no-invalid-permissions</code>
|
|
241
464
|
<a title="Included in recommended config" href="#usage">
|
|
242
465
|
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
466
|
+
<a title="Works w/ 'json/json' config block" href="#usage">
|
|
467
|
+
<img height=18 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-32fcee?style=flat-square" align="middle"></a>
|
|
243
468
|
<a title="Accepts array w/ options" href="#usage">
|
|
244
469
|
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
245
470
|
<a title="Copy rule URL" target="_blank"
|
|
@@ -262,8 +487,8 @@ If you trust every external caller and open tab.
|
|
|
262
487
|
/* eslint chrome-extensions/no-invalid-permissions: ['error', { target: 'mv3' }] */
|
|
263
488
|
|
|
264
489
|
{
|
|
265
|
-
!
|
|
266
|
-
!
|
|
490
|
+
! "permissions": ["browserAction", "tabs"],
|
|
491
|
+
! "optional_permissions": ["history", "pageAction"]
|
|
267
492
|
}
|
|
268
493
|
```
|
|
269
494
|
|
|
@@ -273,10 +498,10 @@ If you trust every external caller and open tab.
|
|
|
273
498
|
/* eslint chrome-extensions/no-invalid-permissions: ['error', { target: 'mv3' }] */
|
|
274
499
|
|
|
275
500
|
{
|
|
276
|
-
-
|
|
277
|
-
+
|
|
278
|
-
-
|
|
279
|
-
+
|
|
501
|
+
- "permissions": ["browserAction", "tabs"],
|
|
502
|
+
+ "permissions": ["tabs"],
|
|
503
|
+
- "optional_permissions": ["history", "pageAction"]
|
|
504
|
+
+ "optional_permissions": ["history"]
|
|
280
505
|
}
|
|
281
506
|
```
|
|
282
507
|
|
|
@@ -295,7 +520,7 @@ If you don't want permission validation at all. For custom permissions from a Ch
|
|
|
295
520
|
|
|
296
521
|
<div align="center">
|
|
297
522
|
|
|
298
|
-
[Back to index ↑][
|
|
523
|
+
[Back to index ↑][epce-rules]
|
|
299
524
|
|
|
300
525
|
</div>
|
|
301
526
|
|
|
@@ -324,7 +549,7 @@ If you don't want permission validation at all. For custom permissions from a Ch
|
|
|
324
549
|
! "permissions": []
|
|
325
550
|
}
|
|
326
551
|
|
|
327
|
-
//
|
|
552
|
+
// service-worker.js
|
|
328
553
|
! chrome.cookies.get({ url: 'https://example.com', name: 'x' })
|
|
329
554
|
! chrome.history.search({ text: '' })
|
|
330
555
|
```
|
|
@@ -338,7 +563,7 @@ If you don't want permission validation at all. For custom permissions from a Ch
|
|
|
338
563
|
+ "permissions": ["cookies", "history"]
|
|
339
564
|
}
|
|
340
565
|
|
|
341
|
-
//
|
|
566
|
+
// service-worker.js
|
|
342
567
|
chrome.cookies.get({ url: 'https://example.com', name: 'x' })
|
|
343
568
|
chrome.history.search({ text: '' })
|
|
344
569
|
```
|
|
@@ -358,7 +583,70 @@ If you use APIs through aliases or wrappers that obscure the original call site.
|
|
|
358
583
|
|
|
359
584
|
<div align="center">
|
|
360
585
|
|
|
361
|
-
[Back to index ↑][
|
|
586
|
+
[Back to index ↑][epce-rules]
|
|
587
|
+
|
|
588
|
+
</div>
|
|
589
|
+
|
|
590
|
+
---
|
|
591
|
+
|
|
592
|
+
</details>
|
|
593
|
+
|
|
594
|
+
<h3 id="no-missing-web-accessible-resources">
|
|
595
|
+
<code>no-missing-web-accessible-resources</code>
|
|
596
|
+
<a title="Included in recommended config" href="#usage">
|
|
597
|
+
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
598
|
+
<a title="Copy rule URL" target="_blank"
|
|
599
|
+
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-missing-web-accessible-resources">
|
|
600
|
+
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
601
|
+
</h3>
|
|
602
|
+
|
|
603
|
+
<details>
|
|
604
|
+
|
|
605
|
+
<summary>Flags <code>chrome.runtime.getURL()</code> paths missing from <code>web_accessible_resources</code>.</summary>
|
|
606
|
+
|
|
607
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect code
|
|
608
|
+
|
|
609
|
+
```diff
|
|
610
|
+
// manifest.json
|
|
611
|
+
{
|
|
612
|
+
"manifest_version": 3,
|
|
613
|
+
! "web_accessible_resources": []
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// content.js
|
|
617
|
+
! img.src = chrome.runtime.getURL('logo.png')
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
|
|
621
|
+
|
|
622
|
+
```diff
|
|
623
|
+
// manifest.json
|
|
624
|
+
{
|
|
625
|
+
"manifest_version": 3,
|
|
626
|
+
- "web_accessible_resources": []
|
|
627
|
+
+ "web_accessible_resources": [{ "resources": ["logo.png"], "matches": ["<all_urls>"] }]
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// content.js
|
|
631
|
+
img.src = chrome.runtime.getURL('logo.png')
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
#### When not to use it
|
|
635
|
+
|
|
636
|
+
If your paths are built at runtime.
|
|
637
|
+
|
|
638
|
+
#### Introduced in
|
|
639
|
+
|
|
640
|
+
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.1.0">v2.1.0</a> (October 5, 2026)
|
|
641
|
+
|
|
642
|
+
#### See also
|
|
643
|
+
|
|
644
|
+
- <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources">Chrome for Developers — `web_accessible_resources`</a>
|
|
645
|
+
- <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/api/runtime#method-getURL">Chrome for Developers — `runtime.getURL()`</a>
|
|
646
|
+
|
|
647
|
+
<div align="center">
|
|
648
|
+
|
|
649
|
+
[Back to index ↑][epce-rules]
|
|
362
650
|
|
|
363
651
|
</div>
|
|
364
652
|
|
|
@@ -370,6 +658,8 @@ If you use APIs through aliases or wrappers that obscure the original call site.
|
|
|
370
658
|
<code>no-remote-code</code>
|
|
371
659
|
<a title="Included in recommended config" href="#usage">
|
|
372
660
|
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
661
|
+
<a title="Included in security config" href="#usage">
|
|
662
|
+
<img height=18 alt="[Security]" src="https://img.shields.io/badge/Security-e8357a?style=flat-square" align="middle"></a>
|
|
373
663
|
<a title="Accepts array w/ options" href="#usage">
|
|
374
664
|
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
375
665
|
<a title="Copy rule URL" target="_blank"
|
|
@@ -379,7 +669,7 @@ If you use APIs through aliases or wrappers that obscure the original call site.
|
|
|
379
669
|
|
|
380
670
|
<details>
|
|
381
671
|
|
|
382
|
-
<summary>Disallows remote or dynamically-evaluated code.</summary>
|
|
672
|
+
<summary>Disallows remote or dynamically-evaluated code (blocked by MV3's CSP at runtime).</summary>
|
|
383
673
|
|
|
384
674
|
#### Options
|
|
385
675
|
|
|
@@ -389,25 +679,28 @@ If you use APIs through aliases or wrappers that obscure the original call site.
|
|
|
389
679
|
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect code
|
|
390
680
|
|
|
391
681
|
```diff
|
|
682
|
+
! await import('https://cdn.example.com/lib.js')
|
|
392
683
|
! const run = eval('1 + 1')
|
|
393
684
|
! setTimeout('doThing()', 1000)
|
|
394
|
-
!
|
|
685
|
+
! chrome.scripting.executeScript({ target: { tabId }, func: () => doThing(value) })
|
|
395
686
|
```
|
|
396
687
|
|
|
397
688
|
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
|
|
398
689
|
|
|
399
690
|
```diff
|
|
691
|
+
- await import('https://cdn.example.com/lib.js')
|
|
692
|
+
+ await import('./lib.js')
|
|
400
693
|
- const run = eval('1 + 1')
|
|
401
694
|
+ const run = 1 + 1
|
|
402
695
|
- setTimeout('doThing()', 1000)
|
|
403
696
|
+ setTimeout(doThing, 1000)
|
|
404
|
-
-
|
|
405
|
-
+
|
|
697
|
+
- chrome.scripting.executeScript({ target: { tabId }, func: () => doThing(value) })
|
|
698
|
+
+ chrome.scripting.executeScript({ target: { tabId }, func: doThing, args: [value] })
|
|
406
699
|
```
|
|
407
700
|
|
|
408
701
|
#### When not to use it
|
|
409
702
|
|
|
410
|
-
|
|
703
|
+
If you intentionally target forks w/ relaxed CSPs. For individual patterns you still need, prefer narrowing via `patterns` over disabling the rule.
|
|
411
704
|
|
|
412
705
|
#### Introduced in
|
|
413
706
|
|
|
@@ -415,12 +708,76 @@ Never (MV3 CSP blocks remote code at runtime).
|
|
|
415
708
|
|
|
416
709
|
#### See also
|
|
417
710
|
|
|
418
|
-
- [`
|
|
711
|
+
- [`require-mv3`](#require-mv3)
|
|
419
712
|
- [`valid-mv3-schema`](#valid-mv3-schema)
|
|
420
713
|
|
|
421
714
|
<div align="center">
|
|
422
715
|
|
|
423
|
-
[Back to index ↑][
|
|
716
|
+
[Back to index ↑][epce-rules]
|
|
717
|
+
|
|
718
|
+
</div>
|
|
719
|
+
|
|
720
|
+
---
|
|
721
|
+
|
|
722
|
+
</details>
|
|
723
|
+
|
|
724
|
+
<h3 id="no-unrestricted-host-permissions">
|
|
725
|
+
<code>no-unrestricted-host-permissions</code>
|
|
726
|
+
<a title="Only works w/ 'json/json' config block" href="#usage">
|
|
727
|
+
<img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-32fcee?style=flat-square" align="middle"></a>
|
|
728
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
729
|
+
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
730
|
+
<a title="Copy rule URL" target="_blank"
|
|
731
|
+
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-unrestricted-host-permissions">
|
|
732
|
+
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
733
|
+
</h3>
|
|
734
|
+
|
|
735
|
+
<details>
|
|
736
|
+
|
|
737
|
+
<summary>Disallows broad host permissions that grant access to every site.</summary>
|
|
738
|
+
|
|
739
|
+
#### Options
|
|
740
|
+
|
|
741
|
+
- `allowedPatterns` (default `[]`) – host patterns to explicitly allow (e.g. `['<all_urls>']`)
|
|
742
|
+
|
|
743
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect code
|
|
744
|
+
|
|
745
|
+
```diff
|
|
746
|
+
{
|
|
747
|
+
"manifest_version": 3,
|
|
748
|
+
! "host_permissions": ["<all_urls>"],
|
|
749
|
+
! "optional_host_permissions": ["*://*/*"]
|
|
750
|
+
}
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
|
|
754
|
+
|
|
755
|
+
```diff
|
|
756
|
+
{
|
|
757
|
+
"manifest_version": 3,
|
|
758
|
+
- "host_permissions": ["<all_urls>"],
|
|
759
|
+
+ "host_permissions": ["https://example.com/*", "https://*.trusted.com/*"],
|
|
760
|
+
- "optional_host_permissions": ["*://*/*"]
|
|
761
|
+
+ "optional_host_permissions": ["https://user-content.example.com/*"]
|
|
762
|
+
}
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
#### When not to use it
|
|
766
|
+
|
|
767
|
+
If you legitimately need broad access (e.g. ad blockers or dev tools).
|
|
768
|
+
|
|
769
|
+
#### Introduced in
|
|
770
|
+
|
|
771
|
+
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.1.0">v2.1.0</a> (October 5, 2026)
|
|
772
|
+
|
|
773
|
+
#### See also
|
|
774
|
+
|
|
775
|
+
- <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions">Chrome for Developers — Declare permissions</a>
|
|
776
|
+
- <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns">Chrome for Developers — Match patterns</a>
|
|
777
|
+
|
|
778
|
+
<div align="center">
|
|
779
|
+
|
|
780
|
+
[Back to index ↑][epce-rules]
|
|
424
781
|
|
|
425
782
|
</div>
|
|
426
783
|
|
|
@@ -430,8 +787,8 @@ Never (MV3 CSP blocks remote code at runtime).
|
|
|
430
787
|
|
|
431
788
|
<h3 id="no-unused-permissions">
|
|
432
789
|
<code>no-unused-permissions</code>
|
|
433
|
-
<a title="Only
|
|
434
|
-
<img height=18 alt="[
|
|
790
|
+
<a title="Only works w/ 'json/json' config block" href="#usage">
|
|
791
|
+
<img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-32fcee?style=flat-square" align="middle"></a>
|
|
435
792
|
<a title="Copy rule URL" target="_blank"
|
|
436
793
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-unused-permissions">
|
|
437
794
|
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
@@ -449,7 +806,7 @@ Never (MV3 CSP blocks remote code at runtime).
|
|
|
449
806
|
! "permissions": ["tabs", "downloads"]
|
|
450
807
|
}
|
|
451
808
|
|
|
452
|
-
//
|
|
809
|
+
// service-worker.js
|
|
453
810
|
chrome.tabs.create({ url: 'https://example.com' })
|
|
454
811
|
```
|
|
455
812
|
|
|
@@ -462,7 +819,7 @@ Never (MV3 CSP blocks remote code at runtime).
|
|
|
462
819
|
+ "permissions": ["tabs"]
|
|
463
820
|
}
|
|
464
821
|
|
|
465
|
-
//
|
|
822
|
+
// service-worker.js
|
|
466
823
|
chrome.tabs.create({ url: 'https://example.com' })
|
|
467
824
|
```
|
|
468
825
|
|
|
@@ -481,7 +838,7 @@ If you access the granted API through an alias or computed property rather than
|
|
|
481
838
|
|
|
482
839
|
<div align="center">
|
|
483
840
|
|
|
484
|
-
[Back to index ↑][
|
|
841
|
+
[Back to index ↑][epce-rules]
|
|
485
842
|
|
|
486
843
|
</div>
|
|
487
844
|
|
|
@@ -493,6 +850,8 @@ If you access the granted API through an alias or computed property rather than
|
|
|
493
850
|
<code>require-mv3</code>
|
|
494
851
|
<a title="Included in recommended config" href="#usage">
|
|
495
852
|
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
853
|
+
<a title="Works w/ 'json/json' config block" href="#usage">
|
|
854
|
+
<img height=18 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-32fcee?style=flat-square" align="middle"></a>
|
|
496
855
|
<a title="Copy rule URL" target="_blank"
|
|
497
856
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23require-mv3">
|
|
498
857
|
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
@@ -505,7 +864,6 @@ If you access the granted API through an alias or computed property rather than
|
|
|
505
864
|
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect code
|
|
506
865
|
|
|
507
866
|
```diff
|
|
508
|
-
// manifest.json
|
|
509
867
|
{
|
|
510
868
|
! "manifest_version": 2,
|
|
511
869
|
"name": "My Extension",
|
|
@@ -516,7 +874,6 @@ If you access the granted API through an alias or computed property rather than
|
|
|
516
874
|
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
|
|
517
875
|
|
|
518
876
|
```diff
|
|
519
|
-
// manifest.json
|
|
520
877
|
{
|
|
521
878
|
- "manifest_version": 2,
|
|
522
879
|
+ "manifest_version": 3,
|
|
@@ -527,7 +884,7 @@ If you access the granted API through an alias or computed property rather than
|
|
|
527
884
|
|
|
528
885
|
#### When not to use it
|
|
529
886
|
|
|
530
|
-
If you intentionally target MV2 for a
|
|
887
|
+
If you intentionally target MV2 for a browser that still supports it (e.g. Firefox).
|
|
531
888
|
|
|
532
889
|
#### Introduced in
|
|
533
890
|
|
|
@@ -539,7 +896,78 @@ If you intentionally target MV2 for a Chromium fork that still supports it.
|
|
|
539
896
|
|
|
540
897
|
<div align="center">
|
|
541
898
|
|
|
542
|
-
[Back to index ↑][
|
|
899
|
+
[Back to index ↑][epce-rules]
|
|
900
|
+
|
|
901
|
+
</div>
|
|
902
|
+
|
|
903
|
+
---
|
|
904
|
+
|
|
905
|
+
</details>
|
|
906
|
+
|
|
907
|
+
<h3 id="valid-manifest-icons">
|
|
908
|
+
<code>valid-manifest-icons</code>
|
|
909
|
+
<a title="Only works w/ 'json/json' config block" href="#usage">
|
|
910
|
+
<img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-32fcee?style=flat-square" align="middle"></a>
|
|
911
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
912
|
+
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
913
|
+
<a title="Copy rule URL" target="_blank"
|
|
914
|
+
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23valid-manifest-icons">
|
|
915
|
+
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
916
|
+
</h3>
|
|
917
|
+
|
|
918
|
+
<details>
|
|
919
|
+
|
|
920
|
+
<summary>Ensures manifest <code>icons</code> exist and use the recommended sizes.</summary>
|
|
921
|
+
|
|
922
|
+
#### Options
|
|
923
|
+
|
|
924
|
+
- `requireRecommendedSizes` (default `true`) – require all of `16`, `32`, `48`, `128`
|
|
925
|
+
- `checkIconExists` (default `true`) – flag icon paths that don't resolve to an existing file
|
|
926
|
+
- `noSVG` (default `true`) – flag SVG icon paths (unsupported by Chrome)
|
|
927
|
+
|
|
928
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect code
|
|
929
|
+
|
|
930
|
+
```diff
|
|
931
|
+
{
|
|
932
|
+
"manifest_version": 3,
|
|
933
|
+
"icons": {
|
|
934
|
+
! "48": "icon48.svg",
|
|
935
|
+
"256": "icon256.png"
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
|
|
941
|
+
|
|
942
|
+
```diff
|
|
943
|
+
{
|
|
944
|
+
"manifest_version": 3,
|
|
945
|
+
"icons": {
|
|
946
|
+
+ "16": "icon16.png",
|
|
947
|
+
+ "32": "icon32.png",
|
|
948
|
+
- "48": "icon48.svg",
|
|
949
|
+
+ "48": "icon48.png",
|
|
950
|
+
+ "128": "icon128.png",
|
|
951
|
+
"256": "icon256.png"
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
#### When not to use it
|
|
957
|
+
|
|
958
|
+
If your icons are injected at build time, or you target browsers that support SVGs (e.g. Firefox).
|
|
959
|
+
|
|
960
|
+
#### Introduced in
|
|
961
|
+
|
|
962
|
+
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.1.0">v2.1.0</a> (October 5, 2026)
|
|
963
|
+
|
|
964
|
+
#### See also
|
|
965
|
+
|
|
966
|
+
- <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/manifest/icons">Chrome for Developers — `icons`</a>
|
|
967
|
+
|
|
968
|
+
<div align="center">
|
|
969
|
+
|
|
970
|
+
[Back to index ↑][epce-rules]
|
|
543
971
|
|
|
544
972
|
</div>
|
|
545
973
|
|
|
@@ -551,6 +979,8 @@ If you intentionally target MV2 for a Chromium fork that still supports it.
|
|
|
551
979
|
<code>valid-mv3-schema</code>
|
|
552
980
|
<a title="Included in recommended config" href="#usage">
|
|
553
981
|
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
982
|
+
<a title="Works w/ 'json/json' config block" href="#usage">
|
|
983
|
+
<img height=18 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-32fcee?style=flat-square" align="middle"></a>
|
|
554
984
|
<a title="Copy rule URL" target="_blank"
|
|
555
985
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23valid-mv3-schema">
|
|
556
986
|
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
@@ -564,11 +994,11 @@ If you intentionally target MV2 for a Chromium fork that still supports it.
|
|
|
564
994
|
|
|
565
995
|
```diff
|
|
566
996
|
{
|
|
567
|
-
|
|
568
|
-
!
|
|
569
|
-
!
|
|
570
|
-
!
|
|
571
|
-
!
|
|
997
|
+
"manifest_version": 3,
|
|
998
|
+
! "browser_action": {},
|
|
999
|
+
! "page_action": {},
|
|
1000
|
+
! "background": { "scripts": ["background.js"], "persistent": true },
|
|
1001
|
+
! "content_security_policy": "script-src 'self' 'unsafe-eval'"
|
|
572
1002
|
}
|
|
573
1003
|
```
|
|
574
1004
|
|
|
@@ -576,20 +1006,20 @@ If you intentionally target MV2 for a Chromium fork that still supports it.
|
|
|
576
1006
|
|
|
577
1007
|
```diff
|
|
578
1008
|
{
|
|
579
|
-
|
|
580
|
-
-
|
|
581
|
-
-
|
|
582
|
-
+
|
|
583
|
-
-
|
|
584
|
-
+
|
|
585
|
-
-
|
|
586
|
-
+
|
|
1009
|
+
"manifest_version": 3,
|
|
1010
|
+
- "browser_action": {},
|
|
1011
|
+
- "page_action": {},
|
|
1012
|
+
+ "action": {},
|
|
1013
|
+
- "background": { "scripts": ["background.js"], "persistent": true },
|
|
1014
|
+
+ "background": { "service_worker": "service-worker.js" },
|
|
1015
|
+
- "content_security_policy": "script-src 'self' 'unsafe-eval'"
|
|
1016
|
+
+ "content_security_policy": { "extension_pages": "script-src 'self'; object-src 'self'" }
|
|
587
1017
|
}
|
|
588
1018
|
```
|
|
589
1019
|
|
|
590
1020
|
#### When not to use it
|
|
591
1021
|
|
|
592
|
-
If you intentionally target MV2 for a
|
|
1022
|
+
If you intentionally target MV2 for a browser that still supports it (e.g. Firefox).
|
|
593
1023
|
|
|
594
1024
|
#### Introduced in
|
|
595
1025
|
|
|
@@ -602,7 +1032,7 @@ If you intentionally target MV2 for a Chromium fork that still supports it.
|
|
|
602
1032
|
|
|
603
1033
|
<div align="center">
|
|
604
1034
|
|
|
605
|
-
[Back to index ↑][
|
|
1035
|
+
[Back to index ↑][epce-rules]
|
|
606
1036
|
|
|
607
1037
|
</div>
|
|
608
1038
|
|
|
@@ -629,7 +1059,7 @@ If you intentionally target MV2 for a Chromium fork that still supports it.
|
|
|
629
1059
|
! (function init() { chrome.runtime.onMessage.addListener(handler) })()
|
|
630
1060
|
! setInterval(() => {}, 5000)
|
|
631
1061
|
! const loc = window.location
|
|
632
|
-
! localStorage.setItem('key', '
|
|
1062
|
+
! localStorage.setItem('key', 'value')
|
|
633
1063
|
```
|
|
634
1064
|
|
|
635
1065
|
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
|
|
@@ -655,12 +1085,82 @@ If your background context isn't a service worker.
|
|
|
655
1085
|
|
|
656
1086
|
#### See also
|
|
657
1087
|
|
|
658
|
-
- [`
|
|
1088
|
+
- [`require-mv3`](#require-mv3)
|
|
659
1089
|
- [`no-remote-code`](#no-remote-code)
|
|
660
1090
|
|
|
661
1091
|
<div align="center">
|
|
662
1092
|
|
|
663
|
-
[Back to index ↑][
|
|
1093
|
+
[Back to index ↑][epce-rules]
|
|
1094
|
+
|
|
1095
|
+
</div>
|
|
1096
|
+
|
|
1097
|
+
---
|
|
1098
|
+
|
|
1099
|
+
</details>
|
|
1100
|
+
|
|
1101
|
+
<h3 id="valid-version-field">
|
|
1102
|
+
<code>valid-version-field</code>
|
|
1103
|
+
<a title="Included in recommended config" href="#usage">
|
|
1104
|
+
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
1105
|
+
<a title="Works w/ 'json/json' config block" href="#usage">
|
|
1106
|
+
<img height=18 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-32fcee?style=flat-square" align="middle"></a>
|
|
1107
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
1108
|
+
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
1109
|
+
<a title="Copy rule URL" target="_blank"
|
|
1110
|
+
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23valid-version-field">
|
|
1111
|
+
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
1112
|
+
</h3>
|
|
1113
|
+
|
|
1114
|
+
<details>
|
|
1115
|
+
|
|
1116
|
+
<summary>Validates the manifest <code>version</code> field.</summary>
|
|
1117
|
+
|
|
1118
|
+
#### Options
|
|
1119
|
+
|
|
1120
|
+
- `versionType` (default `'chrome'`) – version format to enforce (`'chrome'`, `'semver'`, `'date'`)
|
|
1121
|
+
- `allowFutureDate` (default `false`) – allow `version` dates later than the current date
|
|
1122
|
+
- `timezone` (default `''`) – <a target="_blank" href="https://data.iana.org/time-zones/tzdb/zone1970.tab">IANA timezone</a> used to determine the current date (when omitted, only year/month are enforced)
|
|
1123
|
+
|
|
1124
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect code
|
|
1125
|
+
|
|
1126
|
+
```diff
|
|
1127
|
+
/* eslint chrome-extensions/valid-version-field: ['error', { versionType: 'date' }] */
|
|
1128
|
+
|
|
1129
|
+
{
|
|
1130
|
+
"manifest_version": 3,
|
|
1131
|
+
! "version": "2026.1.111",
|
|
1132
|
+
"name": "My Extension"
|
|
1133
|
+
}
|
|
1134
|
+
```
|
|
1135
|
+
|
|
1136
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
|
|
1137
|
+
|
|
1138
|
+
```diff
|
|
1139
|
+
/* eslint chrome-extensions/valid-version-field: ['error', { versionType: 'date' }] */
|
|
1140
|
+
|
|
1141
|
+
{
|
|
1142
|
+
"manifest_version": 3,
|
|
1143
|
+
- "version": "2026.1.111",
|
|
1144
|
+
+ "version": "2026.10.3",
|
|
1145
|
+
"name": "My Extension"
|
|
1146
|
+
}
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
#### When not to use it
|
|
1150
|
+
|
|
1151
|
+
If you don't care about version format consistency, or your version is injected at build time.
|
|
1152
|
+
|
|
1153
|
+
#### Introduced in
|
|
1154
|
+
|
|
1155
|
+
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.0.0">v2.0.0</a> (October 4, 2026)
|
|
1156
|
+
|
|
1157
|
+
#### See also
|
|
1158
|
+
|
|
1159
|
+
- <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/manifest/version">Chrome for Developers — `version`</a>
|
|
1160
|
+
|
|
1161
|
+
<div align="center">
|
|
1162
|
+
|
|
1163
|
+
[Back to index ↑][epce-rules]
|
|
664
1164
|
|
|
665
1165
|
</div>
|
|
666
1166
|
|
|
@@ -668,7 +1168,7 @@ If your background context isn't a service worker.
|
|
|
668
1168
|
|
|
669
1169
|
---
|
|
670
1170
|
|
|
671
|
-
## <img
|
|
1171
|
+
## <img width=30 align="top" src="https://api.iconify.design/boxicons/code-alt-filled.svg?color=%232b2b2a"> Shield embed
|
|
672
1172
|
|
|
673
1173
|
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/#readme">
|
|
674
1174
|
<img height=32 alt="[Linted by eslint-plugin-chrome-extensions]" src="https://img.shields.io/badge/Linted_by-eslint--plugin--chrome--extensions-black?logo=eslint&logoColor=white&labelColor=464646&style=for-the-badge"></a>
|
|
@@ -676,7 +1176,7 @@ If your background context isn't a service worker.
|
|
|
676
1176
|
#### HTML
|
|
677
1177
|
|
|
678
1178
|
```html
|
|
679
|
-
<a href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/#readme">
|
|
1179
|
+
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/#readme">
|
|
680
1180
|
<img height=32 alt="[Linted by eslint-plugin-chrome-extensions]" src="https://img.shields.io/badge/Linted_by-eslint--plugin--chrome--extensions-black?logo=eslint&logoColor=white&labelColor=464646&style=for-the-badge"></a>
|
|
681
1181
|
```
|
|
682
1182
|
|
|
@@ -751,4 +1251,4 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|
|
751
1251
|
|
|
752
1252
|
<!-- Links -->
|
|
753
1253
|
|
|
754
|
-
[
|
|
1254
|
+
[epce-rules]: #supported-rules
|