@adguard/agtree 1.1.4 → 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 CHANGED
@@ -4,6 +4,18 @@ 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
+
7
19
  ## 1.1.4 - 2023-08-30
8
20
 
9
21
  ### Fixed
package/dist/agtree.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * AGTree v1.1.4 (build date: Wed, 30 Aug 2023 10:02:46 GMT)
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:"(?xi)\n ^(\n base-uri|\n child-src|\n connect-src|\n default-src|\n font-src|\n form-action|\n frame-ancestors|\n frame-src|\n img-src|\n manifest-src|\n media-src|\n navigate-to|\n object-src|\n plugin-types|\n prefetch-src|\n report-to|\n report-uri|\n sandbox|\n script-src|\n style-src|\n upgrade-insecure-requests|\n worker-src|\n )\n \\s+\n \\S{1,}" },
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:"(?xi)\n ^(\n base-uri|\n child-src|\n connect-src|\n default-src|\n font-src|\n form-action|\n frame-ancestors|\n frame-src|\n img-src|\n manifest-src|\n media-src|\n navigate-to|\n object-src|\n plugin-types|\n prefetch-src|\n report-to|\n report-uri|\n sandbox|\n script-src|\n style-src|\n upgrade-insecure-requests|\n worker-src|\n )\n \\s+\n \\S{1,}" },
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:"(?xi)\n ^(\n base-uri|\n child-src|\n connect-src|\n default-src|\n font-src|\n form-action|\n frame-ancestors|\n frame-src|\n img-src|\n manifest-src|\n media-src|\n navigate-to|\n object-src|\n plugin-types|\n prefetch-src|\n report-to|\n report-uri|\n sandbox|\n script-src|\n style-src|\n upgrade-insecure-requests|\n worker-src|\n )\n \\s+\n \\S{1,}" },
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:"(?xi)\n ^(\n base-uri|\n child-src|\n connect-src|\n default-src|\n font-src|\n form-action|\n frame-ancestors|\n frame-src|\n img-src|\n manifest-src|\n media-src|\n navigate-to|\n object-src|\n plugin-types|\n prefetch-src|\n report-to|\n report-uri|\n sandbox|\n script-src|\n style-src|\n upgrade-insecure-requests|\n worker-src|\n )\n \\s+\n \\S{1,}" } };
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
- value_format:"(?x)\n ^\n (\n ?:(\n accelerometer|\n ambient-light-sensor|\n autoplay|\n battery|\n camera|\n display-capture|\n document-domain|\n encrypted-media|\n execution-while-not-rendered|\n execution-while-out-of-viewport|\n fullscreen|\n gamepad|\n geolocation|\n gyroscope|\n hid|\n identity-credentials-get|\n idle-detection|\n local-fonts|\n magnetometer|\n microphone|\n midi|\n payment|\n picture-in-picture|\n publickey-credentials-create|\n publickey-credentials-get|\n screen-wake-lock|\n serial|\n speaker-selection|\n storage-access|\n usb|\n web-share|\n xr-spatial-tracking\n )\n =\\(\\)\n # optional escaped comma for multiple permissions\n (\\\\,(\\s+)?)?\n )+\n $" } };
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.",
@@ -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
  /**
@@ -8030,6 +8333,10 @@ const validateForSpecificSyntax = (modifiersData, syntax, modifier, isException)
8030
8333
  // e.g. 'domain'
8031
8334
  if (specificBlockerData[SpecificKey.Assignable]) {
8032
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.
8033
8340
  /**
8034
8341
  * Some assignable modifiers can be used without a value,
8035
8342
  * e.g. '@@||example.com^$cookie'.
@@ -9279,6 +9586,11 @@ class CosmeticRuleConverter extends RuleConverterBase {
9279
9586
  /**
9280
9587
  * @file Network rule modifier list converter.
9281
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;
9282
9594
  /**
9283
9595
  * @see {@link https://adguard.com/kb/general/ad-filtering/create-own-filters/#csp-modifier}
9284
9596
  */
@@ -9394,7 +9706,7 @@ class NetworkRuleModifierListConverter extends ConverterBase {
9394
9706
  : modifierNode.modifier.value;
9395
9707
  // Try to convert the redirect resource name to ADG format
9396
9708
  // This function returns undefined if the resource name is unknown
9397
- const convertedRedirectResource = scriptlets.redirects.convertRedirectNameToAdg(redirectResource);
9709
+ const convertedRedirectResource = redirects.convertRedirectNameToAdg(redirectResource);
9398
9710
  convertedModifierList.children.push(createModifierNode(modifierName,
9399
9711
  // If the redirect resource name is unknown, fall back to the original one
9400
9712
  // Later, the validator will throw an error if the resource name is invalid
@@ -9615,7 +9927,7 @@ class LogicalExpressionUtils {
9615
9927
  }
9616
9928
  }
9617
9929
 
9618
- const version$1 = "1.1.4";
9930
+ const version$1 = "1.1.5";
9619
9931
 
9620
9932
  /**
9621
9933
  * @file AGTree version
package/dist/agtree.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * AGTree v1.1.4 (build date: Wed, 30 Aug 2023 10:02:46 GMT)
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