@adguard/agtree 2.1.3 → 2.2.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 +5 -2
- package/dist/agtree.js +91 -13
- package/dist/agtree.mjs +91 -13
- 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.2.0 (build date: Wed, 27 Nov 2024 16:28:27 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
|
|
@@ -2357,9 +2357,11 @@ declare class FilterListParser extends ParserBase {
|
|
|
2357
2357
|
* @param ast AST to generate
|
|
2358
2358
|
* @param preferRaw If `true`, then the parser will use `raws.text` property of each rule
|
|
2359
2359
|
* if it is available. Default is `false`.
|
|
2360
|
+
* @param tolerant If `true`, errors during rule generation will be logged to the console and invalid rules
|
|
2361
|
+
* will be skipped. If `false`, an error will be thrown on the first invalid rule. Default is `true`.
|
|
2360
2362
|
* @returns Serialized filter list
|
|
2361
2363
|
*/
|
|
2362
|
-
static generate(ast: FilterList, preferRaw?: boolean): string;
|
|
2364
|
+
static generate(ast: FilterList, preferRaw?: boolean, tolerant?: boolean): string;
|
|
2363
2365
|
/**
|
|
2364
2366
|
* Serializes a filter list node to binary format.
|
|
2365
2367
|
*
|
|
@@ -3697,6 +3699,7 @@ declare const modifierDataSchema: zod.ZodEffects<zod.ZodTypeAny, {
|
|
|
3697
3699
|
exceptionOnly: boolean;
|
|
3698
3700
|
valueOptional: boolean;
|
|
3699
3701
|
valueFormat: string | null;
|
|
3702
|
+
valueFormatFlags: string | null;
|
|
3700
3703
|
}, any>;
|
|
3701
3704
|
/**
|
|
3702
3705
|
* Type of the modifier data schema.
|
package/dist/agtree.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v2.
|
|
2
|
+
* AGTree v2.2.0 (build date: Wed, 27 Nov 2024 16:28:27 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');
|
|
@@ -1913,6 +1912,9 @@ class AgentCommentRuleParser extends ParserBase {
|
|
|
1913
1912
|
static serialize(node, buffer) {
|
|
1914
1913
|
buffer.writeUint8(BinaryTypeMap.AgentRuleNode);
|
|
1915
1914
|
const count = node.children.length;
|
|
1915
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
1916
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
1917
|
+
// if there are no children in the binary data.
|
|
1916
1918
|
if (count) {
|
|
1917
1919
|
buffer.writeUint8(AgentRuleSerializationMap.Children);
|
|
1918
1920
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
@@ -1967,6 +1969,11 @@ class AgentCommentRuleParser extends ParserBase {
|
|
|
1967
1969
|
}
|
|
1968
1970
|
prop = buffer.readUint8();
|
|
1969
1971
|
}
|
|
1972
|
+
// Maybe children are not present in the binary data,
|
|
1973
|
+
// in this case, we should initialize it as an empty array.
|
|
1974
|
+
if (!node.children) {
|
|
1975
|
+
node.children = [];
|
|
1976
|
+
}
|
|
1970
1977
|
}
|
|
1971
1978
|
}
|
|
1972
1979
|
|
|
@@ -2862,6 +2869,9 @@ class HintCommentRuleParser extends ParserBase {
|
|
|
2862
2869
|
buffer.writeUint8(SYNTAX_SERIALIZATION_MAP.get(exports.AdblockSyntax.Adg) ?? 0);
|
|
2863
2870
|
}
|
|
2864
2871
|
const count = node.children.length;
|
|
2872
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
2873
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
2874
|
+
// if there are no children in the binary data.
|
|
2865
2875
|
if (count) {
|
|
2866
2876
|
buffer.writeUint8(HintRuleSerializationMap.Children);
|
|
2867
2877
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
@@ -2915,6 +2925,11 @@ class HintCommentRuleParser extends ParserBase {
|
|
|
2915
2925
|
}
|
|
2916
2926
|
prop = buffer.readUint8();
|
|
2917
2927
|
}
|
|
2928
|
+
// Maybe children are not present in the binary data,
|
|
2929
|
+
// in this case, we should initialize it as an empty array.
|
|
2930
|
+
if (!node.children) {
|
|
2931
|
+
node.children = [];
|
|
2932
|
+
}
|
|
2918
2933
|
}
|
|
2919
2934
|
}
|
|
2920
2935
|
|
|
@@ -5348,6 +5363,9 @@ class ModifierListParser extends ParserBase {
|
|
|
5348
5363
|
static serialize(node, buffer) {
|
|
5349
5364
|
buffer.writeUint8(BinaryTypeMap.ModifierListNode);
|
|
5350
5365
|
const count = node.children.length;
|
|
5366
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
5367
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
5368
|
+
// if there are no children in the binary data.
|
|
5351
5369
|
if (count) {
|
|
5352
5370
|
buffer.writeUint8(ModifierListNodeSerializationMap.Children);
|
|
5353
5371
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
@@ -5399,6 +5417,11 @@ class ModifierListParser extends ParserBase {
|
|
|
5399
5417
|
}
|
|
5400
5418
|
prop = buffer.readUint8();
|
|
5401
5419
|
}
|
|
5420
|
+
// Maybe children are not present in the binary data,
|
|
5421
|
+
// in this case, we should initialize it as an empty array.
|
|
5422
|
+
if (!node.children) {
|
|
5423
|
+
node.children = [];
|
|
5424
|
+
}
|
|
5402
5425
|
}
|
|
5403
5426
|
}
|
|
5404
5427
|
|
|
@@ -8611,10 +8634,16 @@ class NetworkRuleParser extends ParserBase {
|
|
|
8611
8634
|
const pattern = ValueParser.parse(raw.slice(patternStart, patternEnd), options, baseOffset + patternStart);
|
|
8612
8635
|
// Parse modifiers (if any)
|
|
8613
8636
|
let modifiers;
|
|
8637
|
+
// Get a last non-whitespace index
|
|
8638
|
+
const lastNonWsIndex = StringUtils.skipWSBack(raw);
|
|
8614
8639
|
// Find start and end index of the modifiers
|
|
8615
8640
|
const modifiersStart = separatorIndex + 1;
|
|
8616
|
-
const modifiersEnd =
|
|
8641
|
+
const modifiersEnd = lastNonWsIndex + 1;
|
|
8617
8642
|
if (separatorIndex !== -1) {
|
|
8643
|
+
// Check for empty modifiers
|
|
8644
|
+
if (separatorIndex === lastNonWsIndex) {
|
|
8645
|
+
throw new AdblockSyntaxError('Empty modifiers are not allowed', baseOffset + separatorIndex, baseOffset + raw.length);
|
|
8646
|
+
}
|
|
8618
8647
|
modifiers = ModifierListParser.parse(raw.slice(modifiersStart, modifiersEnd), options, baseOffset + modifiersStart);
|
|
8619
8648
|
}
|
|
8620
8649
|
// Throw error if there is no pattern and no modifiers
|
|
@@ -8933,6 +8962,9 @@ class HostRuleParser extends ParserBase {
|
|
|
8933
8962
|
buffer.writeUint32(node.end);
|
|
8934
8963
|
}
|
|
8935
8964
|
const count = node.children.length;
|
|
8965
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
8966
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
8967
|
+
// if there are no children in the binary data.
|
|
8936
8968
|
if (count) {
|
|
8937
8969
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
8938
8970
|
if (count > UINT16_MAX) {
|
|
@@ -8976,6 +9008,11 @@ class HostRuleParser extends ParserBase {
|
|
|
8976
9008
|
}
|
|
8977
9009
|
prop = buffer.readUint8();
|
|
8978
9010
|
}
|
|
9011
|
+
// Maybe children are not present in the binary data,
|
|
9012
|
+
// in this case, we should initialize it as an empty array.
|
|
9013
|
+
if (!node.children) {
|
|
9014
|
+
node.children = [];
|
|
9015
|
+
}
|
|
8979
9016
|
}
|
|
8980
9017
|
/**
|
|
8981
9018
|
* Serializes a host rule node to binary format.
|
|
@@ -9743,9 +9780,11 @@ class FilterListParser extends ParserBase {
|
|
|
9743
9780
|
* @param ast AST to generate
|
|
9744
9781
|
* @param preferRaw If `true`, then the parser will use `raws.text` property of each rule
|
|
9745
9782
|
* if it is available. Default is `false`.
|
|
9783
|
+
* @param tolerant If `true`, errors during rule generation will be logged to the console and invalid rules
|
|
9784
|
+
* will be skipped. If `false`, an error will be thrown on the first invalid rule. Default is `true`.
|
|
9746
9785
|
* @returns Serialized filter list
|
|
9747
9786
|
*/
|
|
9748
|
-
static generate(ast, preferRaw = false) {
|
|
9787
|
+
static generate(ast, preferRaw = false, tolerant = true) {
|
|
9749
9788
|
let result = EMPTY;
|
|
9750
9789
|
for (let i = 0; i < ast.children.length; i += 1) {
|
|
9751
9790
|
const rule = ast.children[i];
|
|
@@ -9753,7 +9792,18 @@ class FilterListParser extends ParserBase {
|
|
|
9753
9792
|
result += rule.raws.text;
|
|
9754
9793
|
}
|
|
9755
9794
|
else {
|
|
9756
|
-
|
|
9795
|
+
try {
|
|
9796
|
+
result += RuleParser.generate(rule);
|
|
9797
|
+
}
|
|
9798
|
+
catch (error) {
|
|
9799
|
+
if (tolerant) {
|
|
9800
|
+
// eslint-disable-next-line no-console
|
|
9801
|
+
console.error(`Error when generating: ${error}`);
|
|
9802
|
+
}
|
|
9803
|
+
else {
|
|
9804
|
+
throw new Error(String(error));
|
|
9805
|
+
}
|
|
9806
|
+
}
|
|
9757
9807
|
}
|
|
9758
9808
|
switch (rule.raws?.nl) {
|
|
9759
9809
|
case 'crlf':
|
|
@@ -10713,10 +10763,11 @@ const isCustomValueFormatValidator = (valueFormat) => {
|
|
|
10713
10763
|
*
|
|
10714
10764
|
* @param modifier Modifier AST node.
|
|
10715
10765
|
* @param valueFormat Value format for the modifier.
|
|
10766
|
+
* @param valueFormatFlags Optional; RegExp flags for the value format.
|
|
10716
10767
|
*
|
|
10717
10768
|
* @returns Validation result.
|
|
10718
10769
|
*/
|
|
10719
|
-
const validateValue = (modifier, valueFormat) => {
|
|
10770
|
+
const validateValue = (modifier, valueFormat, valueFormatFlags) => {
|
|
10720
10771
|
if (isCustomValueFormatValidator(valueFormat)) {
|
|
10721
10772
|
const validator = CUSTOM_VALUE_FORMAT_MAP[valueFormat];
|
|
10722
10773
|
return validator(modifier);
|
|
@@ -10725,14 +10776,19 @@ const validateValue = (modifier, valueFormat) => {
|
|
|
10725
10776
|
if (!modifier.value?.value) {
|
|
10726
10777
|
return getValueRequiredValidationResult(modifierName);
|
|
10727
10778
|
}
|
|
10728
|
-
let
|
|
10779
|
+
let regExp;
|
|
10729
10780
|
try {
|
|
10730
|
-
|
|
10781
|
+
if (isString(valueFormatFlags)) {
|
|
10782
|
+
regExp = new RegExp(valueFormat, valueFormatFlags);
|
|
10783
|
+
}
|
|
10784
|
+
else {
|
|
10785
|
+
regExp = new RegExp(valueFormat);
|
|
10786
|
+
}
|
|
10731
10787
|
}
|
|
10732
10788
|
catch (e) {
|
|
10733
10789
|
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.INVALID_VALUE_FORMAT_REGEXP}: '${modifierName}'`);
|
|
10734
10790
|
}
|
|
10735
|
-
const isValid =
|
|
10791
|
+
const isValid = regExp.test(modifier.value?.value);
|
|
10736
10792
|
if (!isValid) {
|
|
10737
10793
|
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}'`);
|
|
10738
10794
|
}
|
|
@@ -11316,7 +11372,7 @@ const validateForSpecificSyntax = (syntax, modifier, isException) => {
|
|
|
11316
11372
|
if (!specificBlockerData.valueFormat) {
|
|
11317
11373
|
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.NO_VALUE_FORMAT_FOR_ASSIGNABLE}: '${modifierName}'`);
|
|
11318
11374
|
}
|
|
11319
|
-
return validateValue(modifier, specificBlockerData.valueFormat);
|
|
11375
|
+
return validateValue(modifier, specificBlockerData.valueFormat, specificBlockerData.valueFormatFlags);
|
|
11320
11376
|
}
|
|
11321
11377
|
if (modifier?.value) {
|
|
11322
11378
|
// e.g. 'third-party=true'
|
|
@@ -13013,8 +13069,14 @@ function getErrorMessage(error) {
|
|
|
13013
13069
|
* Known validators that don't need to be validated as regex.
|
|
13014
13070
|
*/
|
|
13015
13071
|
const KNOWN_VALIDATORS = new Set([
|
|
13072
|
+
'csp_value',
|
|
13016
13073
|
'domain',
|
|
13074
|
+
'permissions_value',
|
|
13075
|
+
'pipe_separated_apps',
|
|
13076
|
+
'pipe_separated_denyallow_domains',
|
|
13017
13077
|
'pipe_separated_domains',
|
|
13078
|
+
'pipe_separated_methods',
|
|
13079
|
+
'pipe_separated_stealth_options',
|
|
13018
13080
|
'regexp',
|
|
13019
13081
|
'url',
|
|
13020
13082
|
]);
|
|
@@ -13065,6 +13127,10 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
|
13065
13127
|
* Its value can be a regex pattern or a known validator name (e.g. `domain`, `pipe_separated_domains`, etc.).
|
|
13066
13128
|
*/
|
|
13067
13129
|
value_format: nonEmptyStringSchema.nullable().default(null),
|
|
13130
|
+
/**
|
|
13131
|
+
* Describes the flags for the `value_format` regex pattern.
|
|
13132
|
+
*/
|
|
13133
|
+
value_format_flags: nonEmptyStringSchema.nullable().default(null),
|
|
13068
13134
|
}).superRefine((data, ctx) => {
|
|
13069
13135
|
// TODO: find something better, for now we can't add refine logic to the base schema:
|
|
13070
13136
|
// https://github.com/colinhacks/zod/issues/454#issuecomment-848370721
|
|
@@ -13085,11 +13151,17 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
|
13085
13151
|
const valueFormat = data.value_format.trim();
|
|
13086
13152
|
// if it is a known validator, we don't need to validate it further
|
|
13087
13153
|
if (KNOWN_VALIDATORS.has(valueFormat)) {
|
|
13154
|
+
if (data.value_format_flags) {
|
|
13155
|
+
ctx.addIssue({
|
|
13156
|
+
code: zod.ZodIssueCode.custom,
|
|
13157
|
+
message: 'value_format_flags are not allowed for known validators',
|
|
13158
|
+
});
|
|
13159
|
+
}
|
|
13088
13160
|
return;
|
|
13089
13161
|
}
|
|
13090
13162
|
// otherwise, we need to validate it as a regex
|
|
13091
13163
|
try {
|
|
13092
|
-
|
|
13164
|
+
new RegExp(valueFormat, data.value_format_flags ?? EMPTY);
|
|
13093
13165
|
}
|
|
13094
13166
|
catch (error) {
|
|
13095
13167
|
ctx.addIssue({
|
|
@@ -13098,6 +13170,12 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
|
13098
13170
|
});
|
|
13099
13171
|
}
|
|
13100
13172
|
}
|
|
13173
|
+
else if (data.value_format_flags) {
|
|
13174
|
+
ctx.addIssue({
|
|
13175
|
+
code: zod.ZodIssueCode.custom,
|
|
13176
|
+
message: 'value_format is required for value_format_flags',
|
|
13177
|
+
});
|
|
13178
|
+
}
|
|
13101
13179
|
}));
|
|
13102
13180
|
|
|
13103
13181
|
/**
|
|
@@ -14700,7 +14778,7 @@ class RawFilterListConverter extends ConverterBase {
|
|
|
14700
14778
|
return createConversionResult(rawFilterList, false);
|
|
14701
14779
|
}
|
|
14702
14780
|
// Otherwise, serialize the filter list and return the result
|
|
14703
|
-
return createConversionResult(FilterListParser.generate(conversionResult.result), true);
|
|
14781
|
+
return createConversionResult(FilterListParser.generate(conversionResult.result, false, tolerant), true);
|
|
14704
14782
|
}
|
|
14705
14783
|
}
|
|
14706
14784
|
|
|
@@ -15648,7 +15726,7 @@ class RuleCategorizer {
|
|
|
15648
15726
|
}
|
|
15649
15727
|
}
|
|
15650
15728
|
|
|
15651
|
-
const version = "2.
|
|
15729
|
+
const version = "2.2.0";
|
|
15652
15730
|
|
|
15653
15731
|
/**
|
|
15654
15732
|
* @file AGTree version
|
package/dist/agtree.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v2.
|
|
2
|
+
* AGTree v2.2.0 (build date: Wed, 27 Nov 2024 16:28:27 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';
|
|
@@ -1893,6 +1892,9 @@ class AgentCommentRuleParser extends ParserBase {
|
|
|
1893
1892
|
static serialize(node, buffer) {
|
|
1894
1893
|
buffer.writeUint8(BinaryTypeMap.AgentRuleNode);
|
|
1895
1894
|
const count = node.children.length;
|
|
1895
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
1896
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
1897
|
+
// if there are no children in the binary data.
|
|
1896
1898
|
if (count) {
|
|
1897
1899
|
buffer.writeUint8(AgentRuleSerializationMap.Children);
|
|
1898
1900
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
@@ -1947,6 +1949,11 @@ class AgentCommentRuleParser extends ParserBase {
|
|
|
1947
1949
|
}
|
|
1948
1950
|
prop = buffer.readUint8();
|
|
1949
1951
|
}
|
|
1952
|
+
// Maybe children are not present in the binary data,
|
|
1953
|
+
// in this case, we should initialize it as an empty array.
|
|
1954
|
+
if (!node.children) {
|
|
1955
|
+
node.children = [];
|
|
1956
|
+
}
|
|
1950
1957
|
}
|
|
1951
1958
|
}
|
|
1952
1959
|
|
|
@@ -2842,6 +2849,9 @@ class HintCommentRuleParser extends ParserBase {
|
|
|
2842
2849
|
buffer.writeUint8(SYNTAX_SERIALIZATION_MAP.get(AdblockSyntax.Adg) ?? 0);
|
|
2843
2850
|
}
|
|
2844
2851
|
const count = node.children.length;
|
|
2852
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
2853
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
2854
|
+
// if there are no children in the binary data.
|
|
2845
2855
|
if (count) {
|
|
2846
2856
|
buffer.writeUint8(HintRuleSerializationMap.Children);
|
|
2847
2857
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
@@ -2895,6 +2905,11 @@ class HintCommentRuleParser extends ParserBase {
|
|
|
2895
2905
|
}
|
|
2896
2906
|
prop = buffer.readUint8();
|
|
2897
2907
|
}
|
|
2908
|
+
// Maybe children are not present in the binary data,
|
|
2909
|
+
// in this case, we should initialize it as an empty array.
|
|
2910
|
+
if (!node.children) {
|
|
2911
|
+
node.children = [];
|
|
2912
|
+
}
|
|
2898
2913
|
}
|
|
2899
2914
|
}
|
|
2900
2915
|
|
|
@@ -5328,6 +5343,9 @@ class ModifierListParser extends ParserBase {
|
|
|
5328
5343
|
static serialize(node, buffer) {
|
|
5329
5344
|
buffer.writeUint8(BinaryTypeMap.ModifierListNode);
|
|
5330
5345
|
const count = node.children.length;
|
|
5346
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
5347
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
5348
|
+
// if there are no children in the binary data.
|
|
5331
5349
|
if (count) {
|
|
5332
5350
|
buffer.writeUint8(ModifierListNodeSerializationMap.Children);
|
|
5333
5351
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
@@ -5379,6 +5397,11 @@ class ModifierListParser extends ParserBase {
|
|
|
5379
5397
|
}
|
|
5380
5398
|
prop = buffer.readUint8();
|
|
5381
5399
|
}
|
|
5400
|
+
// Maybe children are not present in the binary data,
|
|
5401
|
+
// in this case, we should initialize it as an empty array.
|
|
5402
|
+
if (!node.children) {
|
|
5403
|
+
node.children = [];
|
|
5404
|
+
}
|
|
5382
5405
|
}
|
|
5383
5406
|
}
|
|
5384
5407
|
|
|
@@ -8591,10 +8614,16 @@ class NetworkRuleParser extends ParserBase {
|
|
|
8591
8614
|
const pattern = ValueParser.parse(raw.slice(patternStart, patternEnd), options, baseOffset + patternStart);
|
|
8592
8615
|
// Parse modifiers (if any)
|
|
8593
8616
|
let modifiers;
|
|
8617
|
+
// Get a last non-whitespace index
|
|
8618
|
+
const lastNonWsIndex = StringUtils.skipWSBack(raw);
|
|
8594
8619
|
// Find start and end index of the modifiers
|
|
8595
8620
|
const modifiersStart = separatorIndex + 1;
|
|
8596
|
-
const modifiersEnd =
|
|
8621
|
+
const modifiersEnd = lastNonWsIndex + 1;
|
|
8597
8622
|
if (separatorIndex !== -1) {
|
|
8623
|
+
// Check for empty modifiers
|
|
8624
|
+
if (separatorIndex === lastNonWsIndex) {
|
|
8625
|
+
throw new AdblockSyntaxError('Empty modifiers are not allowed', baseOffset + separatorIndex, baseOffset + raw.length);
|
|
8626
|
+
}
|
|
8598
8627
|
modifiers = ModifierListParser.parse(raw.slice(modifiersStart, modifiersEnd), options, baseOffset + modifiersStart);
|
|
8599
8628
|
}
|
|
8600
8629
|
// Throw error if there is no pattern and no modifiers
|
|
@@ -8913,6 +8942,9 @@ class HostRuleParser extends ParserBase {
|
|
|
8913
8942
|
buffer.writeUint32(node.end);
|
|
8914
8943
|
}
|
|
8915
8944
|
const count = node.children.length;
|
|
8945
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
8946
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
8947
|
+
// if there are no children in the binary data.
|
|
8916
8948
|
if (count) {
|
|
8917
8949
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
8918
8950
|
if (count > UINT16_MAX) {
|
|
@@ -8956,6 +8988,11 @@ class HostRuleParser extends ParserBase {
|
|
|
8956
8988
|
}
|
|
8957
8989
|
prop = buffer.readUint8();
|
|
8958
8990
|
}
|
|
8991
|
+
// Maybe children are not present in the binary data,
|
|
8992
|
+
// in this case, we should initialize it as an empty array.
|
|
8993
|
+
if (!node.children) {
|
|
8994
|
+
node.children = [];
|
|
8995
|
+
}
|
|
8959
8996
|
}
|
|
8960
8997
|
/**
|
|
8961
8998
|
* Serializes a host rule node to binary format.
|
|
@@ -9723,9 +9760,11 @@ class FilterListParser extends ParserBase {
|
|
|
9723
9760
|
* @param ast AST to generate
|
|
9724
9761
|
* @param preferRaw If `true`, then the parser will use `raws.text` property of each rule
|
|
9725
9762
|
* if it is available. Default is `false`.
|
|
9763
|
+
* @param tolerant If `true`, errors during rule generation will be logged to the console and invalid rules
|
|
9764
|
+
* will be skipped. If `false`, an error will be thrown on the first invalid rule. Default is `true`.
|
|
9726
9765
|
* @returns Serialized filter list
|
|
9727
9766
|
*/
|
|
9728
|
-
static generate(ast, preferRaw = false) {
|
|
9767
|
+
static generate(ast, preferRaw = false, tolerant = true) {
|
|
9729
9768
|
let result = EMPTY;
|
|
9730
9769
|
for (let i = 0; i < ast.children.length; i += 1) {
|
|
9731
9770
|
const rule = ast.children[i];
|
|
@@ -9733,7 +9772,18 @@ class FilterListParser extends ParserBase {
|
|
|
9733
9772
|
result += rule.raws.text;
|
|
9734
9773
|
}
|
|
9735
9774
|
else {
|
|
9736
|
-
|
|
9775
|
+
try {
|
|
9776
|
+
result += RuleParser.generate(rule);
|
|
9777
|
+
}
|
|
9778
|
+
catch (error) {
|
|
9779
|
+
if (tolerant) {
|
|
9780
|
+
// eslint-disable-next-line no-console
|
|
9781
|
+
console.error(`Error when generating: ${error}`);
|
|
9782
|
+
}
|
|
9783
|
+
else {
|
|
9784
|
+
throw new Error(String(error));
|
|
9785
|
+
}
|
|
9786
|
+
}
|
|
9737
9787
|
}
|
|
9738
9788
|
switch (rule.raws?.nl) {
|
|
9739
9789
|
case 'crlf':
|
|
@@ -10693,10 +10743,11 @@ const isCustomValueFormatValidator = (valueFormat) => {
|
|
|
10693
10743
|
*
|
|
10694
10744
|
* @param modifier Modifier AST node.
|
|
10695
10745
|
* @param valueFormat Value format for the modifier.
|
|
10746
|
+
* @param valueFormatFlags Optional; RegExp flags for the value format.
|
|
10696
10747
|
*
|
|
10697
10748
|
* @returns Validation result.
|
|
10698
10749
|
*/
|
|
10699
|
-
const validateValue = (modifier, valueFormat) => {
|
|
10750
|
+
const validateValue = (modifier, valueFormat, valueFormatFlags) => {
|
|
10700
10751
|
if (isCustomValueFormatValidator(valueFormat)) {
|
|
10701
10752
|
const validator = CUSTOM_VALUE_FORMAT_MAP[valueFormat];
|
|
10702
10753
|
return validator(modifier);
|
|
@@ -10705,14 +10756,19 @@ const validateValue = (modifier, valueFormat) => {
|
|
|
10705
10756
|
if (!modifier.value?.value) {
|
|
10706
10757
|
return getValueRequiredValidationResult(modifierName);
|
|
10707
10758
|
}
|
|
10708
|
-
let
|
|
10759
|
+
let regExp;
|
|
10709
10760
|
try {
|
|
10710
|
-
|
|
10761
|
+
if (isString(valueFormatFlags)) {
|
|
10762
|
+
regExp = new RegExp(valueFormat, valueFormatFlags);
|
|
10763
|
+
}
|
|
10764
|
+
else {
|
|
10765
|
+
regExp = new RegExp(valueFormat);
|
|
10766
|
+
}
|
|
10711
10767
|
}
|
|
10712
10768
|
catch (e) {
|
|
10713
10769
|
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.INVALID_VALUE_FORMAT_REGEXP}: '${modifierName}'`);
|
|
10714
10770
|
}
|
|
10715
|
-
const isValid =
|
|
10771
|
+
const isValid = regExp.test(modifier.value?.value);
|
|
10716
10772
|
if (!isValid) {
|
|
10717
10773
|
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}'`);
|
|
10718
10774
|
}
|
|
@@ -11296,7 +11352,7 @@ const validateForSpecificSyntax = (syntax, modifier, isException) => {
|
|
|
11296
11352
|
if (!specificBlockerData.valueFormat) {
|
|
11297
11353
|
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.NO_VALUE_FORMAT_FOR_ASSIGNABLE}: '${modifierName}'`);
|
|
11298
11354
|
}
|
|
11299
|
-
return validateValue(modifier, specificBlockerData.valueFormat);
|
|
11355
|
+
return validateValue(modifier, specificBlockerData.valueFormat, specificBlockerData.valueFormatFlags);
|
|
11300
11356
|
}
|
|
11301
11357
|
if (modifier?.value) {
|
|
11302
11358
|
// e.g. 'third-party=true'
|
|
@@ -12993,8 +13049,14 @@ function getErrorMessage(error) {
|
|
|
12993
13049
|
* Known validators that don't need to be validated as regex.
|
|
12994
13050
|
*/
|
|
12995
13051
|
const KNOWN_VALIDATORS = new Set([
|
|
13052
|
+
'csp_value',
|
|
12996
13053
|
'domain',
|
|
13054
|
+
'permissions_value',
|
|
13055
|
+
'pipe_separated_apps',
|
|
13056
|
+
'pipe_separated_denyallow_domains',
|
|
12997
13057
|
'pipe_separated_domains',
|
|
13058
|
+
'pipe_separated_methods',
|
|
13059
|
+
'pipe_separated_stealth_options',
|
|
12998
13060
|
'regexp',
|
|
12999
13061
|
'url',
|
|
13000
13062
|
]);
|
|
@@ -13045,6 +13107,10 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
|
13045
13107
|
* Its value can be a regex pattern or a known validator name (e.g. `domain`, `pipe_separated_domains`, etc.).
|
|
13046
13108
|
*/
|
|
13047
13109
|
value_format: nonEmptyStringSchema.nullable().default(null),
|
|
13110
|
+
/**
|
|
13111
|
+
* Describes the flags for the `value_format` regex pattern.
|
|
13112
|
+
*/
|
|
13113
|
+
value_format_flags: nonEmptyStringSchema.nullable().default(null),
|
|
13048
13114
|
}).superRefine((data, ctx) => {
|
|
13049
13115
|
// TODO: find something better, for now we can't add refine logic to the base schema:
|
|
13050
13116
|
// https://github.com/colinhacks/zod/issues/454#issuecomment-848370721
|
|
@@ -13065,11 +13131,17 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
|
13065
13131
|
const valueFormat = data.value_format.trim();
|
|
13066
13132
|
// if it is a known validator, we don't need to validate it further
|
|
13067
13133
|
if (KNOWN_VALIDATORS.has(valueFormat)) {
|
|
13134
|
+
if (data.value_format_flags) {
|
|
13135
|
+
ctx.addIssue({
|
|
13136
|
+
code: zod.ZodIssueCode.custom,
|
|
13137
|
+
message: 'value_format_flags are not allowed for known validators',
|
|
13138
|
+
});
|
|
13139
|
+
}
|
|
13068
13140
|
return;
|
|
13069
13141
|
}
|
|
13070
13142
|
// otherwise, we need to validate it as a regex
|
|
13071
13143
|
try {
|
|
13072
|
-
|
|
13144
|
+
new RegExp(valueFormat, data.value_format_flags ?? EMPTY);
|
|
13073
13145
|
}
|
|
13074
13146
|
catch (error) {
|
|
13075
13147
|
ctx.addIssue({
|
|
@@ -13078,6 +13150,12 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
|
13078
13150
|
});
|
|
13079
13151
|
}
|
|
13080
13152
|
}
|
|
13153
|
+
else if (data.value_format_flags) {
|
|
13154
|
+
ctx.addIssue({
|
|
13155
|
+
code: zod.ZodIssueCode.custom,
|
|
13156
|
+
message: 'value_format is required for value_format_flags',
|
|
13157
|
+
});
|
|
13158
|
+
}
|
|
13081
13159
|
}));
|
|
13082
13160
|
|
|
13083
13161
|
/**
|
|
@@ -14680,7 +14758,7 @@ class RawFilterListConverter extends ConverterBase {
|
|
|
14680
14758
|
return createConversionResult(rawFilterList, false);
|
|
14681
14759
|
}
|
|
14682
14760
|
// Otherwise, serialize the filter list and return the result
|
|
14683
|
-
return createConversionResult(FilterListParser.generate(conversionResult.result), true);
|
|
14761
|
+
return createConversionResult(FilterListParser.generate(conversionResult.result, false, tolerant), true);
|
|
14684
14762
|
}
|
|
14685
14763
|
}
|
|
14686
14764
|
|
|
@@ -15628,7 +15706,7 @@ class RuleCategorizer {
|
|
|
15628
15706
|
}
|
|
15629
15707
|
}
|
|
15630
15708
|
|
|
15631
|
-
const version = "2.
|
|
15709
|
+
const version = "2.2.0";
|
|
15632
15710
|
|
|
15633
15711
|
/**
|
|
15634
15712
|
* @file AGTree version
|