@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/dist/agtree.esm.js
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
|
|
@@ -13,7 +13,7 @@ export { ecssTree as ECSSTree };
|
|
|
13
13
|
import cloneDeep from 'clone-deep';
|
|
14
14
|
import XRegExp from 'xregexp';
|
|
15
15
|
import { parse as parse$1 } from 'tldts';
|
|
16
|
-
import
|
|
16
|
+
import scriptlets from '@adguard/scriptlets';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* @file Possible adblock syntaxes are listed here.
|
|
@@ -69,6 +69,9 @@ var AdblockSyntax;
|
|
|
69
69
|
* @file Constant values used by all parts of the library
|
|
70
70
|
*/
|
|
71
71
|
// General
|
|
72
|
+
/**
|
|
73
|
+
* Empty string.
|
|
74
|
+
*/
|
|
72
75
|
const EMPTY = '';
|
|
73
76
|
const SPACE = ' ';
|
|
74
77
|
const TAB = '\t';
|
|
@@ -6125,7 +6128,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
6125
6128
|
assignable:true,
|
|
6126
6129
|
negatable:false,
|
|
6127
6130
|
value_optional:true,
|
|
6128
|
-
value_format:"
|
|
6131
|
+
value_format:"csp_value" },
|
|
6129
6132
|
adg_ext_any:{ name:"csp",
|
|
6130
6133
|
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.",
|
|
6131
6134
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#csp-modifier",
|
|
@@ -6137,7 +6140,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
6137
6140
|
assignable:true,
|
|
6138
6141
|
negatable:false,
|
|
6139
6142
|
value_optional:true,
|
|
6140
|
-
value_format:"
|
|
6143
|
+
value_format:"csp_value" },
|
|
6141
6144
|
abp_ext_any:{ name:"csp",
|
|
6142
6145
|
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.",
|
|
6143
6146
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293-How-to-write-filters#content-security-policies",
|
|
@@ -6147,7 +6150,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
6147
6150
|
assignable:true,
|
|
6148
6151
|
negatable:false,
|
|
6149
6152
|
value_optional:true,
|
|
6150
|
-
value_format:"
|
|
6153
|
+
value_format:"csp_value" },
|
|
6151
6154
|
ubo_ext_any:{ name:"csp",
|
|
6152
6155
|
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.",
|
|
6153
6156
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#csp",
|
|
@@ -6159,7 +6162,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
6159
6162
|
assignable:true,
|
|
6160
6163
|
negatable:false,
|
|
6161
6164
|
value_optional:true,
|
|
6162
|
-
value_format:"
|
|
6165
|
+
value_format:"csp_value" } };
|
|
6163
6166
|
|
|
6164
6167
|
var data$M = { adg_os_any:{ name:"denyallow",
|
|
6165
6168
|
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.",
|
|
@@ -6666,7 +6669,8 @@ var data$l = { adg_os_any:{ name:"permissions",
|
|
|
6666
6669
|
inverse_conflicts:true,
|
|
6667
6670
|
assignable:true,
|
|
6668
6671
|
negatable:false,
|
|
6669
|
-
|
|
6672
|
+
value_optional:true,
|
|
6673
|
+
value_format:"permissions_value" } };
|
|
6670
6674
|
|
|
6671
6675
|
var data$k = { adg_any:{ name:"ping",
|
|
6672
6676
|
description:"The rule corresponds to requests caused by either navigator.sendBeacon() or the ping attribute on links.",
|
|
@@ -6794,14 +6798,14 @@ var data$g = { adg_os_any:{ name:"redirect",
|
|
|
6794
6798
|
assignable:true,
|
|
6795
6799
|
negatable:false,
|
|
6796
6800
|
value_optional:true,
|
|
6797
|
-
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 )?$" },
|
|
6801
|
+
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 )?$" },
|
|
6798
6802
|
adg_ext_any:{ name:"redirect",
|
|
6799
6803
|
description:"Used to redirect web requests to a local \"resource\".",
|
|
6800
6804
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#redirect-modifier",
|
|
6801
6805
|
assignable:true,
|
|
6802
6806
|
negatable:false,
|
|
6803
6807
|
value_optional:true,
|
|
6804
|
-
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 )?$" },
|
|
6808
|
+
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 )?$" },
|
|
6805
6809
|
ubo_ext_any:{ name:"redirect",
|
|
6806
6810
|
description:"Used to redirect web requests to a local \"resource\".",
|
|
6807
6811
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#redirect",
|
|
@@ -6910,7 +6914,7 @@ var data$d = { adg_os_any:{ name:"replace",
|
|
|
6910
6914
|
inverse_conflicts:true,
|
|
6911
6915
|
assignable:true,
|
|
6912
6916
|
negatable:false,
|
|
6913
|
-
value_format:"(?xi)\n ^\n \\/\n # the regexp to match by\n (.+)\n # separator\n \\/\n # replacement\n (
|
|
6917
|
+
value_format:"(?xi)\n ^\n \\/\n # the regexp to match by\n (.+)\n # separator\n \\/\n # replacement\n (.+)?\n \\/\n # flags\n ([gimuy]*)?\n $" },
|
|
6914
6918
|
adg_ext_firefox:{ name:"replace",
|
|
6915
6919
|
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.",
|
|
6916
6920
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#replace-modifier",
|
|
@@ -6928,7 +6932,7 @@ var data$d = { adg_os_any:{ name:"replace",
|
|
|
6928
6932
|
inverse_conflicts:true,
|
|
6929
6933
|
assignable:true,
|
|
6930
6934
|
negatable:false,
|
|
6931
|
-
value_format:"(?xi)\n ^\n \\/\n # the regexp to match by\n (.+)\n # separator\n \\/\n # replacement\n (
|
|
6935
|
+
value_format:"(?xi)\n ^\n \\/\n # the regexp to match by\n (.+)\n # separator\n \\/\n # replacement\n (.+)?\n \\/\n # flags\n ([gimuy]*)?\n $" } };
|
|
6932
6936
|
|
|
6933
6937
|
var data$c = { adg_any:{ name:"script",
|
|
6934
6938
|
description:"The rule corresponds to script requests, e.g. javascript, vbscript.",
|
|
@@ -7327,15 +7331,104 @@ const ALLOWED_STEALTH_OPTIONS = new Set([
|
|
|
7327
7331
|
'xclientdata',
|
|
7328
7332
|
'dpi',
|
|
7329
7333
|
]);
|
|
7334
|
+
/**
|
|
7335
|
+
* Allowed CSP directives for $csp modifier.
|
|
7336
|
+
*
|
|
7337
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy#directives}
|
|
7338
|
+
*/
|
|
7339
|
+
const ALLOWED_CSP_DIRECTIVES = new Set([
|
|
7340
|
+
'base-uri',
|
|
7341
|
+
'child-src',
|
|
7342
|
+
'connect-src',
|
|
7343
|
+
'default-src',
|
|
7344
|
+
'font-src',
|
|
7345
|
+
'form-action',
|
|
7346
|
+
'frame-ancestors',
|
|
7347
|
+
'frame-src',
|
|
7348
|
+
'img-src',
|
|
7349
|
+
'manifest-src',
|
|
7350
|
+
'media-src',
|
|
7351
|
+
'navigate-to',
|
|
7352
|
+
'object-src',
|
|
7353
|
+
'plugin-types',
|
|
7354
|
+
'prefetch-src',
|
|
7355
|
+
'report-to',
|
|
7356
|
+
'report-uri',
|
|
7357
|
+
'sandbox',
|
|
7358
|
+
'script-src',
|
|
7359
|
+
'style-src',
|
|
7360
|
+
'upgrade-insecure-requests',
|
|
7361
|
+
'worker-src',
|
|
7362
|
+
]);
|
|
7363
|
+
/**
|
|
7364
|
+
* Allowed stealth options for $permissions modifier.
|
|
7365
|
+
*
|
|
7366
|
+
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#permissions-modifier}
|
|
7367
|
+
*/
|
|
7368
|
+
const ALLOWED_PERMISSION_DIRECTIVES = new Set([
|
|
7369
|
+
'accelerometer',
|
|
7370
|
+
'ambient-light-sensor',
|
|
7371
|
+
'autoplay',
|
|
7372
|
+
'battery',
|
|
7373
|
+
'camera',
|
|
7374
|
+
'display-capture',
|
|
7375
|
+
'document-domain',
|
|
7376
|
+
'encrypted-media',
|
|
7377
|
+
'execution-while-not-rendered',
|
|
7378
|
+
'execution-while-out-of-viewport',
|
|
7379
|
+
'fullscreen',
|
|
7380
|
+
'gamepad',
|
|
7381
|
+
'geolocation',
|
|
7382
|
+
'gyroscope',
|
|
7383
|
+
'hid',
|
|
7384
|
+
'identity-credentials-get',
|
|
7385
|
+
'idle-detection',
|
|
7386
|
+
'local-fonts',
|
|
7387
|
+
'magnetometer',
|
|
7388
|
+
'microphone',
|
|
7389
|
+
'midi',
|
|
7390
|
+
'payment',
|
|
7391
|
+
'picture-in-picture',
|
|
7392
|
+
'publickey-credentials-create',
|
|
7393
|
+
'publickey-credentials-get',
|
|
7394
|
+
'screen-wake-lock',
|
|
7395
|
+
'serial',
|
|
7396
|
+
'speaker-selection',
|
|
7397
|
+
'storage-access',
|
|
7398
|
+
'usb',
|
|
7399
|
+
'web-share',
|
|
7400
|
+
'xr-spatial-tracking',
|
|
7401
|
+
]);
|
|
7402
|
+
/**
|
|
7403
|
+
* One of available tokens for $permission modifier value.
|
|
7404
|
+
*
|
|
7405
|
+
* @see {@link https://w3c.github.io/webappsec-permissions-policy/#structured-header-serialization}
|
|
7406
|
+
*/
|
|
7407
|
+
const PERMISSIONS_TOKEN_SELF = 'self';
|
|
7408
|
+
/**
|
|
7409
|
+
* One of allowlist values for $permissions modifier.
|
|
7410
|
+
*
|
|
7411
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Permissions_Policy#allowlists}
|
|
7412
|
+
*/
|
|
7413
|
+
const EMPTY_PERMISSIONS_ALLOWLIST = `${OPEN_PARENTHESIS}${CLOSE_PARENTHESIS}`;
|
|
7330
7414
|
/**
|
|
7331
7415
|
* Prefixes for error messages used in modifier validation.
|
|
7332
7416
|
*/
|
|
7333
7417
|
const VALIDATION_ERROR_PREFIX = {
|
|
7334
7418
|
BLOCK_ONLY: 'Only blocking rules may contain the modifier',
|
|
7335
7419
|
EXCEPTION_ONLY: 'Only exception rules may contain the modifier',
|
|
7420
|
+
INVALID_CSP_DIRECTIVES: 'Invalid CSP directives for the modifier',
|
|
7336
7421
|
INVALID_LIST_VALUES: 'Invalid values for the modifier',
|
|
7337
7422
|
INVALID_NOOP: 'Invalid noop modifier',
|
|
7423
|
+
INVALID_PERMISSION_DIRECTIVE: 'Invalid Permissions-Policy directive for the modifier',
|
|
7424
|
+
INVALID_PERMISSION_ORIGINS: 'Origins in the value is invalid for the modifier and the directive',
|
|
7425
|
+
INVALID_PERMISSION_ORIGIN_QUOTES: 'Double quotes should be used for origins in the value of the modifier',
|
|
7338
7426
|
MIXED_NEGATIONS: 'Simultaneous usage of negated and not negated values is forbidden for the modifier',
|
|
7427
|
+
NO_CSP_VALUE: 'No CSP value for the modifier and the directive',
|
|
7428
|
+
NO_CSP_DIRECTIVE_QUOTE: 'CSP directives should no be quoted for the modifier',
|
|
7429
|
+
NO_UNESCAPED_PERMISSION_COMMA: 'Unescaped comma in the value is not allowed for the modifier',
|
|
7430
|
+
// TODO: implement later for $scp and $permissions
|
|
7431
|
+
// NO_VALUE_ONLY_FOR_EXCEPTION: 'Modifier without value can be used only in exception rules',
|
|
7339
7432
|
NOT_EXISTENT: 'Non-existent modifier',
|
|
7340
7433
|
NOT_NEGATABLE_MODIFIER: 'Non-negatable modifier',
|
|
7341
7434
|
NOT_NEGATABLE_VALUE: 'Values cannot be negated for the modifier',
|
|
@@ -7647,10 +7740,12 @@ class QuoteUtils {
|
|
|
7647
7740
|
var CustomValueFormatValidatorName;
|
|
7648
7741
|
(function (CustomValueFormatValidatorName) {
|
|
7649
7742
|
CustomValueFormatValidatorName["App"] = "pipe_separated_apps";
|
|
7743
|
+
CustomValueFormatValidatorName["Csp"] = "csp_value";
|
|
7650
7744
|
// there are some differences between $domain and $denyallow
|
|
7651
7745
|
CustomValueFormatValidatorName["DenyAllow"] = "pipe_separated_denyallow_domains";
|
|
7652
7746
|
CustomValueFormatValidatorName["Domain"] = "pipe_separated_domains";
|
|
7653
7747
|
CustomValueFormatValidatorName["Method"] = "pipe_separated_methods";
|
|
7748
|
+
CustomValueFormatValidatorName["Permissions"] = "permissions_value";
|
|
7654
7749
|
CustomValueFormatValidatorName["StealthOption"] = "pipe_separated_stealth_options";
|
|
7655
7750
|
})(CustomValueFormatValidatorName || (CustomValueFormatValidatorName = {}));
|
|
7656
7751
|
/**
|
|
@@ -7711,6 +7806,32 @@ const isValidMethodModifierValue = (value) => {
|
|
|
7711
7806
|
const isValidStealthModifierValue = (value) => {
|
|
7712
7807
|
return ALLOWED_STEALTH_OPTIONS.has(value);
|
|
7713
7808
|
};
|
|
7809
|
+
/**
|
|
7810
|
+
* Checks whether the given `rawOrigin` is valid as Permissions Allowlist origin.
|
|
7811
|
+
*
|
|
7812
|
+
* @see {@link https://w3c.github.io/webappsec-permissions-policy/#allowlists}
|
|
7813
|
+
*
|
|
7814
|
+
* @param rawOrigin The raw origin.
|
|
7815
|
+
*
|
|
7816
|
+
* @returns True if the origin is valid, false otherwise.
|
|
7817
|
+
*/
|
|
7818
|
+
const isValidPermissionsOrigin = (rawOrigin) => {
|
|
7819
|
+
// origins should be quoted by double quote
|
|
7820
|
+
const actualQuoteType = QuoteUtils.getStringQuoteType(rawOrigin);
|
|
7821
|
+
if (actualQuoteType !== QuoteType.Double) {
|
|
7822
|
+
return false;
|
|
7823
|
+
}
|
|
7824
|
+
const origin = QuoteUtils.removeQuotes(rawOrigin);
|
|
7825
|
+
try {
|
|
7826
|
+
// validate the origin by URL constructor
|
|
7827
|
+
// https://w3c.github.io/webappsec-permissions-policy/#algo-parse-policy-directive
|
|
7828
|
+
new URL(origin);
|
|
7829
|
+
}
|
|
7830
|
+
catch (e) {
|
|
7831
|
+
return false;
|
|
7832
|
+
}
|
|
7833
|
+
return true;
|
|
7834
|
+
};
|
|
7714
7835
|
/**
|
|
7715
7836
|
* Checks whether the given `value` is valid domain as $denyallow modifier value.
|
|
7716
7837
|
* Important: wildcard tld are not supported, compared to $domain.
|
|
@@ -7901,14 +8022,196 @@ const validatePipeSeparatedMethods = (modifier) => {
|
|
|
7901
8022
|
const validatePipeSeparatedStealthOptions = (modifier) => {
|
|
7902
8023
|
return validateListItemsModifier(modifier, (raw) => StealthOptionListParser.parse(raw), isValidStealthModifierValue, customNoNegatedListItemsValidator);
|
|
7903
8024
|
};
|
|
8025
|
+
/**
|
|
8026
|
+
* Validates `csp_value` custom value format.
|
|
8027
|
+
* Used for $csp modifier.
|
|
8028
|
+
*
|
|
8029
|
+
* @param modifier Modifier AST node.
|
|
8030
|
+
*
|
|
8031
|
+
* @returns Validation result.
|
|
8032
|
+
*/
|
|
8033
|
+
const validateCspValue = (modifier) => {
|
|
8034
|
+
const modifierName = modifier.modifier.value;
|
|
8035
|
+
if (!modifier.value?.value) {
|
|
8036
|
+
return getValueRequiredValidationResult(modifierName);
|
|
8037
|
+
}
|
|
8038
|
+
// $csp modifier value may contain multiple directives
|
|
8039
|
+
// e.g. "csp=child-src 'none'; frame-src 'self' *; worker-src 'none'"
|
|
8040
|
+
const policyDirectives = modifier.value.value
|
|
8041
|
+
.split(SEMICOLON)
|
|
8042
|
+
// rule with $csp modifier may end with semicolon
|
|
8043
|
+
// e.g. "$csp=sandbox allow-same-origin;"
|
|
8044
|
+
// TODO: add predicate helper for `(i) => !!i`
|
|
8045
|
+
.filter((i) => !!i);
|
|
8046
|
+
const invalidValueValidationResult = getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}': "${modifier.value.value}"`);
|
|
8047
|
+
if (policyDirectives.length === 0) {
|
|
8048
|
+
return invalidValueValidationResult;
|
|
8049
|
+
}
|
|
8050
|
+
const invalidDirectives = [];
|
|
8051
|
+
for (let i = 0; i < policyDirectives.length; i += 1) {
|
|
8052
|
+
const policyDirective = policyDirectives[i].trim();
|
|
8053
|
+
if (!policyDirective) {
|
|
8054
|
+
return invalidValueValidationResult;
|
|
8055
|
+
}
|
|
8056
|
+
const chunks = policyDirective.split(SPACE);
|
|
8057
|
+
const [directive, ...valueChunks] = chunks;
|
|
8058
|
+
// e.g. "csp=child-src 'none'; ; worker-src 'none'"
|
|
8059
|
+
// validator it here ↑
|
|
8060
|
+
if (!directive) {
|
|
8061
|
+
return invalidValueValidationResult;
|
|
8062
|
+
}
|
|
8063
|
+
if (!ALLOWED_CSP_DIRECTIVES.has(directive)) {
|
|
8064
|
+
// e.g. "csp='child-src' 'none'"
|
|
8065
|
+
if (ALLOWED_CSP_DIRECTIVES.has(QuoteUtils.removeQuotes(directive))) {
|
|
8066
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NO_CSP_DIRECTIVE_QUOTE}: '${modifierName}': ${directive}`);
|
|
8067
|
+
}
|
|
8068
|
+
invalidDirectives.push(directive);
|
|
8069
|
+
continue;
|
|
8070
|
+
}
|
|
8071
|
+
if (valueChunks.length === 0) {
|
|
8072
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NO_CSP_VALUE}: '${modifierName}': '${directive}'`);
|
|
8073
|
+
}
|
|
8074
|
+
}
|
|
8075
|
+
if (invalidDirectives.length > 0) {
|
|
8076
|
+
const directivesToStr = QuoteUtils.quoteAndJoinStrings(invalidDirectives, QuoteType.Double);
|
|
8077
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.INVALID_CSP_DIRECTIVES}: '${modifierName}': ${directivesToStr}`);
|
|
8078
|
+
}
|
|
8079
|
+
return { valid: true };
|
|
8080
|
+
};
|
|
8081
|
+
/**
|
|
8082
|
+
* Validates permission allowlist origins in the value of $permissions modifier.
|
|
8083
|
+
*
|
|
8084
|
+
* @see {@link https://w3c.github.io/webappsec-permissions-policy/#allowlists}
|
|
8085
|
+
*
|
|
8086
|
+
* @param allowlistChunks Array of allowlist chunks.
|
|
8087
|
+
* @param directive Permission directive name.
|
|
8088
|
+
* @param modifierName Modifier name.
|
|
8089
|
+
*
|
|
8090
|
+
* @returns Validation result.
|
|
8091
|
+
*/
|
|
8092
|
+
const validatePermissionAllowlistOrigins = (allowlistChunks, directive, modifierName) => {
|
|
8093
|
+
const invalidOrigins = [];
|
|
8094
|
+
for (let i = 0; i < allowlistChunks.length; i += 1) {
|
|
8095
|
+
const chunk = allowlistChunks[i].trim();
|
|
8096
|
+
// skip few spaces between origins (they were splitted by space)
|
|
8097
|
+
// e.g. 'geolocation=("https://example.com" "https://*.example.com")'
|
|
8098
|
+
if (chunk.length === 0) {
|
|
8099
|
+
continue;
|
|
8100
|
+
}
|
|
8101
|
+
/**
|
|
8102
|
+
* 'self' should be checked case-insensitively
|
|
8103
|
+
*
|
|
8104
|
+
* @see {@link https://w3c.github.io/webappsec-permissions-policy/#algo-parse-policy-directive}
|
|
8105
|
+
*
|
|
8106
|
+
* @example 'geolocation=(self)'
|
|
8107
|
+
*/
|
|
8108
|
+
if (chunk.toLowerCase() === PERMISSIONS_TOKEN_SELF) {
|
|
8109
|
+
continue;
|
|
8110
|
+
}
|
|
8111
|
+
if (QuoteUtils.getStringQuoteType(chunk) !== QuoteType.Double) {
|
|
8112
|
+
return getInvalidValidationResult(
|
|
8113
|
+
// eslint-disable-next-line max-len
|
|
8114
|
+
`${VALIDATION_ERROR_PREFIX.INVALID_PERMISSION_ORIGIN_QUOTES}: '${modifierName}': '${directive}': '${QuoteUtils.removeQuotes(chunk)}'`);
|
|
8115
|
+
}
|
|
8116
|
+
if (!isValidPermissionsOrigin(chunk)) {
|
|
8117
|
+
invalidOrigins.push(chunk);
|
|
8118
|
+
}
|
|
8119
|
+
}
|
|
8120
|
+
if (invalidOrigins.length > 0) {
|
|
8121
|
+
const originsToStr = QuoteUtils.quoteAndJoinStrings(invalidOrigins);
|
|
8122
|
+
return getInvalidValidationResult(
|
|
8123
|
+
// eslint-disable-next-line max-len
|
|
8124
|
+
`${VALIDATION_ERROR_PREFIX.INVALID_PERMISSION_ORIGINS}: '${modifierName}': '${directive}': ${originsToStr}`);
|
|
8125
|
+
}
|
|
8126
|
+
return { valid: true };
|
|
8127
|
+
};
|
|
8128
|
+
/**
|
|
8129
|
+
* Validates permission allowlist in the modifier value.
|
|
8130
|
+
*
|
|
8131
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Permissions_Policy#allowlists}
|
|
8132
|
+
* @see {@link https://w3c.github.io/webappsec-permissions-policy/#allowlists}
|
|
8133
|
+
*
|
|
8134
|
+
* @param allowlist Allowlist value.
|
|
8135
|
+
* @param directive Permission directive name.
|
|
8136
|
+
* @param modifierName Modifier name.
|
|
8137
|
+
*
|
|
8138
|
+
* @returns Validation result.
|
|
8139
|
+
*/
|
|
8140
|
+
const validatePermissionAllowlist = (allowlist, directive, modifierName) => {
|
|
8141
|
+
// `*` is one of available permissions tokens
|
|
8142
|
+
// e.g. 'fullscreen=*'
|
|
8143
|
+
// https://w3c.github.io/webappsec-permissions-policy/#structured-header-serialization
|
|
8144
|
+
if (allowlist === WILDCARD$1
|
|
8145
|
+
// e.g. 'autoplay=()'
|
|
8146
|
+
|| allowlist === EMPTY_PERMISSIONS_ALLOWLIST) {
|
|
8147
|
+
return { valid: true };
|
|
8148
|
+
}
|
|
8149
|
+
if (!(allowlist.startsWith(OPEN_PARENTHESIS) && allowlist.endsWith(CLOSE_PARENTHESIS))) {
|
|
8150
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}'`);
|
|
8151
|
+
}
|
|
8152
|
+
const allowlistChunks = allowlist.slice(1, -1).split(SPACE);
|
|
8153
|
+
return validatePermissionAllowlistOrigins(allowlistChunks, directive, modifierName);
|
|
8154
|
+
};
|
|
8155
|
+
/**
|
|
8156
|
+
* Validates single permission in the modifier value.
|
|
8157
|
+
*
|
|
8158
|
+
* @param permission Single permission value.
|
|
8159
|
+
* @param modifierName Modifier name.
|
|
8160
|
+
* @param modifierValue Modifier value.
|
|
8161
|
+
*
|
|
8162
|
+
* @returns Validation result.
|
|
8163
|
+
*/
|
|
8164
|
+
const validateSinglePermission = (permission, modifierName, modifierValue) => {
|
|
8165
|
+
// empty permission in the rule
|
|
8166
|
+
// e.g. 'permissions=storage-access=()\\, \\, camera=()'
|
|
8167
|
+
// the validator is here ↑
|
|
8168
|
+
if (!permission) {
|
|
8169
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}'`);
|
|
8170
|
+
}
|
|
8171
|
+
if (permission.includes(COMMA)) {
|
|
8172
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NO_UNESCAPED_PERMISSION_COMMA}: '${modifierName}': '${modifierValue}'`);
|
|
8173
|
+
}
|
|
8174
|
+
const [directive, allowlist] = permission.split(EQUALS);
|
|
8175
|
+
if (!ALLOWED_PERMISSION_DIRECTIVES.has(directive)) {
|
|
8176
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.INVALID_PERMISSION_DIRECTIVE}: '${modifierName}': '${directive}'`);
|
|
8177
|
+
}
|
|
8178
|
+
return validatePermissionAllowlist(allowlist, directive, modifierName);
|
|
8179
|
+
};
|
|
8180
|
+
/**
|
|
8181
|
+
* Validates `permissions_value` custom value format.
|
|
8182
|
+
* Used for $permissions modifier.
|
|
8183
|
+
*
|
|
8184
|
+
* @param modifier Modifier AST node.
|
|
8185
|
+
*
|
|
8186
|
+
* @returns Validation result.
|
|
8187
|
+
*/
|
|
8188
|
+
const validatePermissions = (modifier) => {
|
|
8189
|
+
if (!modifier.value?.value) {
|
|
8190
|
+
return getValueRequiredValidationResult(modifier.modifier.value);
|
|
8191
|
+
}
|
|
8192
|
+
const modifierName = modifier.modifier.value;
|
|
8193
|
+
const modifierValue = modifier.value.value;
|
|
8194
|
+
// multiple permissions may be separated by escaped commas
|
|
8195
|
+
const permissions = modifier.value.value.split(`${BACKSLASH}${COMMA}`);
|
|
8196
|
+
for (let i = 0; i < permissions.length; i += 1) {
|
|
8197
|
+
const permission = permissions[i].trim();
|
|
8198
|
+
const singlePermissionValidationResult = validateSinglePermission(permission, modifierName, modifierValue);
|
|
8199
|
+
if (!singlePermissionValidationResult.valid) {
|
|
8200
|
+
return singlePermissionValidationResult;
|
|
8201
|
+
}
|
|
8202
|
+
}
|
|
8203
|
+
return { valid: true };
|
|
8204
|
+
};
|
|
7904
8205
|
/**
|
|
7905
8206
|
* Map of all available pre-defined validators for modifiers with custom `value_format`.
|
|
7906
8207
|
*/
|
|
7907
8208
|
const CUSTOM_VALUE_FORMAT_MAP = {
|
|
7908
8209
|
[CustomValueFormatValidatorName.App]: validatePipeSeparatedApps,
|
|
8210
|
+
[CustomValueFormatValidatorName.Csp]: validateCspValue,
|
|
7909
8211
|
[CustomValueFormatValidatorName.DenyAllow]: validatePipeSeparatedDenyAllowDomains,
|
|
7910
8212
|
[CustomValueFormatValidatorName.Domain]: validatePipeSeparatedDomains,
|
|
7911
8213
|
[CustomValueFormatValidatorName.Method]: validatePipeSeparatedMethods,
|
|
8214
|
+
[CustomValueFormatValidatorName.Permissions]: validatePermissions,
|
|
7912
8215
|
[CustomValueFormatValidatorName.StealthOption]: validatePipeSeparatedStealthOptions,
|
|
7913
8216
|
};
|
|
7914
8217
|
/**
|
|
@@ -7935,9 +8238,8 @@ const validateValue = (modifier, valueFormat) => {
|
|
|
7935
8238
|
return validator(modifier);
|
|
7936
8239
|
}
|
|
7937
8240
|
const modifierName = modifier.modifier.value;
|
|
7938
|
-
const defaultInvalidValueResult = getValueRequiredValidationResult(modifierName);
|
|
7939
8241
|
if (!modifier.value?.value) {
|
|
7940
|
-
return
|
|
8242
|
+
return getValueRequiredValidationResult(modifierName);
|
|
7941
8243
|
}
|
|
7942
8244
|
let xRegExp;
|
|
7943
8245
|
try {
|
|
@@ -7948,7 +8250,7 @@ const validateValue = (modifier, valueFormat) => {
|
|
|
7948
8250
|
}
|
|
7949
8251
|
const isValid = xRegExp.test(modifier.value?.value);
|
|
7950
8252
|
if (!isValid) {
|
|
7951
|
-
return
|
|
8253
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}'`);
|
|
7952
8254
|
}
|
|
7953
8255
|
return { valid: true };
|
|
7954
8256
|
};
|
|
@@ -8012,6 +8314,10 @@ const validateForSpecificSyntax = (modifiersData, syntax, modifier, isException)
|
|
|
8012
8314
|
// e.g. 'domain'
|
|
8013
8315
|
if (specificBlockerData[SpecificKey.Assignable]) {
|
|
8014
8316
|
if (!modifier.value) {
|
|
8317
|
+
// TODO: ditch value_optional after custom validators are implemented for value_format for all modifiers.
|
|
8318
|
+
// This checking should be done in each separate custom validator,
|
|
8319
|
+
// because $csp and $permissions without value can be used only in extension rules,
|
|
8320
|
+
// but $cookie with no value can be used in both blocking and exception rules.
|
|
8015
8321
|
/**
|
|
8016
8322
|
* Some assignable modifiers can be used without a value,
|
|
8017
8323
|
* e.g. '@@||example.com^$cookie'.
|
|
@@ -9261,6 +9567,11 @@ class CosmeticRuleConverter extends RuleConverterBase {
|
|
|
9261
9567
|
/**
|
|
9262
9568
|
* @file Network rule modifier list converter.
|
|
9263
9569
|
*/
|
|
9570
|
+
// Since scriptlets library doesn't have ESM exports, we should import
|
|
9571
|
+
// the whole module and then extract the required functions from it here.
|
|
9572
|
+
// Otherwise importing AGTree will cause an error in ESM environment,
|
|
9573
|
+
// because scriptlets library doesn't support named exports.
|
|
9574
|
+
const { redirects } = scriptlets;
|
|
9264
9575
|
/**
|
|
9265
9576
|
* @see {@link https://adguard.com/kb/general/ad-filtering/create-own-filters/#csp-modifier}
|
|
9266
9577
|
*/
|
|
@@ -9597,7 +9908,7 @@ class LogicalExpressionUtils {
|
|
|
9597
9908
|
}
|
|
9598
9909
|
}
|
|
9599
9910
|
|
|
9600
|
-
const version$1 = "1.1.
|
|
9911
|
+
const version$1 = "1.1.5";
|
|
9601
9912
|
|
|
9602
9913
|
/**
|
|
9603
9914
|
* @file AGTree version
|