@adguard/agtree 2.1.4 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agtree.d.ts +2 -1
- package/dist/agtree.js +133 -21
- package/dist/agtree.mjs +133 -21
- package/dist/compatibility-table-data.js +296 -148
- package/package.json +3 -3
package/dist/agtree.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v2.
|
|
2
|
+
* AGTree v2.3.0 (build date: Thu, 19 Dec 2024 15:31:22 GMT)
|
|
3
3
|
* (c) 2024 Adguard Software Ltd.
|
|
4
4
|
* Released under the MIT license
|
|
5
5
|
* https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
|
|
@@ -3699,6 +3699,7 @@ declare const modifierDataSchema: zod.ZodEffects<zod.ZodTypeAny, {
|
|
|
3699
3699
|
exceptionOnly: boolean;
|
|
3700
3700
|
valueOptional: boolean;
|
|
3701
3701
|
valueFormat: string | null;
|
|
3702
|
+
valueFormatFlags: string | null;
|
|
3702
3703
|
}, any>;
|
|
3703
3704
|
/**
|
|
3704
3705
|
* Type of the modifier data schema.
|
package/dist/agtree.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v2.
|
|
2
|
+
* AGTree v2.3.0 (build date: Thu, 19 Dec 2024 15:31:22 GMT)
|
|
3
3
|
* (c) 2024 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,6 @@ var sprintfJs = require('sprintf-js');
|
|
|
13
13
|
var cssTokenizer = require('@adguard/css-tokenizer');
|
|
14
14
|
var tldts = require('tldts');
|
|
15
15
|
var isIp = require('is-ip');
|
|
16
|
-
var XRegExp = require('xregexp');
|
|
17
16
|
var cloneDeep = require('clone-deep');
|
|
18
17
|
var compatibilityTableData_js = require('./compatibility-table-data.js');
|
|
19
18
|
var zod = require('zod');
|
|
@@ -312,6 +311,8 @@ const COMMA_DOMAIN_LIST_SEPARATOR = ',';
|
|
|
312
311
|
const PIPE_MODIFIER_SEPARATOR = '|';
|
|
313
312
|
const CSS_MEDIA_MARKER = '@media';
|
|
314
313
|
const CSS_NOT_PSEUDO = 'not';
|
|
314
|
+
const CSS_BLOCK_OPEN = '{';
|
|
315
|
+
const CSS_BLOCK_CLOSE = '}';
|
|
315
316
|
const HINT_MARKER = '!+';
|
|
316
317
|
const HINT_MARKER_LEN = HINT_MARKER.length;
|
|
317
318
|
const NETWORK_RULE_EXCEPTION_MARKER = '@@';
|
|
@@ -6086,14 +6087,24 @@ class CssTokenStream {
|
|
|
6086
6087
|
// - end: end index of the token
|
|
6087
6088
|
// - props: additional properties of the token, if any (we don't use it here, this is why we use underscore)
|
|
6088
6089
|
// - balance: balance level of the token
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6090
|
+
try {
|
|
6091
|
+
tokenizeBalanced(source, (type, start, end, _, balance) => {
|
|
6092
|
+
this.tokens.push({
|
|
6093
|
+
type,
|
|
6094
|
+
start,
|
|
6095
|
+
end,
|
|
6096
|
+
balance,
|
|
6097
|
+
});
|
|
6095
6098
|
});
|
|
6096
|
-
}
|
|
6099
|
+
}
|
|
6100
|
+
catch (error) {
|
|
6101
|
+
// If the error is an AdblockSyntaxError, adjust the error positions to the base offset
|
|
6102
|
+
if (error instanceof AdblockSyntaxError) {
|
|
6103
|
+
error.start += baseOffset;
|
|
6104
|
+
error.end += baseOffset;
|
|
6105
|
+
throw error;
|
|
6106
|
+
}
|
|
6107
|
+
}
|
|
6097
6108
|
this.index = 0;
|
|
6098
6109
|
this.baseOffset = baseOffset;
|
|
6099
6110
|
}
|
|
@@ -7653,6 +7664,40 @@ class AdgScriptletInjectionBodyParser extends ParserBase {
|
|
|
7653
7664
|
}
|
|
7654
7665
|
}
|
|
7655
7666
|
|
|
7667
|
+
/**
|
|
7668
|
+
* Represents an error that occurs when an operation is aborted.
|
|
7669
|
+
*/
|
|
7670
|
+
class AbortError extends Error {
|
|
7671
|
+
constructor() {
|
|
7672
|
+
super('Aborted');
|
|
7673
|
+
}
|
|
7674
|
+
}
|
|
7675
|
+
// TODO: AG-38480 add a stop function to the tokenizers callback and move `hasToken` to CSS Tokenizer as well
|
|
7676
|
+
/**
|
|
7677
|
+
* Checks if the given raw string contains any of the specified tokens.
|
|
7678
|
+
* This function uses error throwing inside the abort tokenization process.
|
|
7679
|
+
*
|
|
7680
|
+
* @param raw - The raw string to be tokenized and checked.
|
|
7681
|
+
* @param tokens - A set of token types to check for in the raw string.
|
|
7682
|
+
* @returns `true` if any of the specified tokens are found in the raw string, otherwise `false`.
|
|
7683
|
+
*/
|
|
7684
|
+
const hasToken = (raw, tokens) => {
|
|
7685
|
+
try {
|
|
7686
|
+
cssTokenizer.tokenizeExtended(raw, (type) => {
|
|
7687
|
+
if (tokens.has(type)) {
|
|
7688
|
+
throw new AbortError();
|
|
7689
|
+
}
|
|
7690
|
+
});
|
|
7691
|
+
}
|
|
7692
|
+
catch (e) {
|
|
7693
|
+
if (e instanceof AbortError) {
|
|
7694
|
+
return true;
|
|
7695
|
+
}
|
|
7696
|
+
throw e;
|
|
7697
|
+
}
|
|
7698
|
+
return false;
|
|
7699
|
+
};
|
|
7700
|
+
|
|
7656
7701
|
/* eslint-disable no-param-reassign */
|
|
7657
7702
|
/**
|
|
7658
7703
|
* Value map for binary serialization. This helps to reduce the size of the serialized data,
|
|
@@ -8025,6 +8070,33 @@ class CosmeticRuleParser extends ParserBase {
|
|
|
8025
8070
|
body: AdgCssInjectionParser.parse(rawBody, options, baseOffset + bodyStart),
|
|
8026
8071
|
};
|
|
8027
8072
|
};
|
|
8073
|
+
/**
|
|
8074
|
+
* Parses Adb CSS injection rules
|
|
8075
|
+
* eg: example.com##.foo { display: none; }
|
|
8076
|
+
*
|
|
8077
|
+
* @returns parsed rule
|
|
8078
|
+
*/
|
|
8079
|
+
const parseAbpCssInjection = () => {
|
|
8080
|
+
if (!options.parseAbpSpecificRules) {
|
|
8081
|
+
return null;
|
|
8082
|
+
}
|
|
8083
|
+
// check if the rule contains both CSS block open and close characters
|
|
8084
|
+
// if none of them is present we can stop parsing
|
|
8085
|
+
if (rawBody.indexOf(CSS_BLOCK_OPEN) === -1 && rawBody.indexOf(CSS_BLOCK_CLOSE) === -1) {
|
|
8086
|
+
return null;
|
|
8087
|
+
}
|
|
8088
|
+
if (!hasToken(rawBody, new Set([cssTokenizer.TokenType.OpenCurlyBracket, cssTokenizer.TokenType.CloseCurlyBracket]))) {
|
|
8089
|
+
return null;
|
|
8090
|
+
}
|
|
8091
|
+
// try to parse the raw body as an AdGuard CSS injection rule
|
|
8092
|
+
const body = AdgCssInjectionParser.parse(rawBody, options, baseOffset + bodyStart);
|
|
8093
|
+
// if the parsed rule type is a 'CssInjectionRuleBody', return the parsed rule
|
|
8094
|
+
return {
|
|
8095
|
+
syntax: exports.AdblockSyntax.Abp,
|
|
8096
|
+
type: exports.CosmeticRuleType.CssInjectionRule,
|
|
8097
|
+
body,
|
|
8098
|
+
};
|
|
8099
|
+
};
|
|
8028
8100
|
const parseAbpSnippetInjection = () => {
|
|
8029
8101
|
if (!options.parseAbpSpecificRules) {
|
|
8030
8102
|
throw new AdblockSyntaxError(sprintfJs.sprintf(ERROR_MESSAGES$3.SYNTAX_DISABLED, exports.AdblockSyntax.Abp), baseOffset + bodyStart, baseOffset + bodyEnd);
|
|
@@ -8136,10 +8208,22 @@ class CosmeticRuleParser extends ParserBase {
|
|
|
8136
8208
|
// the next function is called, and so on.
|
|
8137
8209
|
// If all functions return null, an error should be thrown.
|
|
8138
8210
|
const separatorMap = {
|
|
8139
|
-
'##': [
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8211
|
+
'##': [
|
|
8212
|
+
parseUboHtmlFiltering,
|
|
8213
|
+
parseUboScriptletInjection,
|
|
8214
|
+
parseUboCssInjection,
|
|
8215
|
+
parseAbpCssInjection,
|
|
8216
|
+
parseElementHiding,
|
|
8217
|
+
],
|
|
8218
|
+
'#@#': [
|
|
8219
|
+
parseUboHtmlFiltering,
|
|
8220
|
+
parseUboScriptletInjection,
|
|
8221
|
+
parseUboCssInjection,
|
|
8222
|
+
parseAbpCssInjection,
|
|
8223
|
+
parseElementHiding,
|
|
8224
|
+
],
|
|
8225
|
+
'#?#': [parseUboCssInjection, parseAbpCssInjection, parseElementHiding],
|
|
8226
|
+
'#@?#': [parseUboCssInjection, parseAbpCssInjection, parseElementHiding],
|
|
8143
8227
|
'#$#': [parseAdgCssInjection, parseAbpSnippetInjection],
|
|
8144
8228
|
'#@$#': [parseAdgCssInjection, parseAbpSnippetInjection],
|
|
8145
8229
|
'#$?#': [parseAdgCssInjection],
|
|
@@ -8208,7 +8292,7 @@ class CosmeticRuleParser extends ParserBase {
|
|
|
8208
8292
|
result = node.body.selectorList.value;
|
|
8209
8293
|
break;
|
|
8210
8294
|
case exports.CosmeticRuleType.CssInjectionRule:
|
|
8211
|
-
if (node.syntax === exports.AdblockSyntax.Adg) {
|
|
8295
|
+
if (node.syntax === exports.AdblockSyntax.Adg || node.syntax === exports.AdblockSyntax.Abp) {
|
|
8212
8296
|
result = AdgCssInjectionParser.generate(node.body);
|
|
8213
8297
|
}
|
|
8214
8298
|
else if (node.syntax === exports.AdblockSyntax.Ubo) {
|
|
@@ -10764,10 +10848,11 @@ const isCustomValueFormatValidator = (valueFormat) => {
|
|
|
10764
10848
|
*
|
|
10765
10849
|
* @param modifier Modifier AST node.
|
|
10766
10850
|
* @param valueFormat Value format for the modifier.
|
|
10851
|
+
* @param valueFormatFlags Optional; RegExp flags for the value format.
|
|
10767
10852
|
*
|
|
10768
10853
|
* @returns Validation result.
|
|
10769
10854
|
*/
|
|
10770
|
-
const validateValue = (modifier, valueFormat) => {
|
|
10855
|
+
const validateValue = (modifier, valueFormat, valueFormatFlags) => {
|
|
10771
10856
|
if (isCustomValueFormatValidator(valueFormat)) {
|
|
10772
10857
|
const validator = CUSTOM_VALUE_FORMAT_MAP[valueFormat];
|
|
10773
10858
|
return validator(modifier);
|
|
@@ -10776,14 +10861,19 @@ const validateValue = (modifier, valueFormat) => {
|
|
|
10776
10861
|
if (!modifier.value?.value) {
|
|
10777
10862
|
return getValueRequiredValidationResult(modifierName);
|
|
10778
10863
|
}
|
|
10779
|
-
let
|
|
10864
|
+
let regExp;
|
|
10780
10865
|
try {
|
|
10781
|
-
|
|
10866
|
+
if (isString(valueFormatFlags)) {
|
|
10867
|
+
regExp = new RegExp(valueFormat, valueFormatFlags);
|
|
10868
|
+
}
|
|
10869
|
+
else {
|
|
10870
|
+
regExp = new RegExp(valueFormat);
|
|
10871
|
+
}
|
|
10782
10872
|
}
|
|
10783
10873
|
catch (e) {
|
|
10784
10874
|
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.INVALID_VALUE_FORMAT_REGEXP}: '${modifierName}'`);
|
|
10785
10875
|
}
|
|
10786
|
-
const isValid =
|
|
10876
|
+
const isValid = regExp.test(modifier.value?.value);
|
|
10787
10877
|
if (!isValid) {
|
|
10788
10878
|
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}'`);
|
|
10789
10879
|
}
|
|
@@ -11367,7 +11457,7 @@ const validateForSpecificSyntax = (syntax, modifier, isException) => {
|
|
|
11367
11457
|
if (!specificBlockerData.valueFormat) {
|
|
11368
11458
|
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.NO_VALUE_FORMAT_FOR_ASSIGNABLE}: '${modifierName}'`);
|
|
11369
11459
|
}
|
|
11370
|
-
return validateValue(modifier, specificBlockerData.valueFormat);
|
|
11460
|
+
return validateValue(modifier, specificBlockerData.valueFormat, specificBlockerData.valueFormatFlags);
|
|
11371
11461
|
}
|
|
11372
11462
|
if (modifier?.value) {
|
|
11373
11463
|
// e.g. 'third-party=true'
|
|
@@ -13064,8 +13154,14 @@ function getErrorMessage(error) {
|
|
|
13064
13154
|
* Known validators that don't need to be validated as regex.
|
|
13065
13155
|
*/
|
|
13066
13156
|
const KNOWN_VALIDATORS = new Set([
|
|
13157
|
+
'csp_value',
|
|
13067
13158
|
'domain',
|
|
13159
|
+
'permissions_value',
|
|
13160
|
+
'pipe_separated_apps',
|
|
13161
|
+
'pipe_separated_denyallow_domains',
|
|
13068
13162
|
'pipe_separated_domains',
|
|
13163
|
+
'pipe_separated_methods',
|
|
13164
|
+
'pipe_separated_stealth_options',
|
|
13069
13165
|
'regexp',
|
|
13070
13166
|
'url',
|
|
13071
13167
|
]);
|
|
@@ -13116,6 +13212,10 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
|
13116
13212
|
* Its value can be a regex pattern or a known validator name (e.g. `domain`, `pipe_separated_domains`, etc.).
|
|
13117
13213
|
*/
|
|
13118
13214
|
value_format: nonEmptyStringSchema.nullable().default(null),
|
|
13215
|
+
/**
|
|
13216
|
+
* Describes the flags for the `value_format` regex pattern.
|
|
13217
|
+
*/
|
|
13218
|
+
value_format_flags: nonEmptyStringSchema.nullable().default(null),
|
|
13119
13219
|
}).superRefine((data, ctx) => {
|
|
13120
13220
|
// TODO: find something better, for now we can't add refine logic to the base schema:
|
|
13121
13221
|
// https://github.com/colinhacks/zod/issues/454#issuecomment-848370721
|
|
@@ -13136,11 +13236,17 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
|
13136
13236
|
const valueFormat = data.value_format.trim();
|
|
13137
13237
|
// if it is a known validator, we don't need to validate it further
|
|
13138
13238
|
if (KNOWN_VALIDATORS.has(valueFormat)) {
|
|
13239
|
+
if (data.value_format_flags) {
|
|
13240
|
+
ctx.addIssue({
|
|
13241
|
+
code: zod.ZodIssueCode.custom,
|
|
13242
|
+
message: 'value_format_flags are not allowed for known validators',
|
|
13243
|
+
});
|
|
13244
|
+
}
|
|
13139
13245
|
return;
|
|
13140
13246
|
}
|
|
13141
13247
|
// otherwise, we need to validate it as a regex
|
|
13142
13248
|
try {
|
|
13143
|
-
|
|
13249
|
+
new RegExp(valueFormat, data.value_format_flags ?? EMPTY);
|
|
13144
13250
|
}
|
|
13145
13251
|
catch (error) {
|
|
13146
13252
|
ctx.addIssue({
|
|
@@ -13149,6 +13255,12 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
|
13149
13255
|
});
|
|
13150
13256
|
}
|
|
13151
13257
|
}
|
|
13258
|
+
else if (data.value_format_flags) {
|
|
13259
|
+
ctx.addIssue({
|
|
13260
|
+
code: zod.ZodIssueCode.custom,
|
|
13261
|
+
message: 'value_format is required for value_format_flags',
|
|
13262
|
+
});
|
|
13263
|
+
}
|
|
13152
13264
|
}));
|
|
13153
13265
|
|
|
13154
13266
|
/**
|
|
@@ -15699,7 +15811,7 @@ class RuleCategorizer {
|
|
|
15699
15811
|
}
|
|
15700
15812
|
}
|
|
15701
15813
|
|
|
15702
|
-
const version = "2.
|
|
15814
|
+
const version = "2.3.0";
|
|
15703
15815
|
|
|
15704
15816
|
/**
|
|
15705
15817
|
* @file AGTree version
|
package/dist/agtree.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v2.
|
|
2
|
+
* AGTree v2.3.0 (build date: Thu, 19 Dec 2024 15:31:22 GMT)
|
|
3
3
|
* (c) 2024 Adguard Software Ltd.
|
|
4
4
|
* Released under the MIT license
|
|
5
5
|
* https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
|
|
@@ -12,7 +12,6 @@ import { TokenType as TokenType$1, tokenizeExtended, getFormattedTokenName } fro
|
|
|
12
12
|
import * as tldts from 'tldts';
|
|
13
13
|
import { parse } from 'tldts';
|
|
14
14
|
import isIp from 'is-ip';
|
|
15
|
-
import XRegExp from 'xregexp';
|
|
16
15
|
import cloneDeep from 'clone-deep';
|
|
17
16
|
import { modifiersCompatibilityTableData, redirectsCompatibilityTableData, scriptletsCompatibilityTableData } from './compatibility-table-data.js';
|
|
18
17
|
import zod from 'zod';
|
|
@@ -292,6 +291,8 @@ const COMMA_DOMAIN_LIST_SEPARATOR = ',';
|
|
|
292
291
|
const PIPE_MODIFIER_SEPARATOR = '|';
|
|
293
292
|
const CSS_MEDIA_MARKER = '@media';
|
|
294
293
|
const CSS_NOT_PSEUDO = 'not';
|
|
294
|
+
const CSS_BLOCK_OPEN = '{';
|
|
295
|
+
const CSS_BLOCK_CLOSE = '}';
|
|
295
296
|
const HINT_MARKER = '!+';
|
|
296
297
|
const HINT_MARKER_LEN = HINT_MARKER.length;
|
|
297
298
|
const NETWORK_RULE_EXCEPTION_MARKER = '@@';
|
|
@@ -6066,14 +6067,24 @@ class CssTokenStream {
|
|
|
6066
6067
|
// - end: end index of the token
|
|
6067
6068
|
// - props: additional properties of the token, if any (we don't use it here, this is why we use underscore)
|
|
6068
6069
|
// - balance: balance level of the token
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6070
|
+
try {
|
|
6071
|
+
tokenizeBalanced(source, (type, start, end, _, balance) => {
|
|
6072
|
+
this.tokens.push({
|
|
6073
|
+
type,
|
|
6074
|
+
start,
|
|
6075
|
+
end,
|
|
6076
|
+
balance,
|
|
6077
|
+
});
|
|
6075
6078
|
});
|
|
6076
|
-
}
|
|
6079
|
+
}
|
|
6080
|
+
catch (error) {
|
|
6081
|
+
// If the error is an AdblockSyntaxError, adjust the error positions to the base offset
|
|
6082
|
+
if (error instanceof AdblockSyntaxError) {
|
|
6083
|
+
error.start += baseOffset;
|
|
6084
|
+
error.end += baseOffset;
|
|
6085
|
+
throw error;
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6077
6088
|
this.index = 0;
|
|
6078
6089
|
this.baseOffset = baseOffset;
|
|
6079
6090
|
}
|
|
@@ -7633,6 +7644,40 @@ class AdgScriptletInjectionBodyParser extends ParserBase {
|
|
|
7633
7644
|
}
|
|
7634
7645
|
}
|
|
7635
7646
|
|
|
7647
|
+
/**
|
|
7648
|
+
* Represents an error that occurs when an operation is aborted.
|
|
7649
|
+
*/
|
|
7650
|
+
class AbortError extends Error {
|
|
7651
|
+
constructor() {
|
|
7652
|
+
super('Aborted');
|
|
7653
|
+
}
|
|
7654
|
+
}
|
|
7655
|
+
// TODO: AG-38480 add a stop function to the tokenizers callback and move `hasToken` to CSS Tokenizer as well
|
|
7656
|
+
/**
|
|
7657
|
+
* Checks if the given raw string contains any of the specified tokens.
|
|
7658
|
+
* This function uses error throwing inside the abort tokenization process.
|
|
7659
|
+
*
|
|
7660
|
+
* @param raw - The raw string to be tokenized and checked.
|
|
7661
|
+
* @param tokens - A set of token types to check for in the raw string.
|
|
7662
|
+
* @returns `true` if any of the specified tokens are found in the raw string, otherwise `false`.
|
|
7663
|
+
*/
|
|
7664
|
+
const hasToken = (raw, tokens) => {
|
|
7665
|
+
try {
|
|
7666
|
+
tokenizeExtended(raw, (type) => {
|
|
7667
|
+
if (tokens.has(type)) {
|
|
7668
|
+
throw new AbortError();
|
|
7669
|
+
}
|
|
7670
|
+
});
|
|
7671
|
+
}
|
|
7672
|
+
catch (e) {
|
|
7673
|
+
if (e instanceof AbortError) {
|
|
7674
|
+
return true;
|
|
7675
|
+
}
|
|
7676
|
+
throw e;
|
|
7677
|
+
}
|
|
7678
|
+
return false;
|
|
7679
|
+
};
|
|
7680
|
+
|
|
7636
7681
|
/* eslint-disable no-param-reassign */
|
|
7637
7682
|
/**
|
|
7638
7683
|
* Value map for binary serialization. This helps to reduce the size of the serialized data,
|
|
@@ -8005,6 +8050,33 @@ class CosmeticRuleParser extends ParserBase {
|
|
|
8005
8050
|
body: AdgCssInjectionParser.parse(rawBody, options, baseOffset + bodyStart),
|
|
8006
8051
|
};
|
|
8007
8052
|
};
|
|
8053
|
+
/**
|
|
8054
|
+
* Parses Adb CSS injection rules
|
|
8055
|
+
* eg: example.com##.foo { display: none; }
|
|
8056
|
+
*
|
|
8057
|
+
* @returns parsed rule
|
|
8058
|
+
*/
|
|
8059
|
+
const parseAbpCssInjection = () => {
|
|
8060
|
+
if (!options.parseAbpSpecificRules) {
|
|
8061
|
+
return null;
|
|
8062
|
+
}
|
|
8063
|
+
// check if the rule contains both CSS block open and close characters
|
|
8064
|
+
// if none of them is present we can stop parsing
|
|
8065
|
+
if (rawBody.indexOf(CSS_BLOCK_OPEN) === -1 && rawBody.indexOf(CSS_BLOCK_CLOSE) === -1) {
|
|
8066
|
+
return null;
|
|
8067
|
+
}
|
|
8068
|
+
if (!hasToken(rawBody, new Set([TokenType$1.OpenCurlyBracket, TokenType$1.CloseCurlyBracket]))) {
|
|
8069
|
+
return null;
|
|
8070
|
+
}
|
|
8071
|
+
// try to parse the raw body as an AdGuard CSS injection rule
|
|
8072
|
+
const body = AdgCssInjectionParser.parse(rawBody, options, baseOffset + bodyStart);
|
|
8073
|
+
// if the parsed rule type is a 'CssInjectionRuleBody', return the parsed rule
|
|
8074
|
+
return {
|
|
8075
|
+
syntax: AdblockSyntax.Abp,
|
|
8076
|
+
type: CosmeticRuleType.CssInjectionRule,
|
|
8077
|
+
body,
|
|
8078
|
+
};
|
|
8079
|
+
};
|
|
8008
8080
|
const parseAbpSnippetInjection = () => {
|
|
8009
8081
|
if (!options.parseAbpSpecificRules) {
|
|
8010
8082
|
throw new AdblockSyntaxError(sprintf(ERROR_MESSAGES$3.SYNTAX_DISABLED, AdblockSyntax.Abp), baseOffset + bodyStart, baseOffset + bodyEnd);
|
|
@@ -8116,10 +8188,22 @@ class CosmeticRuleParser extends ParserBase {
|
|
|
8116
8188
|
// the next function is called, and so on.
|
|
8117
8189
|
// If all functions return null, an error should be thrown.
|
|
8118
8190
|
const separatorMap = {
|
|
8119
|
-
'##': [
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8191
|
+
'##': [
|
|
8192
|
+
parseUboHtmlFiltering,
|
|
8193
|
+
parseUboScriptletInjection,
|
|
8194
|
+
parseUboCssInjection,
|
|
8195
|
+
parseAbpCssInjection,
|
|
8196
|
+
parseElementHiding,
|
|
8197
|
+
],
|
|
8198
|
+
'#@#': [
|
|
8199
|
+
parseUboHtmlFiltering,
|
|
8200
|
+
parseUboScriptletInjection,
|
|
8201
|
+
parseUboCssInjection,
|
|
8202
|
+
parseAbpCssInjection,
|
|
8203
|
+
parseElementHiding,
|
|
8204
|
+
],
|
|
8205
|
+
'#?#': [parseUboCssInjection, parseAbpCssInjection, parseElementHiding],
|
|
8206
|
+
'#@?#': [parseUboCssInjection, parseAbpCssInjection, parseElementHiding],
|
|
8123
8207
|
'#$#': [parseAdgCssInjection, parseAbpSnippetInjection],
|
|
8124
8208
|
'#@$#': [parseAdgCssInjection, parseAbpSnippetInjection],
|
|
8125
8209
|
'#$?#': [parseAdgCssInjection],
|
|
@@ -8188,7 +8272,7 @@ class CosmeticRuleParser extends ParserBase {
|
|
|
8188
8272
|
result = node.body.selectorList.value;
|
|
8189
8273
|
break;
|
|
8190
8274
|
case CosmeticRuleType.CssInjectionRule:
|
|
8191
|
-
if (node.syntax === AdblockSyntax.Adg) {
|
|
8275
|
+
if (node.syntax === AdblockSyntax.Adg || node.syntax === AdblockSyntax.Abp) {
|
|
8192
8276
|
result = AdgCssInjectionParser.generate(node.body);
|
|
8193
8277
|
}
|
|
8194
8278
|
else if (node.syntax === AdblockSyntax.Ubo) {
|
|
@@ -10744,10 +10828,11 @@ const isCustomValueFormatValidator = (valueFormat) => {
|
|
|
10744
10828
|
*
|
|
10745
10829
|
* @param modifier Modifier AST node.
|
|
10746
10830
|
* @param valueFormat Value format for the modifier.
|
|
10831
|
+
* @param valueFormatFlags Optional; RegExp flags for the value format.
|
|
10747
10832
|
*
|
|
10748
10833
|
* @returns Validation result.
|
|
10749
10834
|
*/
|
|
10750
|
-
const validateValue = (modifier, valueFormat) => {
|
|
10835
|
+
const validateValue = (modifier, valueFormat, valueFormatFlags) => {
|
|
10751
10836
|
if (isCustomValueFormatValidator(valueFormat)) {
|
|
10752
10837
|
const validator = CUSTOM_VALUE_FORMAT_MAP[valueFormat];
|
|
10753
10838
|
return validator(modifier);
|
|
@@ -10756,14 +10841,19 @@ const validateValue = (modifier, valueFormat) => {
|
|
|
10756
10841
|
if (!modifier.value?.value) {
|
|
10757
10842
|
return getValueRequiredValidationResult(modifierName);
|
|
10758
10843
|
}
|
|
10759
|
-
let
|
|
10844
|
+
let regExp;
|
|
10760
10845
|
try {
|
|
10761
|
-
|
|
10846
|
+
if (isString(valueFormatFlags)) {
|
|
10847
|
+
regExp = new RegExp(valueFormat, valueFormatFlags);
|
|
10848
|
+
}
|
|
10849
|
+
else {
|
|
10850
|
+
regExp = new RegExp(valueFormat);
|
|
10851
|
+
}
|
|
10762
10852
|
}
|
|
10763
10853
|
catch (e) {
|
|
10764
10854
|
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.INVALID_VALUE_FORMAT_REGEXP}: '${modifierName}'`);
|
|
10765
10855
|
}
|
|
10766
|
-
const isValid =
|
|
10856
|
+
const isValid = regExp.test(modifier.value?.value);
|
|
10767
10857
|
if (!isValid) {
|
|
10768
10858
|
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}'`);
|
|
10769
10859
|
}
|
|
@@ -11347,7 +11437,7 @@ const validateForSpecificSyntax = (syntax, modifier, isException) => {
|
|
|
11347
11437
|
if (!specificBlockerData.valueFormat) {
|
|
11348
11438
|
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.NO_VALUE_FORMAT_FOR_ASSIGNABLE}: '${modifierName}'`);
|
|
11349
11439
|
}
|
|
11350
|
-
return validateValue(modifier, specificBlockerData.valueFormat);
|
|
11440
|
+
return validateValue(modifier, specificBlockerData.valueFormat, specificBlockerData.valueFormatFlags);
|
|
11351
11441
|
}
|
|
11352
11442
|
if (modifier?.value) {
|
|
11353
11443
|
// e.g. 'third-party=true'
|
|
@@ -13044,8 +13134,14 @@ function getErrorMessage(error) {
|
|
|
13044
13134
|
* Known validators that don't need to be validated as regex.
|
|
13045
13135
|
*/
|
|
13046
13136
|
const KNOWN_VALIDATORS = new Set([
|
|
13137
|
+
'csp_value',
|
|
13047
13138
|
'domain',
|
|
13139
|
+
'permissions_value',
|
|
13140
|
+
'pipe_separated_apps',
|
|
13141
|
+
'pipe_separated_denyallow_domains',
|
|
13048
13142
|
'pipe_separated_domains',
|
|
13143
|
+
'pipe_separated_methods',
|
|
13144
|
+
'pipe_separated_stealth_options',
|
|
13049
13145
|
'regexp',
|
|
13050
13146
|
'url',
|
|
13051
13147
|
]);
|
|
@@ -13096,6 +13192,10 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
|
13096
13192
|
* Its value can be a regex pattern or a known validator name (e.g. `domain`, `pipe_separated_domains`, etc.).
|
|
13097
13193
|
*/
|
|
13098
13194
|
value_format: nonEmptyStringSchema.nullable().default(null),
|
|
13195
|
+
/**
|
|
13196
|
+
* Describes the flags for the `value_format` regex pattern.
|
|
13197
|
+
*/
|
|
13198
|
+
value_format_flags: nonEmptyStringSchema.nullable().default(null),
|
|
13099
13199
|
}).superRefine((data, ctx) => {
|
|
13100
13200
|
// TODO: find something better, for now we can't add refine logic to the base schema:
|
|
13101
13201
|
// https://github.com/colinhacks/zod/issues/454#issuecomment-848370721
|
|
@@ -13116,11 +13216,17 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
|
13116
13216
|
const valueFormat = data.value_format.trim();
|
|
13117
13217
|
// if it is a known validator, we don't need to validate it further
|
|
13118
13218
|
if (KNOWN_VALIDATORS.has(valueFormat)) {
|
|
13219
|
+
if (data.value_format_flags) {
|
|
13220
|
+
ctx.addIssue({
|
|
13221
|
+
code: zod.ZodIssueCode.custom,
|
|
13222
|
+
message: 'value_format_flags are not allowed for known validators',
|
|
13223
|
+
});
|
|
13224
|
+
}
|
|
13119
13225
|
return;
|
|
13120
13226
|
}
|
|
13121
13227
|
// otherwise, we need to validate it as a regex
|
|
13122
13228
|
try {
|
|
13123
|
-
|
|
13229
|
+
new RegExp(valueFormat, data.value_format_flags ?? EMPTY);
|
|
13124
13230
|
}
|
|
13125
13231
|
catch (error) {
|
|
13126
13232
|
ctx.addIssue({
|
|
@@ -13129,6 +13235,12 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
|
13129
13235
|
});
|
|
13130
13236
|
}
|
|
13131
13237
|
}
|
|
13238
|
+
else if (data.value_format_flags) {
|
|
13239
|
+
ctx.addIssue({
|
|
13240
|
+
code: zod.ZodIssueCode.custom,
|
|
13241
|
+
message: 'value_format is required for value_format_flags',
|
|
13242
|
+
});
|
|
13243
|
+
}
|
|
13132
13244
|
}));
|
|
13133
13245
|
|
|
13134
13246
|
/**
|
|
@@ -15679,7 +15791,7 @@ class RuleCategorizer {
|
|
|
15679
15791
|
}
|
|
15680
15792
|
}
|
|
15681
15793
|
|
|
15682
|
-
const version = "2.
|
|
15794
|
+
const version = "2.3.0";
|
|
15683
15795
|
|
|
15684
15796
|
/**
|
|
15685
15797
|
* @file AGTree version
|