@adguard/agtree 1.1.3 → 1.1.5
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/CHANGELOG.md +19 -0
- package/dist/agtree.cjs +326 -15
- package/dist/agtree.d.ts +1 -1
- package/dist/agtree.esm.js +326 -15
- package/dist/agtree.iife.min.js +5 -5
- package/dist/agtree.umd.min.js +5 -5
- package/dist/build.txt +1 -1
- package/package.json +10 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog][keepachangelog], and this project adheres to [Semantic Versioning][semver].
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
## 1.1.5 - 2023-09-05
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Validation of `$csp` and `$permissions` modifiers value
|
|
13
|
+
by custom pre-defined validator instead of regular expression
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Exports to `package.json`
|
|
18
|
+
|
|
19
|
+
## 1.1.4 - 2023-08-30
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Validation of `$redirect` and `$replace` modifiers by `ModifierValidator.validate()`
|
|
24
|
+
|
|
25
|
+
|
|
7
26
|
## 1.1.3 - 2023-08-28
|
|
8
27
|
|
|
9
28
|
### Added
|
package/dist/agtree.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v1.1.
|
|
2
|
+
* AGTree v1.1.5 (build date: Tue, 05 Sep 2023 15:10:53 GMT)
|
|
3
3
|
* (c) 2023 AdGuard Software Ltd.
|
|
4
4
|
* Released under the MIT license
|
|
5
5
|
* https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
|
|
@@ -88,6 +88,9 @@ exports.AdblockSyntax = void 0;
|
|
|
88
88
|
* @file Constant values used by all parts of the library
|
|
89
89
|
*/
|
|
90
90
|
// General
|
|
91
|
+
/**
|
|
92
|
+
* Empty string.
|
|
93
|
+
*/
|
|
91
94
|
const EMPTY = '';
|
|
92
95
|
const SPACE = ' ';
|
|
93
96
|
const TAB = '\t';
|
|
@@ -6144,7 +6147,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
6144
6147
|
assignable:true,
|
|
6145
6148
|
negatable:false,
|
|
6146
6149
|
value_optional:true,
|
|
6147
|
-
value_format:"
|
|
6150
|
+
value_format:"csp_value" },
|
|
6148
6151
|
adg_ext_any:{ name:"csp",
|
|
6149
6152
|
description:"This modifier completely changes the rule behavior.\nIf it is applied to a rule, it will not block the matching request.\nThe response headers are going to be modified instead.",
|
|
6150
6153
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#csp-modifier",
|
|
@@ -6156,7 +6159,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
6156
6159
|
assignable:true,
|
|
6157
6160
|
negatable:false,
|
|
6158
6161
|
value_optional:true,
|
|
6159
|
-
value_format:"
|
|
6162
|
+
value_format:"csp_value" },
|
|
6160
6163
|
abp_ext_any:{ name:"csp",
|
|
6161
6164
|
description:"This modifier completely changes the rule behavior.\nIf it is applied to a rule, it will not block the matching request.\nThe response headers are going to be modified instead.",
|
|
6162
6165
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293-How-to-write-filters#content-security-policies",
|
|
@@ -6166,7 +6169,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
6166
6169
|
assignable:true,
|
|
6167
6170
|
negatable:false,
|
|
6168
6171
|
value_optional:true,
|
|
6169
|
-
value_format:"
|
|
6172
|
+
value_format:"csp_value" },
|
|
6170
6173
|
ubo_ext_any:{ name:"csp",
|
|
6171
6174
|
description:"This modifier completely changes the rule behavior.\nIf it is applied to a rule, it will not block the matching request.\nThe response headers are going to be modified instead.",
|
|
6172
6175
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#csp",
|
|
@@ -6178,7 +6181,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
6178
6181
|
assignable:true,
|
|
6179
6182
|
negatable:false,
|
|
6180
6183
|
value_optional:true,
|
|
6181
|
-
value_format:"
|
|
6184
|
+
value_format:"csp_value" } };
|
|
6182
6185
|
|
|
6183
6186
|
var data$M = { adg_os_any:{ name:"denyallow",
|
|
6184
6187
|
description:"The `$denyallow` modifier allows to avoid creating additional rules\nwhen it is needed to disable a certain rule for specific domains.\n`$denyallow` matches only target domains and not referrer domains.",
|
|
@@ -6685,7 +6688,8 @@ var data$l = { adg_os_any:{ name:"permissions",
|
|
|
6685
6688
|
inverse_conflicts:true,
|
|
6686
6689
|
assignable:true,
|
|
6687
6690
|
negatable:false,
|
|
6688
|
-
|
|
6691
|
+
value_optional:true,
|
|
6692
|
+
value_format:"permissions_value" } };
|
|
6689
6693
|
|
|
6690
6694
|
var data$k = { adg_any:{ name:"ping",
|
|
6691
6695
|
description:"The rule corresponds to requests caused by either navigator.sendBeacon() or the ping attribute on links.",
|
|
@@ -6813,14 +6817,14 @@ var data$g = { adg_os_any:{ name:"redirect",
|
|
|
6813
6817
|
assignable:true,
|
|
6814
6818
|
negatable:false,
|
|
6815
6819
|
value_optional:true,
|
|
6816
|
-
value_format:"(?x)\n ^(\n 1x1-transparent\\.gif|\n 2x2-transparent\\.png|\n 3x2-transparent\\.png|\n 32x32-transparent\\.png|\n noopframe|\n noopcss|\n noopjs|\n noopjson|\n nooptext|\n empty|\n noopvmap-1\\.0|\n noopvast-2\\.0|\n noopvast-3\\.0|\n noopvast-4\\.0|\n noopmp3-0\\.1s|\n noopmp4-1s|\n amazon-apstag|\n ati-smarttag|\n didomi-loader|\n fingerprintjs2|\n fingerprintjs3|\n gemius|\n google-analytics-ga|\n google-analytics|\n google-ima3|\n googlesyndication-adsbygoogle|\n googletagservices-gpt|\n matomo|\n metrika-yandex-tag|\n metrika-yandex-watch|\n naver-wcslog|\n noeval|\n pardot-1\\.0|\n prebid-ads|\n prebid|\n prevent-bab|\n prevent-bab2|\n prevent-fab-3\\.2\\.0|\n prevent-popads-net|\n scorecardresearch-beacon|\n set-popads-dummy|\n click2load\\.html\n )?$" },
|
|
6820
|
+
value_format:"(?x)\n ^(\n 1x1-transparent\\.gif|\n 2x2-transparent\\.png|\n 3x2-transparent\\.png|\n 32x32-transparent\\.png|\n noopframe|\n noopcss|\n noopjs|\n noopjson|\n nooptext|\n empty|\n noopvmap-1\\.0|\n noopvast-2\\.0|\n noopvast-3\\.0|\n noopvast-4\\.0|\n noopmp3-0\\.1s|\n noopmp4-1s|\n amazon-apstag|\n ati-smarttag|\n didomi-loader|\n fingerprintjs2|\n fingerprintjs3|\n gemius|\n google-analytics-ga|\n google-analytics|\n googletagmanager-gtm|\n google-ima3|\n googlesyndication-adsbygoogle|\n googletagservices-gpt|\n matomo|\n metrika-yandex-tag|\n metrika-yandex-watch|\n naver-wcslog|\n noeval|\n pardot-1\\.0|\n prebid-ads|\n prebid|\n prevent-bab|\n prevent-bab2|\n prevent-fab-3\\.2\\.0|\n prevent-popads-net|\n scorecardresearch-beacon|\n set-popads-dummy|\n click2load\\.html\n )?$" },
|
|
6817
6821
|
adg_ext_any:{ name:"redirect",
|
|
6818
6822
|
description:"Used to redirect web requests to a local \"resource\".",
|
|
6819
6823
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#redirect-modifier",
|
|
6820
6824
|
assignable:true,
|
|
6821
6825
|
negatable:false,
|
|
6822
6826
|
value_optional:true,
|
|
6823
|
-
value_format:"(?x)\n ^(\n 1x1-transparent\\.gif|\n 2x2-transparent\\.png|\n 3x2-transparent\\.png|\n 32x32-transparent\\.png|\n noopframe|\n noopcss|\n noopjs|\n noopjson|\n nooptext|\n empty|\n noopvmap-1\\.0|\n noopvast-2\\.0|\n noopvast-3\\.0|\n noopvast-4\\.0|\n noopmp3-0\\.1s|\n noopmp4-1s|\n amazon-apstag|\n ati-smarttag|\n didomi-loader|\n fingerprintjs2|\n fingerprintjs3|\n gemius|\n google-analytics-ga|\n google-analytics|\n google-ima3|\n googlesyndication-adsbygoogle|\n googletagservices-gpt|\n matomo|\n metrika-yandex-tag|\n metrika-yandex-watch|\n naver-wcslog|\n noeval|\n pardot-1\\.0|\n prebid-ads|\n prebid|\n prevent-bab|\n prevent-bab2|\n prevent-fab-3\\.2\\.0|\n prevent-popads-net|\n scorecardresearch-beacon|\n set-popads-dummy|\n click2load\\.html\n )?$" },
|
|
6827
|
+
value_format:"(?x)\n ^(\n 1x1-transparent\\.gif|\n 2x2-transparent\\.png|\n 3x2-transparent\\.png|\n 32x32-transparent\\.png|\n noopframe|\n noopcss|\n noopjs|\n noopjson|\n nooptext|\n empty|\n noopvmap-1\\.0|\n noopvast-2\\.0|\n noopvast-3\\.0|\n noopvast-4\\.0|\n noopmp3-0\\.1s|\n noopmp4-1s|\n amazon-apstag|\n ati-smarttag|\n didomi-loader|\n fingerprintjs2|\n fingerprintjs3|\n gemius|\n google-analytics-ga|\n google-analytics|\n googletagmanager-gtm|\n google-ima3|\n googlesyndication-adsbygoogle|\n googletagservices-gpt|\n matomo|\n metrika-yandex-tag|\n metrika-yandex-watch|\n naver-wcslog|\n noeval|\n pardot-1\\.0|\n prebid-ads|\n prebid|\n prevent-bab|\n prevent-bab2|\n prevent-fab-3\\.2\\.0|\n prevent-popads-net|\n scorecardresearch-beacon|\n set-popads-dummy|\n click2load\\.html\n )?$" },
|
|
6824
6828
|
ubo_ext_any:{ name:"redirect",
|
|
6825
6829
|
description:"Used to redirect web requests to a local \"resource\".",
|
|
6826
6830
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#redirect",
|
|
@@ -6929,7 +6933,7 @@ var data$d = { adg_os_any:{ name:"replace",
|
|
|
6929
6933
|
inverse_conflicts:true,
|
|
6930
6934
|
assignable:true,
|
|
6931
6935
|
negatable:false,
|
|
6932
|
-
value_format:"(?xi)\n ^\n \\/\n # the regexp to match by\n (.+)\n # separator\n \\/\n # replacement\n (
|
|
6936
|
+
value_format:"(?xi)\n ^\n \\/\n # the regexp to match by\n (.+)\n # separator\n \\/\n # replacement\n (.+)?\n \\/\n # flags\n ([gimuy]*)?\n $" },
|
|
6933
6937
|
adg_ext_firefox:{ name:"replace",
|
|
6934
6938
|
description:"This modifier completely changes the rule behavior.\nIf it is applied, the rule will not block the request. The response is going to be modified instead.",
|
|
6935
6939
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#replace-modifier",
|
|
@@ -6947,7 +6951,7 @@ var data$d = { adg_os_any:{ name:"replace",
|
|
|
6947
6951
|
inverse_conflicts:true,
|
|
6948
6952
|
assignable:true,
|
|
6949
6953
|
negatable:false,
|
|
6950
|
-
value_format:"(?xi)\n ^\n \\/\n # the regexp to match by\n (.+)\n # separator\n \\/\n # replacement\n (
|
|
6954
|
+
value_format:"(?xi)\n ^\n \\/\n # the regexp to match by\n (.+)\n # separator\n \\/\n # replacement\n (.+)?\n \\/\n # flags\n ([gimuy]*)?\n $" } };
|
|
6951
6955
|
|
|
6952
6956
|
var data$c = { adg_any:{ name:"script",
|
|
6953
6957
|
description:"The rule corresponds to script requests, e.g. javascript, vbscript.",
|
|
@@ -7346,15 +7350,104 @@ const ALLOWED_STEALTH_OPTIONS = new Set([
|
|
|
7346
7350
|
'xclientdata',
|
|
7347
7351
|
'dpi',
|
|
7348
7352
|
]);
|
|
7353
|
+
/**
|
|
7354
|
+
* Allowed CSP directives for $csp modifier.
|
|
7355
|
+
*
|
|
7356
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy#directives}
|
|
7357
|
+
*/
|
|
7358
|
+
const ALLOWED_CSP_DIRECTIVES = new Set([
|
|
7359
|
+
'base-uri',
|
|
7360
|
+
'child-src',
|
|
7361
|
+
'connect-src',
|
|
7362
|
+
'default-src',
|
|
7363
|
+
'font-src',
|
|
7364
|
+
'form-action',
|
|
7365
|
+
'frame-ancestors',
|
|
7366
|
+
'frame-src',
|
|
7367
|
+
'img-src',
|
|
7368
|
+
'manifest-src',
|
|
7369
|
+
'media-src',
|
|
7370
|
+
'navigate-to',
|
|
7371
|
+
'object-src',
|
|
7372
|
+
'plugin-types',
|
|
7373
|
+
'prefetch-src',
|
|
7374
|
+
'report-to',
|
|
7375
|
+
'report-uri',
|
|
7376
|
+
'sandbox',
|
|
7377
|
+
'script-src',
|
|
7378
|
+
'style-src',
|
|
7379
|
+
'upgrade-insecure-requests',
|
|
7380
|
+
'worker-src',
|
|
7381
|
+
]);
|
|
7382
|
+
/**
|
|
7383
|
+
* Allowed stealth options for $permissions modifier.
|
|
7384
|
+
*
|
|
7385
|
+
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#permissions-modifier}
|
|
7386
|
+
*/
|
|
7387
|
+
const ALLOWED_PERMISSION_DIRECTIVES = new Set([
|
|
7388
|
+
'accelerometer',
|
|
7389
|
+
'ambient-light-sensor',
|
|
7390
|
+
'autoplay',
|
|
7391
|
+
'battery',
|
|
7392
|
+
'camera',
|
|
7393
|
+
'display-capture',
|
|
7394
|
+
'document-domain',
|
|
7395
|
+
'encrypted-media',
|
|
7396
|
+
'execution-while-not-rendered',
|
|
7397
|
+
'execution-while-out-of-viewport',
|
|
7398
|
+
'fullscreen',
|
|
7399
|
+
'gamepad',
|
|
7400
|
+
'geolocation',
|
|
7401
|
+
'gyroscope',
|
|
7402
|
+
'hid',
|
|
7403
|
+
'identity-credentials-get',
|
|
7404
|
+
'idle-detection',
|
|
7405
|
+
'local-fonts',
|
|
7406
|
+
'magnetometer',
|
|
7407
|
+
'microphone',
|
|
7408
|
+
'midi',
|
|
7409
|
+
'payment',
|
|
7410
|
+
'picture-in-picture',
|
|
7411
|
+
'publickey-credentials-create',
|
|
7412
|
+
'publickey-credentials-get',
|
|
7413
|
+
'screen-wake-lock',
|
|
7414
|
+
'serial',
|
|
7415
|
+
'speaker-selection',
|
|
7416
|
+
'storage-access',
|
|
7417
|
+
'usb',
|
|
7418
|
+
'web-share',
|
|
7419
|
+
'xr-spatial-tracking',
|
|
7420
|
+
]);
|
|
7421
|
+
/**
|
|
7422
|
+
* One of available tokens for $permission modifier value.
|
|
7423
|
+
*
|
|
7424
|
+
* @see {@link https://w3c.github.io/webappsec-permissions-policy/#structured-header-serialization}
|
|
7425
|
+
*/
|
|
7426
|
+
const PERMISSIONS_TOKEN_SELF = 'self';
|
|
7427
|
+
/**
|
|
7428
|
+
* One of allowlist values for $permissions modifier.
|
|
7429
|
+
*
|
|
7430
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Permissions_Policy#allowlists}
|
|
7431
|
+
*/
|
|
7432
|
+
const EMPTY_PERMISSIONS_ALLOWLIST = `${OPEN_PARENTHESIS}${CLOSE_PARENTHESIS}`;
|
|
7349
7433
|
/**
|
|
7350
7434
|
* Prefixes for error messages used in modifier validation.
|
|
7351
7435
|
*/
|
|
7352
7436
|
const VALIDATION_ERROR_PREFIX = {
|
|
7353
7437
|
BLOCK_ONLY: 'Only blocking rules may contain the modifier',
|
|
7354
7438
|
EXCEPTION_ONLY: 'Only exception rules may contain the modifier',
|
|
7439
|
+
INVALID_CSP_DIRECTIVES: 'Invalid CSP directives for the modifier',
|
|
7355
7440
|
INVALID_LIST_VALUES: 'Invalid values for the modifier',
|
|
7356
7441
|
INVALID_NOOP: 'Invalid noop modifier',
|
|
7442
|
+
INVALID_PERMISSION_DIRECTIVE: 'Invalid Permissions-Policy directive for the modifier',
|
|
7443
|
+
INVALID_PERMISSION_ORIGINS: 'Origins in the value is invalid for the modifier and the directive',
|
|
7444
|
+
INVALID_PERMISSION_ORIGIN_QUOTES: 'Double quotes should be used for origins in the value of the modifier',
|
|
7357
7445
|
MIXED_NEGATIONS: 'Simultaneous usage of negated and not negated values is forbidden for the modifier',
|
|
7446
|
+
NO_CSP_VALUE: 'No CSP value for the modifier and the directive',
|
|
7447
|
+
NO_CSP_DIRECTIVE_QUOTE: 'CSP directives should no be quoted for the modifier',
|
|
7448
|
+
NO_UNESCAPED_PERMISSION_COMMA: 'Unescaped comma in the value is not allowed for the modifier',
|
|
7449
|
+
// TODO: implement later for $scp and $permissions
|
|
7450
|
+
// NO_VALUE_ONLY_FOR_EXCEPTION: 'Modifier without value can be used only in exception rules',
|
|
7358
7451
|
NOT_EXISTENT: 'Non-existent modifier',
|
|
7359
7452
|
NOT_NEGATABLE_MODIFIER: 'Non-negatable modifier',
|
|
7360
7453
|
NOT_NEGATABLE_VALUE: 'Values cannot be negated for the modifier',
|
|
@@ -7666,10 +7759,12 @@ class QuoteUtils {
|
|
|
7666
7759
|
var CustomValueFormatValidatorName;
|
|
7667
7760
|
(function (CustomValueFormatValidatorName) {
|
|
7668
7761
|
CustomValueFormatValidatorName["App"] = "pipe_separated_apps";
|
|
7762
|
+
CustomValueFormatValidatorName["Csp"] = "csp_value";
|
|
7669
7763
|
// there are some differences between $domain and $denyallow
|
|
7670
7764
|
CustomValueFormatValidatorName["DenyAllow"] = "pipe_separated_denyallow_domains";
|
|
7671
7765
|
CustomValueFormatValidatorName["Domain"] = "pipe_separated_domains";
|
|
7672
7766
|
CustomValueFormatValidatorName["Method"] = "pipe_separated_methods";
|
|
7767
|
+
CustomValueFormatValidatorName["Permissions"] = "permissions_value";
|
|
7673
7768
|
CustomValueFormatValidatorName["StealthOption"] = "pipe_separated_stealth_options";
|
|
7674
7769
|
})(CustomValueFormatValidatorName || (CustomValueFormatValidatorName = {}));
|
|
7675
7770
|
/**
|
|
@@ -7730,6 +7825,32 @@ const isValidMethodModifierValue = (value) => {
|
|
|
7730
7825
|
const isValidStealthModifierValue = (value) => {
|
|
7731
7826
|
return ALLOWED_STEALTH_OPTIONS.has(value);
|
|
7732
7827
|
};
|
|
7828
|
+
/**
|
|
7829
|
+
* Checks whether the given `rawOrigin` is valid as Permissions Allowlist origin.
|
|
7830
|
+
*
|
|
7831
|
+
* @see {@link https://w3c.github.io/webappsec-permissions-policy/#allowlists}
|
|
7832
|
+
*
|
|
7833
|
+
* @param rawOrigin The raw origin.
|
|
7834
|
+
*
|
|
7835
|
+
* @returns True if the origin is valid, false otherwise.
|
|
7836
|
+
*/
|
|
7837
|
+
const isValidPermissionsOrigin = (rawOrigin) => {
|
|
7838
|
+
// origins should be quoted by double quote
|
|
7839
|
+
const actualQuoteType = QuoteUtils.getStringQuoteType(rawOrigin);
|
|
7840
|
+
if (actualQuoteType !== exports.QuoteType.Double) {
|
|
7841
|
+
return false;
|
|
7842
|
+
}
|
|
7843
|
+
const origin = QuoteUtils.removeQuotes(rawOrigin);
|
|
7844
|
+
try {
|
|
7845
|
+
// validate the origin by URL constructor
|
|
7846
|
+
// https://w3c.github.io/webappsec-permissions-policy/#algo-parse-policy-directive
|
|
7847
|
+
new URL(origin);
|
|
7848
|
+
}
|
|
7849
|
+
catch (e) {
|
|
7850
|
+
return false;
|
|
7851
|
+
}
|
|
7852
|
+
return true;
|
|
7853
|
+
};
|
|
7733
7854
|
/**
|
|
7734
7855
|
* Checks whether the given `value` is valid domain as $denyallow modifier value.
|
|
7735
7856
|
* Important: wildcard tld are not supported, compared to $domain.
|
|
@@ -7920,14 +8041,196 @@ const validatePipeSeparatedMethods = (modifier) => {
|
|
|
7920
8041
|
const validatePipeSeparatedStealthOptions = (modifier) => {
|
|
7921
8042
|
return validateListItemsModifier(modifier, (raw) => StealthOptionListParser.parse(raw), isValidStealthModifierValue, customNoNegatedListItemsValidator);
|
|
7922
8043
|
};
|
|
8044
|
+
/**
|
|
8045
|
+
* Validates `csp_value` custom value format.
|
|
8046
|
+
* Used for $csp modifier.
|
|
8047
|
+
*
|
|
8048
|
+
* @param modifier Modifier AST node.
|
|
8049
|
+
*
|
|
8050
|
+
* @returns Validation result.
|
|
8051
|
+
*/
|
|
8052
|
+
const validateCspValue = (modifier) => {
|
|
8053
|
+
const modifierName = modifier.modifier.value;
|
|
8054
|
+
if (!modifier.value?.value) {
|
|
8055
|
+
return getValueRequiredValidationResult(modifierName);
|
|
8056
|
+
}
|
|
8057
|
+
// $csp modifier value may contain multiple directives
|
|
8058
|
+
// e.g. "csp=child-src 'none'; frame-src 'self' *; worker-src 'none'"
|
|
8059
|
+
const policyDirectives = modifier.value.value
|
|
8060
|
+
.split(SEMICOLON)
|
|
8061
|
+
// rule with $csp modifier may end with semicolon
|
|
8062
|
+
// e.g. "$csp=sandbox allow-same-origin;"
|
|
8063
|
+
// TODO: add predicate helper for `(i) => !!i`
|
|
8064
|
+
.filter((i) => !!i);
|
|
8065
|
+
const invalidValueValidationResult = getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}': "${modifier.value.value}"`);
|
|
8066
|
+
if (policyDirectives.length === 0) {
|
|
8067
|
+
return invalidValueValidationResult;
|
|
8068
|
+
}
|
|
8069
|
+
const invalidDirectives = [];
|
|
8070
|
+
for (let i = 0; i < policyDirectives.length; i += 1) {
|
|
8071
|
+
const policyDirective = policyDirectives[i].trim();
|
|
8072
|
+
if (!policyDirective) {
|
|
8073
|
+
return invalidValueValidationResult;
|
|
8074
|
+
}
|
|
8075
|
+
const chunks = policyDirective.split(SPACE);
|
|
8076
|
+
const [directive, ...valueChunks] = chunks;
|
|
8077
|
+
// e.g. "csp=child-src 'none'; ; worker-src 'none'"
|
|
8078
|
+
// validator it here ↑
|
|
8079
|
+
if (!directive) {
|
|
8080
|
+
return invalidValueValidationResult;
|
|
8081
|
+
}
|
|
8082
|
+
if (!ALLOWED_CSP_DIRECTIVES.has(directive)) {
|
|
8083
|
+
// e.g. "csp='child-src' 'none'"
|
|
8084
|
+
if (ALLOWED_CSP_DIRECTIVES.has(QuoteUtils.removeQuotes(directive))) {
|
|
8085
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NO_CSP_DIRECTIVE_QUOTE}: '${modifierName}': ${directive}`);
|
|
8086
|
+
}
|
|
8087
|
+
invalidDirectives.push(directive);
|
|
8088
|
+
continue;
|
|
8089
|
+
}
|
|
8090
|
+
if (valueChunks.length === 0) {
|
|
8091
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NO_CSP_VALUE}: '${modifierName}': '${directive}'`);
|
|
8092
|
+
}
|
|
8093
|
+
}
|
|
8094
|
+
if (invalidDirectives.length > 0) {
|
|
8095
|
+
const directivesToStr = QuoteUtils.quoteAndJoinStrings(invalidDirectives, exports.QuoteType.Double);
|
|
8096
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.INVALID_CSP_DIRECTIVES}: '${modifierName}': ${directivesToStr}`);
|
|
8097
|
+
}
|
|
8098
|
+
return { valid: true };
|
|
8099
|
+
};
|
|
8100
|
+
/**
|
|
8101
|
+
* Validates permission allowlist origins in the value of $permissions modifier.
|
|
8102
|
+
*
|
|
8103
|
+
* @see {@link https://w3c.github.io/webappsec-permissions-policy/#allowlists}
|
|
8104
|
+
*
|
|
8105
|
+
* @param allowlistChunks Array of allowlist chunks.
|
|
8106
|
+
* @param directive Permission directive name.
|
|
8107
|
+
* @param modifierName Modifier name.
|
|
8108
|
+
*
|
|
8109
|
+
* @returns Validation result.
|
|
8110
|
+
*/
|
|
8111
|
+
const validatePermissionAllowlistOrigins = (allowlistChunks, directive, modifierName) => {
|
|
8112
|
+
const invalidOrigins = [];
|
|
8113
|
+
for (let i = 0; i < allowlistChunks.length; i += 1) {
|
|
8114
|
+
const chunk = allowlistChunks[i].trim();
|
|
8115
|
+
// skip few spaces between origins (they were splitted by space)
|
|
8116
|
+
// e.g. 'geolocation=("https://example.com" "https://*.example.com")'
|
|
8117
|
+
if (chunk.length === 0) {
|
|
8118
|
+
continue;
|
|
8119
|
+
}
|
|
8120
|
+
/**
|
|
8121
|
+
* 'self' should be checked case-insensitively
|
|
8122
|
+
*
|
|
8123
|
+
* @see {@link https://w3c.github.io/webappsec-permissions-policy/#algo-parse-policy-directive}
|
|
8124
|
+
*
|
|
8125
|
+
* @example 'geolocation=(self)'
|
|
8126
|
+
*/
|
|
8127
|
+
if (chunk.toLowerCase() === PERMISSIONS_TOKEN_SELF) {
|
|
8128
|
+
continue;
|
|
8129
|
+
}
|
|
8130
|
+
if (QuoteUtils.getStringQuoteType(chunk) !== exports.QuoteType.Double) {
|
|
8131
|
+
return getInvalidValidationResult(
|
|
8132
|
+
// eslint-disable-next-line max-len
|
|
8133
|
+
`${VALIDATION_ERROR_PREFIX.INVALID_PERMISSION_ORIGIN_QUOTES}: '${modifierName}': '${directive}': '${QuoteUtils.removeQuotes(chunk)}'`);
|
|
8134
|
+
}
|
|
8135
|
+
if (!isValidPermissionsOrigin(chunk)) {
|
|
8136
|
+
invalidOrigins.push(chunk);
|
|
8137
|
+
}
|
|
8138
|
+
}
|
|
8139
|
+
if (invalidOrigins.length > 0) {
|
|
8140
|
+
const originsToStr = QuoteUtils.quoteAndJoinStrings(invalidOrigins);
|
|
8141
|
+
return getInvalidValidationResult(
|
|
8142
|
+
// eslint-disable-next-line max-len
|
|
8143
|
+
`${VALIDATION_ERROR_PREFIX.INVALID_PERMISSION_ORIGINS}: '${modifierName}': '${directive}': ${originsToStr}`);
|
|
8144
|
+
}
|
|
8145
|
+
return { valid: true };
|
|
8146
|
+
};
|
|
8147
|
+
/**
|
|
8148
|
+
* Validates permission allowlist in the modifier value.
|
|
8149
|
+
*
|
|
8150
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Permissions_Policy#allowlists}
|
|
8151
|
+
* @see {@link https://w3c.github.io/webappsec-permissions-policy/#allowlists}
|
|
8152
|
+
*
|
|
8153
|
+
* @param allowlist Allowlist value.
|
|
8154
|
+
* @param directive Permission directive name.
|
|
8155
|
+
* @param modifierName Modifier name.
|
|
8156
|
+
*
|
|
8157
|
+
* @returns Validation result.
|
|
8158
|
+
*/
|
|
8159
|
+
const validatePermissionAllowlist = (allowlist, directive, modifierName) => {
|
|
8160
|
+
// `*` is one of available permissions tokens
|
|
8161
|
+
// e.g. 'fullscreen=*'
|
|
8162
|
+
// https://w3c.github.io/webappsec-permissions-policy/#structured-header-serialization
|
|
8163
|
+
if (allowlist === WILDCARD$1
|
|
8164
|
+
// e.g. 'autoplay=()'
|
|
8165
|
+
|| allowlist === EMPTY_PERMISSIONS_ALLOWLIST) {
|
|
8166
|
+
return { valid: true };
|
|
8167
|
+
}
|
|
8168
|
+
if (!(allowlist.startsWith(OPEN_PARENTHESIS) && allowlist.endsWith(CLOSE_PARENTHESIS))) {
|
|
8169
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}'`);
|
|
8170
|
+
}
|
|
8171
|
+
const allowlistChunks = allowlist.slice(1, -1).split(SPACE);
|
|
8172
|
+
return validatePermissionAllowlistOrigins(allowlistChunks, directive, modifierName);
|
|
8173
|
+
};
|
|
8174
|
+
/**
|
|
8175
|
+
* Validates single permission in the modifier value.
|
|
8176
|
+
*
|
|
8177
|
+
* @param permission Single permission value.
|
|
8178
|
+
* @param modifierName Modifier name.
|
|
8179
|
+
* @param modifierValue Modifier value.
|
|
8180
|
+
*
|
|
8181
|
+
* @returns Validation result.
|
|
8182
|
+
*/
|
|
8183
|
+
const validateSinglePermission = (permission, modifierName, modifierValue) => {
|
|
8184
|
+
// empty permission in the rule
|
|
8185
|
+
// e.g. 'permissions=storage-access=()\\, \\, camera=()'
|
|
8186
|
+
// the validator is here ↑
|
|
8187
|
+
if (!permission) {
|
|
8188
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}'`);
|
|
8189
|
+
}
|
|
8190
|
+
if (permission.includes(COMMA)) {
|
|
8191
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NO_UNESCAPED_PERMISSION_COMMA}: '${modifierName}': '${modifierValue}'`);
|
|
8192
|
+
}
|
|
8193
|
+
const [directive, allowlist] = permission.split(EQUALS);
|
|
8194
|
+
if (!ALLOWED_PERMISSION_DIRECTIVES.has(directive)) {
|
|
8195
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.INVALID_PERMISSION_DIRECTIVE}: '${modifierName}': '${directive}'`);
|
|
8196
|
+
}
|
|
8197
|
+
return validatePermissionAllowlist(allowlist, directive, modifierName);
|
|
8198
|
+
};
|
|
8199
|
+
/**
|
|
8200
|
+
* Validates `permissions_value` custom value format.
|
|
8201
|
+
* Used for $permissions modifier.
|
|
8202
|
+
*
|
|
8203
|
+
* @param modifier Modifier AST node.
|
|
8204
|
+
*
|
|
8205
|
+
* @returns Validation result.
|
|
8206
|
+
*/
|
|
8207
|
+
const validatePermissions = (modifier) => {
|
|
8208
|
+
if (!modifier.value?.value) {
|
|
8209
|
+
return getValueRequiredValidationResult(modifier.modifier.value);
|
|
8210
|
+
}
|
|
8211
|
+
const modifierName = modifier.modifier.value;
|
|
8212
|
+
const modifierValue = modifier.value.value;
|
|
8213
|
+
// multiple permissions may be separated by escaped commas
|
|
8214
|
+
const permissions = modifier.value.value.split(`${BACKSLASH}${COMMA}`);
|
|
8215
|
+
for (let i = 0; i < permissions.length; i += 1) {
|
|
8216
|
+
const permission = permissions[i].trim();
|
|
8217
|
+
const singlePermissionValidationResult = validateSinglePermission(permission, modifierName, modifierValue);
|
|
8218
|
+
if (!singlePermissionValidationResult.valid) {
|
|
8219
|
+
return singlePermissionValidationResult;
|
|
8220
|
+
}
|
|
8221
|
+
}
|
|
8222
|
+
return { valid: true };
|
|
8223
|
+
};
|
|
7923
8224
|
/**
|
|
7924
8225
|
* Map of all available pre-defined validators for modifiers with custom `value_format`.
|
|
7925
8226
|
*/
|
|
7926
8227
|
const CUSTOM_VALUE_FORMAT_MAP = {
|
|
7927
8228
|
[CustomValueFormatValidatorName.App]: validatePipeSeparatedApps,
|
|
8229
|
+
[CustomValueFormatValidatorName.Csp]: validateCspValue,
|
|
7928
8230
|
[CustomValueFormatValidatorName.DenyAllow]: validatePipeSeparatedDenyAllowDomains,
|
|
7929
8231
|
[CustomValueFormatValidatorName.Domain]: validatePipeSeparatedDomains,
|
|
7930
8232
|
[CustomValueFormatValidatorName.Method]: validatePipeSeparatedMethods,
|
|
8233
|
+
[CustomValueFormatValidatorName.Permissions]: validatePermissions,
|
|
7931
8234
|
[CustomValueFormatValidatorName.StealthOption]: validatePipeSeparatedStealthOptions,
|
|
7932
8235
|
};
|
|
7933
8236
|
/**
|
|
@@ -7954,9 +8257,8 @@ const validateValue = (modifier, valueFormat) => {
|
|
|
7954
8257
|
return validator(modifier);
|
|
7955
8258
|
}
|
|
7956
8259
|
const modifierName = modifier.modifier.value;
|
|
7957
|
-
const defaultInvalidValueResult = getValueRequiredValidationResult(modifierName);
|
|
7958
8260
|
if (!modifier.value?.value) {
|
|
7959
|
-
return
|
|
8261
|
+
return getValueRequiredValidationResult(modifierName);
|
|
7960
8262
|
}
|
|
7961
8263
|
let xRegExp;
|
|
7962
8264
|
try {
|
|
@@ -7967,7 +8269,7 @@ const validateValue = (modifier, valueFormat) => {
|
|
|
7967
8269
|
}
|
|
7968
8270
|
const isValid = xRegExp.test(modifier.value?.value);
|
|
7969
8271
|
if (!isValid) {
|
|
7970
|
-
return
|
|
8272
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}'`);
|
|
7971
8273
|
}
|
|
7972
8274
|
return { valid: true };
|
|
7973
8275
|
};
|
|
@@ -8031,6 +8333,10 @@ const validateForSpecificSyntax = (modifiersData, syntax, modifier, isException)
|
|
|
8031
8333
|
// e.g. 'domain'
|
|
8032
8334
|
if (specificBlockerData[SpecificKey.Assignable]) {
|
|
8033
8335
|
if (!modifier.value) {
|
|
8336
|
+
// TODO: ditch value_optional after custom validators are implemented for value_format for all modifiers.
|
|
8337
|
+
// This checking should be done in each separate custom validator,
|
|
8338
|
+
// because $csp and $permissions without value can be used only in extension rules,
|
|
8339
|
+
// but $cookie with no value can be used in both blocking and exception rules.
|
|
8034
8340
|
/**
|
|
8035
8341
|
* Some assignable modifiers can be used without a value,
|
|
8036
8342
|
* e.g. '@@||example.com^$cookie'.
|
|
@@ -9280,6 +9586,11 @@ class CosmeticRuleConverter extends RuleConverterBase {
|
|
|
9280
9586
|
/**
|
|
9281
9587
|
* @file Network rule modifier list converter.
|
|
9282
9588
|
*/
|
|
9589
|
+
// Since scriptlets library doesn't have ESM exports, we should import
|
|
9590
|
+
// the whole module and then extract the required functions from it here.
|
|
9591
|
+
// Otherwise importing AGTree will cause an error in ESM environment,
|
|
9592
|
+
// because scriptlets library doesn't support named exports.
|
|
9593
|
+
const { redirects } = scriptlets;
|
|
9283
9594
|
/**
|
|
9284
9595
|
* @see {@link https://adguard.com/kb/general/ad-filtering/create-own-filters/#csp-modifier}
|
|
9285
9596
|
*/
|
|
@@ -9395,7 +9706,7 @@ class NetworkRuleModifierListConverter extends ConverterBase {
|
|
|
9395
9706
|
: modifierNode.modifier.value;
|
|
9396
9707
|
// Try to convert the redirect resource name to ADG format
|
|
9397
9708
|
// This function returns undefined if the resource name is unknown
|
|
9398
|
-
const convertedRedirectResource =
|
|
9709
|
+
const convertedRedirectResource = redirects.convertRedirectNameToAdg(redirectResource);
|
|
9399
9710
|
convertedModifierList.children.push(createModifierNode(modifierName,
|
|
9400
9711
|
// If the redirect resource name is unknown, fall back to the original one
|
|
9401
9712
|
// Later, the validator will throw an error if the resource name is invalid
|
|
@@ -9616,7 +9927,7 @@ class LogicalExpressionUtils {
|
|
|
9616
9927
|
}
|
|
9617
9928
|
}
|
|
9618
9929
|
|
|
9619
|
-
const version$1 = "1.1.
|
|
9930
|
+
const version$1 = "1.1.5";
|
|
9620
9931
|
|
|
9621
9932
|
/**
|
|
9622
9933
|
* @file AGTree version
|
package/dist/agtree.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v1.1.
|
|
2
|
+
* AGTree v1.1.5 (build date: Tue, 05 Sep 2023 15:10:53 GMT)
|
|
3
3
|
* (c) 2023 AdGuard Software Ltd.
|
|
4
4
|
* Released under the MIT license
|
|
5
5
|
* https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
|