38 | handleToggleActive(id)}
41 | />
42 |
43 | )}
44 | />
45 |
46 | );
47 | };
48 |
49 | const root = document.createElement("div");
50 | document.body.appendChild(root);
51 | createRoot(root).render();
52 |
--------------------------------------------------------------------------------
/src/schema/README.md:
--------------------------------------------------------------------------------
1 | ```sh
2 | npx ts-json-schema-generator --path fields.ts
3 | ```
4 |
--------------------------------------------------------------------------------
/src/schema/fields.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-07/schema#",
3 | "definitions": {
4 | "DomainType": {
5 | "description": "TThis describes whether the request is first or third party to the frame in which it originated. A request is said to be first party if it has the same domain (eTLD+1) as the frame in which the request originated.",
6 | "enum": [
7 | "firstParty",
8 | "thirdParty"
9 | ],
10 | "type": "string"
11 | },
12 | "ExtensionActionOptions": {
13 | "additionalProperties": false,
14 | "properties": {
15 | "displayActionCountAsBadgeText": {
16 | "description": "Whether to automatically display the action count for a page as the extension's badge text. This preference is persisted across sessions.",
17 | "type": "boolean"
18 | },
19 | "tabUpdate": {
20 | "$ref": "#/definitions/TabActionCountUpdate",
21 | "description": "Details of how the tab's action count should be adjusted."
22 | }
23 | },
24 | "type": "object"
25 | },
26 | "HeaderOperation": {
27 | "description": "This describes the possible operations for a \"modifyHeaders\" rule.",
28 | "enum": [
29 | "append",
30 | "set",
31 | "remove"
32 | ],
33 | "type": "string"
34 | },
35 | "IsRegexSupportedResult": {
36 | "additionalProperties": false,
37 | "properties": {
38 | "isSupported": {
39 | "type": "boolean"
40 | },
41 | "reason": {
42 | "$ref": "#/definitions/UnsupportedRegexReason",
43 | "description": "Specifies the reason why the regular expression is not supported. Only provided if isSupported is false."
44 | }
45 | },
46 | "required": [
47 | "isSupported"
48 | ],
49 | "type": "object"
50 | },
51 | "MatchedRule": {
52 | "additionalProperties": false,
53 | "properties": {
54 | "ruleId": {
55 | "description": "A matching rule's ID.",
56 | "type": "number"
57 | },
58 | "rulesetId": {
59 | "description": "ID of the Ruleset this rule belongs to. For a rule originating from the set of dynamic rules, this will be equal to DYNAMIC_RULESET_ID.",
60 | "type": "string"
61 | }
62 | },
63 | "required": [
64 | "ruleId",
65 | "rulesetId"
66 | ],
67 | "type": "object"
68 | },
69 | "MatchedRuleInfo": {
70 | "additionalProperties": false,
71 | "properties": {
72 | "rule": {
73 | "$ref": "#/definitions/MatchedRule"
74 | },
75 | "tabId": {
76 | "description": "The tabId of the tab from which the request originated if the tab is still active. Else -1.",
77 | "type": "number"
78 | },
79 | "timeStamp": {
80 | "description": "The time the rule was matched. Timestamps will correspond to the Javascript convention for times, i.e. number of milliseconds since the epoch.",
81 | "type": "number"
82 | }
83 | },
84 | "required": [
85 | "rule",
86 | "tabId",
87 | "timeStamp"
88 | ],
89 | "type": "object"
90 | },
91 | "MatchedRuleInfoDebug": {
92 | "additionalProperties": false,
93 | "properties": {
94 | "request": {
95 | "$ref": "#/definitions/RequestDetails",
96 | "description": "Details about the request for which the rule was matched."
97 | },
98 | "rule": {
99 | "$ref": "#/definitions/MatchedRule"
100 | }
101 | },
102 | "required": [
103 | "request",
104 | "rule"
105 | ],
106 | "type": "object"
107 | },
108 | "MatchedRulesFilter": {
109 | "additionalProperties": false,
110 | "properties": {
111 | "minTimeStamp": {
112 | "description": "If specified, only matches rules after the given timestamp.",
113 | "type": "number"
114 | },
115 | "tabId": {
116 | "description": "If specified, only matches rules for the given tab. Matches rules not associated with any active tab if set to -1.",
117 | "type": "number"
118 | }
119 | },
120 | "type": "object"
121 | },
122 | "ModifyHeaderInfo": {
123 | "additionalProperties": false,
124 | "properties": {
125 | "header": {
126 | "description": "The name of the header to be modified.",
127 | "type": "string"
128 | },
129 | "operation": {
130 | "$ref": "#/definitions/HeaderOperation",
131 | "description": "The operation to be performed on a header."
132 | },
133 | "value": {
134 | "description": "The new value for the header. Must be specified for append and set operations.",
135 | "type": "string"
136 | }
137 | },
138 | "required": [
139 | "header",
140 | "operation"
141 | ],
142 | "type": "object"
143 | },
144 | "QueryKeyValue": {
145 | "additionalProperties": false,
146 | "properties": {
147 | "key": {
148 | "type": "string"
149 | },
150 | "value": {
151 | "type": "string"
152 | }
153 | },
154 | "required": [
155 | "key",
156 | "value"
157 | ],
158 | "type": "object"
159 | },
160 | "QueryTransform": {
161 | "additionalProperties": false,
162 | "properties": {
163 | "addOrReplaceParams": {
164 | "description": "The list of query key-value pairs to be added or replaced.",
165 | "items": {
166 | "$ref": "#/definitions/QueryKeyValue"
167 | },
168 | "type": "array"
169 | },
170 | "removeParams": {
171 | "description": "The list of query keys to be removed.",
172 | "items": {
173 | "type": "string"
174 | },
175 | "type": "array"
176 | }
177 | },
178 | "type": "object"
179 | },
180 | "Redirect": {
181 | "additionalProperties": false,
182 | "properties": {
183 | "extensionPath": {
184 | "description": "Path relative to the extension directory. Should start with '/'.",
185 | "type": "string"
186 | },
187 | "regexSubstitution": {
188 | "description": "Substitution pattern for rules which specify a regexFilter. The first match of regexFilter within the url will be replaced with this pattern. Within regexSubstitution, backslash-escaped digits (\\1 to \\9) can be used to insert the corresponding capture groups. \\0 refers to the entire matching text.",
189 | "type": "string"
190 | },
191 | "transform": {
192 | "$ref": "#/definitions/URLTransform",
193 | "description": "Url transformations to perform."
194 | },
195 | "url": {
196 | "description": "The redirect url. Redirects to JavaScript urls are not allowed.",
197 | "type": "string"
198 | }
199 | },
200 | "type": "object"
201 | },
202 | "RegexOptions": {
203 | "additionalProperties": false,
204 | "properties": {
205 | "isCaseSensitive": {
206 | "description": "Whether the regex specified is case sensitive. Default is true.",
207 | "type": "boolean"
208 | },
209 | "regex": {
210 | "description": "The regular expression to check.",
211 | "type": "string"
212 | },
213 | "requireCapturing": {
214 | "description": "Whether the regex specified requires capturing. Capturing is only required for redirect rules which specify a regexSubstitution action. The default is false.",
215 | "type": "boolean"
216 | }
217 | },
218 | "required": [
219 | "regex"
220 | ],
221 | "type": "object"
222 | },
223 | "RequestDetails": {
224 | "additionalProperties": false,
225 | "properties": {
226 | "frameId": {
227 | "description": "The value 0 indicates that the request happens in the main frame; a positive value indicates the ID of a subframe in which the request happens. If the document of a (sub-)frame is loaded (type is main_frame or sub_frame), frameId indicates the ID of this frame, not the ID of the outer frame. Frame IDs are unique within a tab.",
228 | "type": "number"
229 | },
230 | "initiator": {
231 | "description": "The origin where the request was initiated. This does not change through redirects. If this is an opaque origin, the string 'null' will be used.",
232 | "type": "string"
233 | },
234 | "method": {
235 | "description": "Standard HTTP method.",
236 | "type": "string"
237 | },
238 | "partentFrameId": {
239 | "description": "ID of frame that wraps the frame which sent the request. Set to -1 if no parent frame exists.",
240 | "type": "number"
241 | },
242 | "requestId": {
243 | "description": "The ID of the request. Request IDs are unique within a browser session.",
244 | "type": "string"
245 | },
246 | "tabId": {
247 | "description": "The ID of the tab in which the request takes place. Set to -1 if the request isn't related to a tab.",
248 | "type": "number"
249 | },
250 | "type": {
251 | "$ref": "#/definitions/ResourceType",
252 | "description": "The resource type of the request."
253 | },
254 | "url": {
255 | "description": "The URL of the request.",
256 | "type": "string"
257 | }
258 | },
259 | "required": [
260 | "frameId",
261 | "method",
262 | "partentFrameId",
263 | "requestId",
264 | "tabId",
265 | "type",
266 | "url"
267 | ],
268 | "type": "object"
269 | },
270 | "RequestMethod": {
271 | "description": "This describes the HTTP request method of a network request.",
272 | "enum": [
273 | "connect",
274 | "delete",
275 | "get",
276 | "head",
277 | "options",
278 | "patch",
279 | "post",
280 | "put"
281 | ],
282 | "type": "string"
283 | },
284 | "ResourceType": {
285 | "description": "This describes the resource type of the network request.",
286 | "enum": [
287 | "main_frame",
288 | "sub_frame",
289 | "stylesheet",
290 | "script",
291 | "image",
292 | "font",
293 | "object",
294 | "xmlhttprequest",
295 | "ping",
296 | "csp_report",
297 | "media",
298 | "websocket",
299 | "other"
300 | ],
301 | "type": "string"
302 | },
303 | "Rule": {
304 | "additionalProperties": false,
305 | "properties": {
306 | "action": {
307 | "$ref": "#/definitions/RuleAction",
308 | "description": "The action to take if this rule is matched."
309 | },
310 | "condition": {
311 | "$ref": "#/definitions/RuleCondition",
312 | "description": "The condition under which this rule is triggered."
313 | },
314 | "id": {
315 | "description": "An id which uniquely identifies a rule. Mandatory and should be >= 1.",
316 | "type": "number"
317 | },
318 | "priority": {
319 | "description": "Rule priority. Defaults to 1. When specified, should be >= 1.",
320 | "type": "number"
321 | }
322 | },
323 | "required": [
324 | "action",
325 | "condition",
326 | "id"
327 | ],
328 | "type": "object"
329 | },
330 | "RuleAction": {
331 | "additionalProperties": false,
332 | "properties": {
333 | "redirect": {
334 | "$ref": "#/definitions/Redirect",
335 | "description": "Describes how the redirect should be performed. Only valid for redirect rules."
336 | },
337 | "requestHeaders": {
338 | "description": "The request headers to modify for the request. Only valid if RuleActionType is \"modifyHeaders\".",
339 | "items": {
340 | "$ref": "#/definitions/ModifyHeaderInfo"
341 | },
342 | "type": "array"
343 | },
344 | "responseHeaders": {
345 | "description": "The response headers to modify for the request. Only valid if RuleActionType is \"modifyHeaders\".",
346 | "items": {
347 | "$ref": "#/definitions/ModifyHeaderInfo"
348 | },
349 | "type": "array"
350 | },
351 | "type": {
352 | "$ref": "#/definitions/RuleActionType",
353 | "description": "The type of action to perform."
354 | }
355 | },
356 | "required": [
357 | "type"
358 | ],
359 | "type": "object"
360 | },
361 | "RuleActionType": {
362 | "description": "Describes the kind of action to take if a given RuleCondition matches.",
363 | "enum": [
364 | "block",
365 | "redirect",
366 | "allow",
367 | "upgradeScheme",
368 | "modifyHeaders",
369 | "allowAllRequests"
370 | ],
371 | "type": "string"
372 | },
373 | "RuleCondition": {
374 | "additionalProperties": false,
375 | "properties": {
376 | "domainType": {
377 | "$ref": "#/definitions/DomainType",
378 | "description": "Specifies whether the network request is first-party or third-party to the domain from which it originated. If omitted, all requests are accepted."
379 | },
380 | "domains": {
381 | "deprecated": "since Chrome 101. Use initiatorDomains instead.\n\nThe rule will only match network requests originating from the list of domains.\nIf the list is omitted, the rule is applied to requests from all domains.\nAn empty list is not allowed.\n\nNotes:\nSub-domains like \"a.example.com\" are also allowed.\nThe entries must consist of only ascii characters.\nUse punycode encoding for internationalized domains.\nThis matches against the request initiator and not the request url.",
382 | "items": {
383 | "type": "string"
384 | },
385 | "type": "array"
386 | },
387 | "excludedDomains": {
388 | "deprecated": "since Chrome 101. Use excludedInitiatorDomains instead\n\nThe rule will not match network requests originating from the list of excludedDomains.\nIf the list is empty or omitted, no domains are excluded.\nThis takes precedence over domains.\n\nNotes:\nSub-domains like \"a.example.com\" are also allowed.\nThe entries must consist of only ascii characters.\nUse punycode encoding for internationalized domains.\nThis matches against the request initiator and not the request url.",
389 | "items": {
390 | "type": "string"
391 | },
392 | "type": "array"
393 | },
394 | "excludedInitiatorDomains": {
395 | "description": "The rule will not match network requests originating from the list of excludedInitiatorDomains. If the list is empty or omitted, no domains are excluded. This takes precedence over initiatorDomains.\n\nNotes: Sub-domains like \"a.example.com\" are also allowed. The entries must consist of only ascii characters. Use punycode encoding for internationalized domains. This matches against the request initiator and not the request url.",
396 | "items": {
397 | "type": "string"
398 | },
399 | "type": "array"
400 | },
401 | "excludedRequestDomains": {
402 | "description": "The rule will not match network requests when the domains matches one from the list of excludedRequestDomains. If the list is empty or omitted, no domains are excluded. This takes precedence over requestDomains.\n\nNotes: Sub-domains like \"a.example.com\" are also allowed. The entries must consist of only ascii characters. Use punycode encoding for internationalized domains.",
403 | "items": {
404 | "type": "string"
405 | },
406 | "type": "array"
407 | },
408 | "excludedRequestMethods": {
409 | "description": "List of request methods which the rule won't match. Only one of requestMethods and excludedRequestMethods should be specified. If neither of them is specified, all request methods are matched.",
410 | "items": {
411 | "$ref": "#/definitions/RequestMethod"
412 | },
413 | "type": "array"
414 | },
415 | "excludedResourceTypes": {
416 | "description": "List of resource types which the rule won't match. Only one of {@link chrome.declarativeNetRequest.RuleCondition.resourceTypes } and {@link chrome.declarativeNetRequest.RuleCondition.excludedResourceTypes } should be specified. If neither of them is specified, all resource types except \"main_frame\" are blocked.",
417 | "items": {
418 | "$ref": "#/definitions/ResourceType"
419 | },
420 | "type": "array"
421 | },
422 | "excludedTabIds": {
423 | "description": "List of {@link chrome.tabs.Tab.id } which the rule should not match. An ID of {@link chrome.tabs.TAB_ID_NONE } excludes requests which don't originate from a tab. Only supported for session-scoped rules.",
424 | "items": {
425 | "type": "number"
426 | },
427 | "type": "array"
428 | },
429 | "initiatorDomains": {
430 | "description": "The rule will only match network requests originating from the list of initiatorDomains. If the list is omitted, the rule is applied to requests from all domains. An empty list is not allowed.\n\nNotes: Sub-domains like \"a.example.com\" are also allowed. The entries must consist of only ascii characters. Use punycode encoding for internationalized domains. This matches against the request initiator and not the request url.",
431 | "items": {
432 | "type": "string"
433 | },
434 | "type": "array"
435 | },
436 | "isUrlFilterCaseSensitive": {
437 | "default": "false Before Chrome 118 the default was true.",
438 | "description": "Whether the urlFilter or regexFilter (whichever is specified) is case sensitive.",
439 | "type": "boolean"
440 | },
441 | "regexFilter": {
442 | "description": "Regular expression to match against the network request url. This follows the RE2 syntax.\n\nNote: Only one of urlFilter or regexFilter can be specified.\n\nNote: The regexFilter must be composed of only ASCII characters. This is matched against a url where the host is encoded in the punycode format (in case of internationalized domains) and any other non-ascii characters are url encoded in utf-8.",
443 | "type": "string"
444 | },
445 | "requestDomains": {
446 | "description": "The rule will only match network requests when the domain matches one from the list of requestDomains. If the list is omitted, the rule is applied to requests from all domains. An empty list is not allowed.\n\nNotes: Sub-domains like \"a.example.com\" are also allowed. The entries must consist of only ascii characters. Use punycode encoding for internationalized domains.",
447 | "items": {
448 | "type": "string"
449 | },
450 | "type": "array"
451 | },
452 | "requestMethods": {
453 | "description": "List of HTTP request methods which the rule can match. An empty list is not allowed. Note: Specifying a {@link chrome.declarativeNetRequest.RuleCondition.requestMethods } rule condition will also exclude non-HTTP(s) requests, whereas specifying {@link chrome.declarativeNetRequest.RuleCondition.excludedRequestMethods } will not.",
454 | "items": {
455 | "$ref": "#/definitions/RequestMethod"
456 | },
457 | "type": "array"
458 | },
459 | "resourceTypes": {
460 | "description": "List of resource types which the rule can match. An empty list is not allowed.\n\nNote: this must be specified for allowAllRequests rules and may only include the sub_frame and main_frame resource types.",
461 | "items": {
462 | "$ref": "#/definitions/ResourceType"
463 | },
464 | "type": "array"
465 | },
466 | "tabIds": {
467 | "description": "List of {@link chrome.tabs.Tab.id } which the rule should not match. An ID of {@link chrome.tabs.TAB_ID_NONE } excludes requests which don't originate from a tab. An empty list is not allowed. Only supported for session-scoped rules.",
468 | "items": {
469 | "type": "number"
470 | },
471 | "type": "array"
472 | },
473 | "urlFilter": {
474 | "description": "The pattern which is matched against the network request url. Supported constructs:\n\n'*' : Wildcard: Matches any number of characters.\n\n'|' : Left/right anchor: If used at either end of the pattern, specifies the beginning/end of the url respectively.\n\n'||' : Domain name anchor: If used at the beginning of the pattern, specifies the start of a (sub-)domain of the URL.\n\n'^' : Separator character: This matches anything except a letter, a digit or one of the following: _ - . %. This can also match the end of the URL.\n\nTherefore urlFilter is composed of the following parts: (optional Left/Domain name anchor) + pattern + (optional Right anchor).\n\nIf omitted, all urls are matched. An empty string is not allowed.\n\nA pattern beginning with || is not allowed. Use instead.\n\nNote: Only one of urlFilter or regexFilter can be specified.\n\nNote: The urlFilter must be composed of only ASCII characters. This is matched against a url where the host is encoded in the punycode format (in case of internationalized domains) and any other non-ascii characters are url encoded in utf-8. For example, when the request url is http://abc.рф?q=ф, the urlFilter will be matched against the url http://abc.xn--p1ai/?q=%D1%84.",
475 | "type": "string"
476 | }
477 | },
478 | "type": "object"
479 | },
480 | "RulesMatchedDetails": {
481 | "additionalProperties": false,
482 | "properties": {
483 | "rulesMatchedInfo": {
484 | "description": "Rules matching the given filter.",
485 | "items": {
486 | "$ref": "#/definitions/MatchedRuleInfo"
487 | },
488 | "type": "array"
489 | }
490 | },
491 | "required": [
492 | "rulesMatchedInfo"
493 | ],
494 | "type": "object"
495 | },
496 | "Ruleset": {
497 | "additionalProperties": false,
498 | "properties": {
499 | "enabled": {
500 | "description": "Whether the ruleset is enabled by default.",
501 | "type": "boolean"
502 | },
503 | "id": {
504 | "description": "A non-empty string uniquely identifying the ruleset. IDs beginning with '_' are reserved for internal use.",
505 | "type": "string"
506 | },
507 | "path": {
508 | "description": "The path of the JSON ruleset relative to the extension directory.",
509 | "type": "string"
510 | }
511 | },
512 | "required": [
513 | "enabled",
514 | "id",
515 | "path"
516 | ],
517 | "type": "object"
518 | },
519 | "TabActionCountUpdate": {
520 | "additionalProperties": false,
521 | "properties": {
522 | "increment": {
523 | "description": "The amount to increment the tab's action count by. Negative values will decrement the count",
524 | "type": "number"
525 | },
526 | "tabId": {
527 | "description": "The tab for which to update the action count.",
528 | "type": "number"
529 | }
530 | },
531 | "required": [
532 | "increment",
533 | "tabId"
534 | ],
535 | "type": "object"
536 | },
537 | "URLTransform": {
538 | "additionalProperties": false,
539 | "properties": {
540 | "fragment": {
541 | "description": "The new fragment for the request. Should be either empty, in which case the existing fragment is cleared; or should begin with '#'.",
542 | "type": "string"
543 | },
544 | "host": {
545 | "description": "The new host for the request.",
546 | "type": "string"
547 | },
548 | "password": {
549 | "description": "The new password for the request.",
550 | "type": "string"
551 | },
552 | "path": {
553 | "description": "The new path for the request. If empty, the existing path is cleared.",
554 | "type": "string"
555 | },
556 | "port": {
557 | "description": "The new port for the request. If empty, the existing port is cleared.",
558 | "type": "string"
559 | },
560 | "query": {
561 | "description": "The new query for the request. Should be either empty, in which case the existing query is cleared; or should begin with '?'.",
562 | "type": "string"
563 | },
564 | "queryTransform": {
565 | "$ref": "#/definitions/QueryTransform",
566 | "description": "Add, remove or replace query key-value pairs."
567 | },
568 | "scheme": {
569 | "description": "The new scheme for the request. Allowed values are \"http\", \"https\", \"ftp\" and \"chrome-extension\".",
570 | "type": "string"
571 | },
572 | "username": {
573 | "description": "The new username for the request.",
574 | "type": "string"
575 | }
576 | },
577 | "type": "object"
578 | },
579 | "UnsupportedRegexReason": {
580 | "description": "Describes the reason why a given regular expression isn't supported.",
581 | "enum": [
582 | "syntaxError",
583 | "memoryLimitExceeded"
584 | ],
585 | "type": "string"
586 | },
587 | "UpdateRuleOptions": {
588 | "additionalProperties": false,
589 | "properties": {
590 | "addRules": {
591 | "description": "Rules to add.",
592 | "items": {
593 | "$ref": "#/definitions/Rule"
594 | },
595 | "type": "array"
596 | },
597 | "removeRuleIds": {
598 | "description": "IDs of the rules to remove. Any invalid IDs will be ignored.",
599 | "items": {
600 | "type": "number"
601 | },
602 | "type": "array"
603 | }
604 | },
605 | "type": "object"
606 | },
607 | "UpdateRulesetOptions": {
608 | "additionalProperties": false,
609 | "properties": {
610 | "disableRulesetIds": {
611 | "description": "The set of ids corresponding to a static Ruleset that should be disabled.",
612 | "items": {
613 | "type": "string"
614 | },
615 | "type": "array"
616 | },
617 | "enableRulesetIds": {
618 | "description": "The set of ids corresponding to a static Ruleset that should be enabled.",
619 | "items": {
620 | "type": "string"
621 | },
622 | "type": "array"
623 | }
624 | },
625 | "type": "object"
626 | },
627 | "UpdateStaticRulesOptions": {
628 | "additionalProperties": false,
629 | "properties": {
630 | "disableRuleIds": {
631 | "description": "Set of ids corresponding to rules in the Ruleset to disable.",
632 | "items": {
633 | "type": "number"
634 | },
635 | "type": "array"
636 | },
637 | "enableRuleIds": {
638 | "description": "Set of ids corresponding to rules in the Ruleset to enable.",
639 | "items": {
640 | "type": "number"
641 | },
642 | "type": "array"
643 | },
644 | "rulesetId": {
645 | "description": "The id corresponding to a static Ruleset.",
646 | "type": "string"
647 | }
648 | },
649 | "required": [
650 | "rulesetId"
651 | ],
652 | "type": "object"
653 | }
654 | }
655 | }
656 |
--------------------------------------------------------------------------------
/src/schema/fields.ts:
--------------------------------------------------------------------------------
1 | /** This describes the HTTP request method of a network request. */
2 | export enum RequestMethod {
3 | CONNECT = "connect",
4 | DELETE = "delete",
5 | GET = "get",
6 | HEAD = "head",
7 | OPTIONS = "options",
8 | PATCH = "patch",
9 | POST = "post",
10 | PUT = "put",
11 | }
12 |
13 | /** This describes the resource type of the network request. */
14 | export enum ResourceType {
15 | MAIN_FRAME = "main_frame",
16 | SUB_FRAME = "sub_frame",
17 | STYLESHEET = "stylesheet",
18 | SCRIPT = "script",
19 | IMAGE = "image",
20 | FONT = "font",
21 | OBJECT = "object",
22 | XMLHTTPREQUEST = "xmlhttprequest",
23 | PING = "ping",
24 | CSP_REPORT = "csp_report",
25 | MEDIA = "media",
26 | WEBSOCKET = "websocket",
27 | OTHER = "other",
28 | }
29 |
30 | /** Describes the kind of action to take if a given RuleCondition matches. */
31 | export enum RuleActionType {
32 | BLOCK = "block",
33 | REDIRECT = "redirect",
34 | ALLOW = "allow",
35 | UPGRADE_SCHEME = "upgradeScheme",
36 | MODIFY_HEADERS = "modifyHeaders",
37 | ALLOW_ALL_REQUESTS = "allowAllRequests",
38 | }
39 |
40 | /** Describes the reason why a given regular expression isn't supported. */
41 | export enum UnsupportedRegexReason {
42 | SYNTAX_ERROR = "syntaxError",
43 | MEMORY_LIMIT_EXCEEDED = "memoryLimitExceeded",
44 | }
45 |
46 | /** TThis describes whether the request is first or third party to the frame in which it originated.
47 | * A request is said to be first party if it has the same domain (eTLD+1) as the frame in which the request originated.
48 | */
49 | export enum DomainType {
50 | FIRST_PARTY = "firstParty",
51 | THIRD_PARTY = "thirdParty",
52 | }
53 |
54 | /** This describes the possible operations for a "modifyHeaders" rule. */
55 | export enum HeaderOperation {
56 | APPEND = "append",
57 | SET = "set",
58 | REMOVE = "remove",
59 | }
60 |
61 | export interface RequestDetails {
62 | /** The value 0 indicates that the request happens in the main frame; a positive value indicates the ID of a subframe in which the request happens.
63 | * If the document of a (sub-)frame is loaded (type is main_frame or sub_frame), frameId indicates the ID of this frame, not the ID of the outer frame.
64 | * Frame IDs are unique within a tab.
65 | */
66 | frameId: number;
67 |
68 | /** The origin where the request was initiated.
69 | * This does not change through redirects.
70 | * If this is an opaque origin, the string 'null' will be used.
71 | */
72 | initiator?: string | undefined;
73 |
74 | /** Standard HTTP method. */
75 | method: string;
76 |
77 | /** ID of frame that wraps the frame which sent the request.
78 | * Set to -1 if no parent frame exists.
79 | */
80 | partentFrameId: number;
81 |
82 | /** The ID of the request.
83 | * Request IDs are unique within a browser session.
84 | */
85 | requestId: string;
86 |
87 | /** The ID of the tab in which the request takes place.
88 | * Set to -1 if the request isn't related to a tab.
89 | */
90 | tabId: number;
91 |
92 | /** The resource type of the request. */
93 | type: ResourceType;
94 |
95 | /** The URL of the request. */
96 | url: string;
97 | }
98 |
99 | export interface Rule {
100 | /** The action to take if this rule is matched. */
101 | action: RuleAction;
102 |
103 | /** The condition under which this rule is triggered. */
104 | condition: RuleCondition;
105 |
106 | /** An id which uniquely identifies a rule.
107 | * Mandatory and should be >= 1.
108 | */
109 | id: number;
110 |
111 | /** Rule priority.
112 | * Defaults to 1.
113 | * When specified, should be >= 1.
114 | */
115 | priority?: number | undefined;
116 | }
117 |
118 | export interface RuleAction {
119 | /** Describes how the redirect should be performed.
120 | * Only valid for redirect rules.
121 | */
122 | redirect?: Redirect | undefined;
123 |
124 | /** The request headers to modify for the request.
125 | * Only valid if RuleActionType is "modifyHeaders".
126 | */
127 | requestHeaders?: ModifyHeaderInfo[] | undefined;
128 |
129 | /** The response headers to modify for the request.
130 | * Only valid if RuleActionType is "modifyHeaders".
131 | */
132 | responseHeaders?: ModifyHeaderInfo[] | undefined;
133 |
134 | /** The type of action to perform. */
135 | type: RuleActionType;
136 | }
137 |
138 | export interface RuleCondition {
139 | /**
140 | * Specifies whether the network request is first-party or third-party to the domain from which it originated.
141 | * If omitted, all requests are accepted.
142 | */
143 | domainType?: DomainType | undefined;
144 |
145 | /**
146 | * @deprecated since Chrome 101. Use initiatorDomains instead.
147 |
148 | * The rule will only match network requests originating from the list of domains.
149 | * If the list is omitted, the rule is applied to requests from all domains.
150 | * An empty list is not allowed.
151 | *
152 | * Notes:
153 | * Sub-domains like "a.example.com" are also allowed.
154 | * The entries must consist of only ascii characters.
155 | * Use punycode encoding for internationalized domains.
156 | * This matches against the request initiator and not the request url.
157 | */
158 | domains?: string[] | undefined;
159 |
160 | /**
161 | * @deprecated since Chrome 101. Use excludedInitiatorDomains instead
162 | *
163 | * The rule will not match network requests originating from the list of excludedDomains.
164 | * If the list is empty or omitted, no domains are excluded.
165 | * This takes precedence over domains.
166 | *
167 | * Notes:
168 | * Sub-domains like "a.example.com" are also allowed.
169 | * The entries must consist of only ascii characters.
170 | * Use punycode encoding for internationalized domains.
171 | * This matches against the request initiator and not the request url.
172 | */
173 | excludedDomains?: string[] | undefined;
174 |
175 | /**
176 | * The rule will only match network requests originating from the list of initiatorDomains.
177 | * If the list is omitted, the rule is applied to requests from all domains.
178 | * An empty list is not allowed.
179 | *
180 | * Notes:
181 | * Sub-domains like "a.example.com" are also allowed.
182 | * The entries must consist of only ascii characters.
183 | * Use punycode encoding for internationalized domains.
184 | * This matches against the request initiator and not the request url.
185 | */
186 | initiatorDomains?: string[] | undefined;
187 |
188 | /**
189 | * The rule will not match network requests originating from the list of excludedInitiatorDomains.
190 | * If the list is empty or omitted, no domains are excluded.
191 | * This takes precedence over initiatorDomains.
192 | *
193 | * Notes:
194 | * Sub-domains like "a.example.com" are also allowed.
195 | * The entries must consist of only ascii characters.
196 | * Use punycode encoding for internationalized domains.
197 | * This matches against the request initiator and not the request url.
198 | */
199 | excludedInitiatorDomains?: string[] | undefined;
200 |
201 | /**
202 | * The rule will only match network requests when the domain matches one from the list of requestDomains.
203 | * If the list is omitted, the rule is applied to requests from all domains.
204 | * An empty list is not allowed.
205 | *
206 | * Notes:
207 | * Sub-domains like "a.example.com" are also allowed.
208 | * The entries must consist of only ascii characters.
209 | * Use punycode encoding for internationalized domains.
210 | */
211 | requestDomains?: string[] | undefined;
212 |
213 | /**
214 | * The rule will not match network requests when the domains matches one from the list of excludedRequestDomains.
215 | * If the list is empty or omitted, no domains are excluded.
216 | * This takes precedence over requestDomains.
217 | *
218 | * Notes:
219 | * Sub-domains like "a.example.com" are also allowed.
220 | * The entries must consist of only ascii characters.
221 | * Use punycode encoding for internationalized domains.
222 | */
223 | excludedRequestDomains?: string[] | undefined;
224 |
225 | /**
226 | * List of request methods which the rule won't match.
227 | * Only one of requestMethods and excludedRequestMethods should be specified.
228 | * If neither of them is specified, all request methods are matched.
229 | */
230 | excludedRequestMethods?: RequestMethod[] | undefined;
231 |
232 | /**
233 | * List of resource types which the rule won't match.
234 | * Only one of {@link chrome.declarativeNetRequest.RuleCondition.resourceTypes}
235 | * and {@link chrome.declarativeNetRequest.RuleCondition.excludedResourceTypes} should be specified.
236 | * If neither of them is specified, all resource types except "main_frame" are blocked.
237 | */
238 | excludedResourceTypes?: ResourceType[] | undefined;
239 |
240 | /**
241 | * List of {@link chrome.tabs.Tab.id} which the rule should not match.
242 | * An ID of {@link chrome.tabs.TAB_ID_NONE} excludes requests which don't originate from a tab.
243 | * Only supported for session-scoped rules.
244 | */
245 | excludedTabIds?: number[] | undefined;
246 |
247 | /**
248 | * Whether the urlFilter or regexFilter (whichever is specified) is case sensitive.
249 | * @default false Before Chrome 118 the default was true.
250 | */
251 | isUrlFilterCaseSensitive?: boolean | undefined;
252 |
253 | /**
254 | * Regular expression to match against the network request url.
255 | * This follows the RE2 syntax.
256 | *
257 | * Note: Only one of urlFilter or regexFilter can be specified.
258 | *
259 | * Note: The regexFilter must be composed of only ASCII characters.
260 | * This is matched against a url where the host is encoded in the punycode format (in case of internationalized domains) and any other non-ascii characters are url encoded in utf-8.
261 | */
262 | regexFilter?: string | undefined;
263 |
264 | /**
265 | * List of HTTP request methods which the rule can match. An empty list is not allowed.
266 | * Note: Specifying a {@link chrome.declarativeNetRequest.RuleCondition.requestMethods} rule condition will also exclude non-HTTP(s) requests,
267 | * whereas specifying {@link chrome.declarativeNetRequest.RuleCondition.excludedRequestMethods} will not.
268 | */
269 | requestMethods?: RequestMethod[];
270 |
271 | /**
272 | * List of {@link chrome.tabs.Tab.id} which the rule should not match.
273 | * An ID of {@link chrome.tabs.TAB_ID_NONE} excludes requests which don't originate from a tab.
274 | * An empty list is not allowed. Only supported for session-scoped rules.
275 | */
276 | tabIds?: number[] | undefined;
277 |
278 | /**
279 | * The pattern which is matched against the network request url.
280 | * Supported constructs:
281 | *
282 | * '*' : Wildcard: Matches any number of characters.
283 | *
284 | * '|' : Left/right anchor: If used at either end of the pattern, specifies the beginning/end of the url respectively.
285 | *
286 | * '||' : Domain name anchor: If used at the beginning of the pattern, specifies the start of a (sub-)domain of the URL.
287 | *
288 | * '^' : Separator character: This matches anything except a letter, a digit or one of the following: _ - . %.
289 | * This can also match the end of the URL.
290 | *
291 | * Therefore urlFilter is composed of the following parts: (optional Left/Domain name anchor) + pattern + (optional Right anchor).
292 | *
293 | * If omitted, all urls are matched. An empty string is not allowed.
294 | *
295 | * A pattern beginning with || is not allowed. Use instead.
296 | *
297 | * Note: Only one of urlFilter or regexFilter can be specified.
298 | *
299 | * Note: The urlFilter must be composed of only ASCII characters.
300 | * This is matched against a url where the host is encoded in the punycode format (in case of internationalized domains) and any other non-ascii characters are url encoded in utf-8.
301 | * For example, when the request url is http://abc.рф?q=ф, the urlFilter will be matched against the url http://abc.xn--p1ai/?q=%D1%84.
302 | */
303 | urlFilter?: string | undefined;
304 |
305 | /**
306 | * List of resource types which the rule can match.
307 | * An empty list is not allowed.
308 | *
309 | * Note: this must be specified for allowAllRequests rules and may only include the sub_frame and main_frame resource types.
310 | */
311 | resourceTypes?: ResourceType[] | undefined;
312 | }
313 |
314 | export interface MatchedRule {
315 | /** A matching rule's ID. */
316 | ruleId: number;
317 |
318 | /** ID of the Ruleset this rule belongs to.
319 | * For a rule originating from the set of dynamic rules, this will be equal to DYNAMIC_RULESET_ID.
320 | */
321 | rulesetId: string;
322 | }
323 |
324 | export interface MatchedRuleInfo {
325 | rule: MatchedRule;
326 |
327 | /** The tabId of the tab from which the request originated if the tab is still active. Else -1. */
328 | tabId: number;
329 |
330 | /** The time the rule was matched.
331 | * Timestamps will correspond to the Javascript convention for times, i.e. number of milliseconds since the epoch.
332 | */
333 | timeStamp: number;
334 | }
335 |
336 | export interface MatchedRulesFilter {
337 | /** If specified, only matches rules after the given timestamp. */
338 | minTimeStamp?: number | undefined;
339 |
340 | /** If specified, only matches rules for the given tab.
341 | * Matches rules not associated with any active tab if set to -1.
342 | */
343 | tabId?: number | undefined;
344 | }
345 |
346 | export interface ModifyHeaderInfo {
347 | /** The name of the header to be modified. */
348 | header: string;
349 |
350 | /** The operation to be performed on a header. */
351 | operation: HeaderOperation;
352 |
353 | /** The new value for the header.
354 | * Must be specified for append and set operations.
355 | */
356 | value?: string | undefined;
357 | }
358 |
359 | export interface QueryKeyValue {
360 | key: string;
361 | value: string;
362 | }
363 |
364 | export interface QueryTransform {
365 | /** The list of query key-value pairs to be added or replaced. */
366 | addOrReplaceParams?: QueryKeyValue[] | undefined;
367 |
368 | /** The list of query keys to be removed. */
369 | removeParams?: string[] | undefined;
370 | }
371 |
372 | export interface URLTransform {
373 | /** The new fragment for the request.
374 | * Should be either empty, in which case the existing fragment is cleared; or should begin with '#'.
375 | */
376 | fragment?: string | undefined;
377 |
378 | /** The new host for the request. */
379 | host?: string | undefined;
380 |
381 | /** The new password for the request. */
382 | password?: string | undefined;
383 |
384 | /** The new path for the request.
385 | * If empty, the existing path is cleared.
386 | */
387 | path?: string | undefined;
388 |
389 | /** The new port for the request.
390 | * If empty, the existing port is cleared.
391 | */
392 | port?: string | undefined;
393 |
394 | /** The new query for the request.
395 | * Should be either empty, in which case the existing query is cleared; or should begin with '?'.
396 | */
397 | query?: string | undefined;
398 |
399 | /** Add, remove or replace query key-value pairs. */
400 | queryTransform?: QueryTransform | undefined;
401 |
402 | /** The new scheme for the request.
403 | * Allowed values are "http", "https", "ftp" and "chrome-extension".
404 | */
405 | scheme?: string | undefined;
406 |
407 | /** The new username for the request. */
408 | username?: string | undefined;
409 | }
410 |
411 | export interface RegexOptions {
412 | /** Whether the regex specified is case sensitive.
413 | * Default is true.
414 | */
415 | isCaseSensitive?: boolean | undefined;
416 |
417 | /** The regular expression to check. */
418 | regex: string;
419 |
420 | /** Whether the regex specified requires capturing.
421 | * Capturing is only required for redirect rules which specify a regexSubstitution action.
422 | * The default is false.
423 | */
424 | requireCapturing?: boolean | undefined;
425 | }
426 |
427 | export interface IsRegexSupportedResult {
428 | isSupported: boolean;
429 |
430 | /** Specifies the reason why the regular expression is not supported.
431 | * Only provided if isSupported is false.
432 | */
433 | reason?: UnsupportedRegexReason | undefined;
434 | }
435 |
436 | export interface TabActionCountUpdate {
437 | /** The amount to increment the tab's action count by.
438 | * Negative values will decrement the count
439 | */
440 | increment: number;
441 |
442 | /** The tab for which to update the action count. */
443 | tabId: number;
444 | }
445 |
446 | export interface ExtensionActionOptions {
447 | /** Whether to automatically display the action count for a page as the extension's badge text.
448 | * This preference is persisted across sessions.
449 | */
450 | displayActionCountAsBadgeText?: boolean | undefined;
451 |
452 | /** Details of how the tab's action count should be adjusted. */
453 | tabUpdate?: TabActionCountUpdate | undefined;
454 | }
455 |
456 | export interface Redirect {
457 | /** Path relative to the extension directory.
458 | * Should start with '/'.
459 | */
460 | extensionPath?: string | undefined;
461 |
462 | /** Substitution pattern for rules which specify a regexFilter.
463 | * The first match of regexFilter within the url will be replaced with this pattern.
464 | * Within regexSubstitution, backslash-escaped digits (\1 to \9) can be used to insert the corresponding capture groups.
465 | * \0 refers to the entire matching text.
466 | */
467 | regexSubstitution?: string | undefined;
468 |
469 | /** Url transformations to perform. */
470 | transform?: URLTransform | undefined;
471 |
472 | /** The redirect url.
473 | * Redirects to JavaScript urls are not allowed.
474 | */
475 | url?: string | undefined;
476 | }
477 |
478 | export interface UpdateRuleOptions {
479 | /** Rules to add. */
480 | addRules?: Rule[] | undefined;
481 |
482 | /**
483 | * IDs of the rules to remove.
484 | * Any invalid IDs will be ignored.
485 | */
486 | removeRuleIds?: number[] | undefined;
487 | }
488 |
489 | export interface UpdateStaticRulesOptions {
490 | /** Set of ids corresponding to rules in the Ruleset to disable. */
491 | disableRuleIds?: number[];
492 |
493 | /** Set of ids corresponding to rules in the Ruleset to enable. */
494 | enableRuleIds?: number[];
495 |
496 | /** The id corresponding to a static Ruleset. */
497 | rulesetId: string;
498 | }
499 |
500 | export interface UpdateRulesetOptions {
501 | /** The set of ids corresponding to a static Ruleset that should be disabled. */
502 | disableRulesetIds?: string[] | undefined;
503 |
504 | /** The set of ids corresponding to a static Ruleset that should be enabled. */
505 | enableRulesetIds?: string[] | undefined;
506 | }
507 |
508 | export interface MatchedRuleInfoDebug {
509 | /** Details about the request for which the rule was matched. */
510 | request: RequestDetails;
511 |
512 | rule: MatchedRule;
513 | }
514 |
515 | export interface Ruleset {
516 | /** Whether the ruleset is enabled by default. */
517 | enabled: boolean;
518 |
519 | /** A non-empty string uniquely identifying the ruleset.
520 | * IDs beginning with '_' are reserved for internal use.
521 | */
522 | id: string;
523 |
524 | /** The path of the JSON ruleset relative to the extension directory. */
525 | path: string;
526 | }
527 |
528 | export interface RulesMatchedDetails {
529 | /** Rules matching the given filter. */
530 | rulesMatchedInfo: MatchedRuleInfo[];
531 | }
532 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { parse } from "jsonc-parser";
2 |
3 | export type Item = { title: string; rule: string };
4 |
5 | export type ById = Record;
6 |
7 | // the preivous version is `data`
8 | export const byIdKey = "byId";
9 |
10 | export const updateRules = async (
11 | options: chrome.declarativeNetRequest.UpdateRuleOptions,
12 | ) => {
13 | return new Promise((resolve, reject) => {
14 | chrome.declarativeNetRequest.updateDynamicRules(options, () => {
15 | if (chrome.runtime.lastError) {
16 | reject(chrome.runtime.lastError);
17 | } else {
18 | console.log("Rule added or removed successfully");
19 | resolve(0);
20 | }
21 | });
22 | });
23 | };
24 |
25 | export const findNewRuleId = async () => {
26 | const currentRules = await chrome.declarativeNetRequest.getDynamicRules();
27 | const ruleId = Math.max(...currentRules.map((rule) => rule.id)) + 1;
28 | return ruleId;
29 | };
30 |
31 | export const parseRule = (rule: string) => {
32 | // TODO: runtime type checking
33 | return parse(rule) as Omit;
34 | };
35 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src"],
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "esnext",
6 | "lib": ["esnext", "dom"],
7 | "jsx": "react-jsx",
8 | "noEmit": true,
9 | "strict": true,
10 | "moduleResolution": "node",
11 | "esModuleInterop": true,
12 | "skipLibCheck": true,
13 | "resolveJsonModule": true,
14 | "noUncheckedIndexedAccess": true,
15 | "verbatimModuleSyntax": true
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import { crx } from "@crxjs/vite-plugin";
3 | import pkg from "./package.json";
4 |
5 | export default defineConfig({
6 | build: {
7 | rollupOptions: {
8 | input: ["dashboard.html"],
9 | },
10 | },
11 | plugins: [
12 | crx({
13 | manifest: {
14 | manifest_version: 3,
15 | name: "Tampery",
16 | version: pkg.version,
17 | description: "Tamper browser requests in flight",
18 | homepage_url: "https://github.com/pd4d10/tampery",
19 | icons: {
20 | "128": "icon.png",
21 | },
22 | background: {
23 | service_worker: "src/background.ts",
24 | },
25 | permissions: ["webRequest", "storage", "declarativeNetRequest"],
26 | host_permissions: [""],
27 | action: {
28 | default_popup: "popup.html",
29 | },
30 | },
31 | }),
32 | ],
33 | });
34 |
--------------------------------------------------------------------------------