@adguard/agtree 1.1.2 → 1.1.4
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 -1
- package/dist/agtree.cjs +1118 -395
- package/dist/agtree.d.ts +233 -40
- package/dist/agtree.esm.js +1113 -392
- package/dist/agtree.iife.min.js +14 -3
- package/dist/agtree.umd.min.js +14 -3
- package/dist/build.txt +1 -1
- package/package.json +1 -1
package/dist/agtree.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v1.1.
|
|
2
|
+
* AGTree v1.1.4 (build date: Wed, 30 Aug 2023 10:02:46 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
|
|
@@ -11,8 +11,9 @@ var coerce = require('semver/functions/coerce.js');
|
|
|
11
11
|
var JSON5 = require('json5');
|
|
12
12
|
var ecssTree = require('@adguard/ecss-tree');
|
|
13
13
|
var cloneDeep = require('clone-deep');
|
|
14
|
-
var
|
|
14
|
+
var XRegExp = require('xregexp');
|
|
15
15
|
var tldts = require('tldts');
|
|
16
|
+
var scriptlets = require('@adguard/scriptlets');
|
|
16
17
|
|
|
17
18
|
function _interopNamespaceDefault(e) {
|
|
18
19
|
var n = Object.create(null);
|
|
@@ -115,6 +116,7 @@ const CR = '\r';
|
|
|
115
116
|
const FF = '\f';
|
|
116
117
|
const LF = '\n';
|
|
117
118
|
const CRLF = CR + LF;
|
|
119
|
+
const NEWLINE = LF;
|
|
118
120
|
const DOUBLE_QUOTE = '"';
|
|
119
121
|
const SINGLE_QUOTE = '\'';
|
|
120
122
|
// Brackets
|
|
@@ -126,21 +128,157 @@ const OPEN_CURLY_BRACKET = '{';
|
|
|
126
128
|
const CLOSE_CURLY_BRACKET = '}';
|
|
127
129
|
// Letters
|
|
128
130
|
const SMALL_LETTER_A = 'a';
|
|
131
|
+
const SMALL_LETTER_B = 'b';
|
|
132
|
+
const SMALL_LETTER_C = 'c';
|
|
133
|
+
const SMALL_LETTER_D = 'd';
|
|
134
|
+
const SMALL_LETTER_E = 'e';
|
|
135
|
+
const SMALL_LETTER_F = 'f';
|
|
136
|
+
const SMALL_LETTER_G = 'g';
|
|
137
|
+
const SMALL_LETTER_H = 'h';
|
|
138
|
+
const SMALL_LETTER_I = 'i';
|
|
139
|
+
const SMALL_LETTER_J = 'j';
|
|
140
|
+
const SMALL_LETTER_K = 'k';
|
|
141
|
+
const SMALL_LETTER_L = 'l';
|
|
142
|
+
const SMALL_LETTER_M = 'm';
|
|
143
|
+
const SMALL_LETTER_N = 'n';
|
|
144
|
+
const SMALL_LETTER_O = 'o';
|
|
145
|
+
const SMALL_LETTER_P = 'p';
|
|
146
|
+
const SMALL_LETTER_Q = 'q';
|
|
147
|
+
const SMALL_LETTER_R = 'r';
|
|
148
|
+
const SMALL_LETTER_S = 's';
|
|
149
|
+
const SMALL_LETTER_T = 't';
|
|
150
|
+
const SMALL_LETTER_U = 'u';
|
|
151
|
+
const SMALL_LETTER_V = 'v';
|
|
152
|
+
const SMALL_LETTER_W = 'w';
|
|
153
|
+
const SMALL_LETTER_X = 'x';
|
|
154
|
+
const SMALL_LETTER_Y = 'y';
|
|
129
155
|
const SMALL_LETTER_Z = 'z';
|
|
156
|
+
/**
|
|
157
|
+
* Set of all small letters.
|
|
158
|
+
*/
|
|
159
|
+
const SMALL_LETTERS = new Set([
|
|
160
|
+
SMALL_LETTER_A,
|
|
161
|
+
SMALL_LETTER_B,
|
|
162
|
+
SMALL_LETTER_C,
|
|
163
|
+
SMALL_LETTER_D,
|
|
164
|
+
SMALL_LETTER_E,
|
|
165
|
+
SMALL_LETTER_F,
|
|
166
|
+
SMALL_LETTER_G,
|
|
167
|
+
SMALL_LETTER_H,
|
|
168
|
+
SMALL_LETTER_I,
|
|
169
|
+
SMALL_LETTER_J,
|
|
170
|
+
SMALL_LETTER_K,
|
|
171
|
+
SMALL_LETTER_L,
|
|
172
|
+
SMALL_LETTER_M,
|
|
173
|
+
SMALL_LETTER_N,
|
|
174
|
+
SMALL_LETTER_O,
|
|
175
|
+
SMALL_LETTER_P,
|
|
176
|
+
SMALL_LETTER_Q,
|
|
177
|
+
SMALL_LETTER_R,
|
|
178
|
+
SMALL_LETTER_S,
|
|
179
|
+
SMALL_LETTER_T,
|
|
180
|
+
SMALL_LETTER_U,
|
|
181
|
+
SMALL_LETTER_V,
|
|
182
|
+
SMALL_LETTER_W,
|
|
183
|
+
SMALL_LETTER_X,
|
|
184
|
+
SMALL_LETTER_Y,
|
|
185
|
+
SMALL_LETTER_Z,
|
|
186
|
+
]);
|
|
130
187
|
// Capital letters
|
|
131
188
|
const CAPITAL_LETTER_A = 'A';
|
|
189
|
+
const CAPITAL_LETTER_B = 'B';
|
|
190
|
+
const CAPITAL_LETTER_C = 'C';
|
|
191
|
+
const CAPITAL_LETTER_D = 'D';
|
|
192
|
+
const CAPITAL_LETTER_E = 'E';
|
|
193
|
+
const CAPITAL_LETTER_F = 'F';
|
|
194
|
+
const CAPITAL_LETTER_G = 'G';
|
|
195
|
+
const CAPITAL_LETTER_H = 'H';
|
|
196
|
+
const CAPITAL_LETTER_I = 'I';
|
|
197
|
+
const CAPITAL_LETTER_J = 'J';
|
|
198
|
+
const CAPITAL_LETTER_K = 'K';
|
|
199
|
+
const CAPITAL_LETTER_L = 'L';
|
|
200
|
+
const CAPITAL_LETTER_M = 'M';
|
|
201
|
+
const CAPITAL_LETTER_N = 'N';
|
|
202
|
+
const CAPITAL_LETTER_O = 'O';
|
|
203
|
+
const CAPITAL_LETTER_P = 'P';
|
|
204
|
+
const CAPITAL_LETTER_Q = 'Q';
|
|
205
|
+
const CAPITAL_LETTER_R = 'R';
|
|
206
|
+
const CAPITAL_LETTER_S = 'S';
|
|
207
|
+
const CAPITAL_LETTER_T = 'T';
|
|
208
|
+
const CAPITAL_LETTER_U = 'U';
|
|
209
|
+
const CAPITAL_LETTER_V = 'V';
|
|
210
|
+
const CAPITAL_LETTER_W = 'W';
|
|
211
|
+
const CAPITAL_LETTER_X = 'X';
|
|
212
|
+
const CAPITAL_LETTER_Y = 'Y';
|
|
132
213
|
const CAPITAL_LETTER_Z = 'Z';
|
|
214
|
+
/**
|
|
215
|
+
* Set of all capital letters.
|
|
216
|
+
*/
|
|
217
|
+
const CAPITAL_LETTERS = new Set([
|
|
218
|
+
CAPITAL_LETTER_A,
|
|
219
|
+
CAPITAL_LETTER_B,
|
|
220
|
+
CAPITAL_LETTER_C,
|
|
221
|
+
CAPITAL_LETTER_D,
|
|
222
|
+
CAPITAL_LETTER_E,
|
|
223
|
+
CAPITAL_LETTER_F,
|
|
224
|
+
CAPITAL_LETTER_G,
|
|
225
|
+
CAPITAL_LETTER_H,
|
|
226
|
+
CAPITAL_LETTER_I,
|
|
227
|
+
CAPITAL_LETTER_J,
|
|
228
|
+
CAPITAL_LETTER_K,
|
|
229
|
+
CAPITAL_LETTER_L,
|
|
230
|
+
CAPITAL_LETTER_M,
|
|
231
|
+
CAPITAL_LETTER_N,
|
|
232
|
+
CAPITAL_LETTER_O,
|
|
233
|
+
CAPITAL_LETTER_P,
|
|
234
|
+
CAPITAL_LETTER_Q,
|
|
235
|
+
CAPITAL_LETTER_R,
|
|
236
|
+
CAPITAL_LETTER_S,
|
|
237
|
+
CAPITAL_LETTER_T,
|
|
238
|
+
CAPITAL_LETTER_U,
|
|
239
|
+
CAPITAL_LETTER_V,
|
|
240
|
+
CAPITAL_LETTER_W,
|
|
241
|
+
CAPITAL_LETTER_X,
|
|
242
|
+
CAPITAL_LETTER_Y,
|
|
243
|
+
CAPITAL_LETTER_Z,
|
|
244
|
+
]);
|
|
133
245
|
// Numbers as strings
|
|
134
246
|
const NUMBER_0 = '0';
|
|
247
|
+
const NUMBER_1 = '1';
|
|
248
|
+
const NUMBER_2 = '2';
|
|
249
|
+
const NUMBER_3 = '3';
|
|
250
|
+
const NUMBER_4 = '4';
|
|
251
|
+
const NUMBER_5 = '5';
|
|
252
|
+
const NUMBER_6 = '6';
|
|
253
|
+
const NUMBER_7 = '7';
|
|
254
|
+
const NUMBER_8 = '8';
|
|
135
255
|
const NUMBER_9 = '9';
|
|
256
|
+
/**
|
|
257
|
+
* Set of all numbers as strings.
|
|
258
|
+
*/
|
|
259
|
+
const NUMBERS = new Set([
|
|
260
|
+
NUMBER_0,
|
|
261
|
+
NUMBER_1,
|
|
262
|
+
NUMBER_2,
|
|
263
|
+
NUMBER_3,
|
|
264
|
+
NUMBER_4,
|
|
265
|
+
NUMBER_5,
|
|
266
|
+
NUMBER_6,
|
|
267
|
+
NUMBER_7,
|
|
268
|
+
NUMBER_8,
|
|
269
|
+
NUMBER_9,
|
|
270
|
+
]);
|
|
136
271
|
const REGEX_MARKER = '/';
|
|
137
272
|
const ADG_SCRIPTLET_MASK = '//scriptlet';
|
|
138
273
|
const UBO_SCRIPTLET_MASK = 'js';
|
|
139
274
|
// Modifiers are separated by ",". For example: "script,domain=example.com"
|
|
140
275
|
const MODIFIERS_SEPARATOR = ',';
|
|
141
|
-
const MODIFIER_EXCEPTION_MARKER = '~';
|
|
142
276
|
const MODIFIER_ASSIGN_OPERATOR = '=';
|
|
143
|
-
const
|
|
277
|
+
const NEGATION_MARKER = '~';
|
|
278
|
+
/**
|
|
279
|
+
* The wildcard symbol — `*`.
|
|
280
|
+
*/
|
|
281
|
+
const WILDCARD$1 = ASTERISK;
|
|
144
282
|
/**
|
|
145
283
|
* Classic domain separator.
|
|
146
284
|
*
|
|
@@ -150,9 +288,9 @@ const DOMAIN_EXCEPTION_MARKER = '~';
|
|
|
150
288
|
* example.com,~example.org##.ads
|
|
151
289
|
* ```
|
|
152
290
|
*/
|
|
153
|
-
const
|
|
291
|
+
const COMMA_DOMAIN_LIST_SEPARATOR = ',';
|
|
154
292
|
/**
|
|
155
|
-
* Modifier domain
|
|
293
|
+
* Modifier separator for $app, $denyallow, $domain, $method.
|
|
156
294
|
*
|
|
157
295
|
* @example
|
|
158
296
|
* ```adblock
|
|
@@ -160,8 +298,7 @@ const CLASSIC_DOMAIN_SEPARATOR = ',';
|
|
|
160
298
|
* ads.js^$script,domains=example.com|~example.org
|
|
161
299
|
* ```
|
|
162
300
|
*/
|
|
163
|
-
const
|
|
164
|
-
const DOMAIN_LIST_TYPE = 'DomainList';
|
|
301
|
+
const PIPE_MODIFIER_SEPARATOR = '|';
|
|
165
302
|
const CSS_IMPORTANT = '!important';
|
|
166
303
|
const HINT_MARKER = '!+';
|
|
167
304
|
const HINT_MARKER_LEN = HINT_MARKER.length;
|
|
@@ -794,6 +931,27 @@ exports.RuleCategory = void 0;
|
|
|
794
931
|
*/
|
|
795
932
|
RuleCategory["Network"] = "Network";
|
|
796
933
|
})(exports.RuleCategory || (exports.RuleCategory = {}));
|
|
934
|
+
/**
|
|
935
|
+
* Represents similar types of modifiers values
|
|
936
|
+
* which may be separated by a comma `,` (only for DomainList) or a pipe `|`.
|
|
937
|
+
*/
|
|
938
|
+
var ListNodeType;
|
|
939
|
+
(function (ListNodeType) {
|
|
940
|
+
ListNodeType["AppList"] = "AppList";
|
|
941
|
+
ListNodeType["DomainList"] = "DomainList";
|
|
942
|
+
ListNodeType["MethodList"] = "MethodList";
|
|
943
|
+
ListNodeType["StealthOptionList"] = "StealthOptionList";
|
|
944
|
+
})(ListNodeType || (ListNodeType = {}));
|
|
945
|
+
/**
|
|
946
|
+
* Represents child items for {@link ListNodeType}.
|
|
947
|
+
*/
|
|
948
|
+
var ListItemNodeType;
|
|
949
|
+
(function (ListItemNodeType) {
|
|
950
|
+
ListItemNodeType["App"] = "App";
|
|
951
|
+
ListItemNodeType["Domain"] = "Domain";
|
|
952
|
+
ListItemNodeType["Method"] = "Method";
|
|
953
|
+
ListItemNodeType["StealthOption"] = "StealthOption";
|
|
954
|
+
})(ListItemNodeType || (ListItemNodeType = {}));
|
|
797
955
|
/**
|
|
798
956
|
* Represents possible comment types.
|
|
799
957
|
*/
|
|
@@ -2545,6 +2703,87 @@ class CommentRuleParser {
|
|
|
2545
2703
|
}
|
|
2546
2704
|
}
|
|
2547
2705
|
|
|
2706
|
+
/**
|
|
2707
|
+
* Prefixes for error messages which are used for parsing of value lists.
|
|
2708
|
+
*/
|
|
2709
|
+
const LIST_PARSE_ERROR_PREFIX = {
|
|
2710
|
+
EMPTY_ITEM: 'Empty value specified in the list',
|
|
2711
|
+
NO_MULTIPLE_NEGATION: 'Exception marker cannot be followed by another exception marker',
|
|
2712
|
+
NO_SEPARATOR_AFTER_NEGATION: 'Exception marker cannot be followed by a separator',
|
|
2713
|
+
NO_SEPARATOR_AT_THE_END: 'Value list cannot end with a separator',
|
|
2714
|
+
NO_WHITESPACE_AFTER_NEGATION: 'Exception marker cannot be followed by whitespace',
|
|
2715
|
+
};
|
|
2716
|
+
/**
|
|
2717
|
+
* Parses a `raw` modifier value which may be represented as a list of items separated by `separator`.
|
|
2718
|
+
* Needed for $app, $denyallow, $domain, $method.
|
|
2719
|
+
*
|
|
2720
|
+
* @param raw Raw modifier value.
|
|
2721
|
+
* @param separator Separator character.
|
|
2722
|
+
* @param loc Location of the modifier value.
|
|
2723
|
+
*
|
|
2724
|
+
* @returns List AST children — {@link App} | {@link Domain} | {@link Method} —
|
|
2725
|
+
* but with no `type` specified (see {@link ListItemNoType}).
|
|
2726
|
+
* @throws An {@link AdblockSyntaxError} if the list is syntactically invalid
|
|
2727
|
+
*
|
|
2728
|
+
* @example
|
|
2729
|
+
* - parses an app list — `com.example.app|Example.exe`
|
|
2730
|
+
* - parses a domain list — `example.com,example.org,~example.org` or `example.com|~example.org`
|
|
2731
|
+
* - parses a method list — `~post|~put`
|
|
2732
|
+
*/
|
|
2733
|
+
const parseListItems = (raw, separator, loc = defaultLocation) => {
|
|
2734
|
+
const rawListItems = [];
|
|
2735
|
+
// If the last character is a separator, then the list item is invalid
|
|
2736
|
+
// and no need to continue parsing
|
|
2737
|
+
const realEndIndex = StringUtils.skipWSBack(raw);
|
|
2738
|
+
if (raw[realEndIndex] === separator) {
|
|
2739
|
+
throw new AdblockSyntaxError(LIST_PARSE_ERROR_PREFIX.NO_SEPARATOR_AT_THE_END, locRange(loc, realEndIndex, realEndIndex + 1));
|
|
2740
|
+
}
|
|
2741
|
+
let offset = 0;
|
|
2742
|
+
// Skip whitespace before the list
|
|
2743
|
+
offset = StringUtils.skipWS(raw, offset);
|
|
2744
|
+
// Split list items by unescaped separators
|
|
2745
|
+
while (offset < raw.length) {
|
|
2746
|
+
// Skip whitespace before the list item
|
|
2747
|
+
offset = StringUtils.skipWS(raw, offset);
|
|
2748
|
+
let itemStart = offset;
|
|
2749
|
+
// Find the index of the first unescaped separator character
|
|
2750
|
+
const separatorStartIndex = StringUtils.findNextUnescapedCharacter(raw, separator, offset);
|
|
2751
|
+
const itemEnd = separatorStartIndex === -1
|
|
2752
|
+
? StringUtils.skipWSBack(raw) + 1
|
|
2753
|
+
: StringUtils.skipWSBack(raw, separatorStartIndex - 1) + 1;
|
|
2754
|
+
const exception = raw[itemStart] === NEGATION_MARKER;
|
|
2755
|
+
// Skip the exception marker
|
|
2756
|
+
if (exception) {
|
|
2757
|
+
itemStart += 1;
|
|
2758
|
+
// Exception marker cannot be followed by another exception marker
|
|
2759
|
+
if (raw[itemStart] === NEGATION_MARKER) {
|
|
2760
|
+
throw new AdblockSyntaxError(LIST_PARSE_ERROR_PREFIX.NO_MULTIPLE_NEGATION, locRange(loc, itemStart, itemStart + 1));
|
|
2761
|
+
}
|
|
2762
|
+
// Exception marker cannot be followed by a separator
|
|
2763
|
+
if (raw[itemStart] === separator) {
|
|
2764
|
+
throw new AdblockSyntaxError(LIST_PARSE_ERROR_PREFIX.NO_SEPARATOR_AFTER_NEGATION, locRange(loc, itemStart, itemStart + 1));
|
|
2765
|
+
}
|
|
2766
|
+
// Exception marker cannot be followed by whitespace
|
|
2767
|
+
if (StringUtils.isWhitespace(raw[itemStart])) {
|
|
2768
|
+
throw new AdblockSyntaxError(LIST_PARSE_ERROR_PREFIX.NO_WHITESPACE_AFTER_NEGATION, locRange(loc, itemStart, itemStart + 1));
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
// List item can't be empty
|
|
2772
|
+
if (itemStart === itemEnd) {
|
|
2773
|
+
throw new AdblockSyntaxError(LIST_PARSE_ERROR_PREFIX.EMPTY_ITEM, locRange(loc, itemStart, raw.length));
|
|
2774
|
+
}
|
|
2775
|
+
// Collect list item
|
|
2776
|
+
rawListItems.push({
|
|
2777
|
+
loc: locRange(loc, itemStart, itemEnd),
|
|
2778
|
+
value: raw.substring(itemStart, itemEnd),
|
|
2779
|
+
exception,
|
|
2780
|
+
});
|
|
2781
|
+
// Increment the offset to the next list item (or the end of the string)
|
|
2782
|
+
offset = separatorStartIndex === -1 ? raw.length : separatorStartIndex + 1;
|
|
2783
|
+
}
|
|
2784
|
+
return rawListItems;
|
|
2785
|
+
};
|
|
2786
|
+
|
|
2548
2787
|
/**
|
|
2549
2788
|
* `DomainListParser` is responsible for parsing a domain list.
|
|
2550
2789
|
*
|
|
@@ -2558,83 +2797,39 @@ class DomainListParser {
|
|
|
2558
2797
|
/**
|
|
2559
2798
|
* Parses a domain list, eg. `example.com,example.org,~example.org`
|
|
2560
2799
|
*
|
|
2561
|
-
* @param raw Raw domain list
|
|
2562
|
-
* @param separator Separator character
|
|
2563
|
-
* @param loc Location of the domain list
|
|
2564
|
-
*
|
|
2565
|
-
* @
|
|
2800
|
+
* @param raw Raw domain list.
|
|
2801
|
+
* @param separator Separator character.
|
|
2802
|
+
* @param loc Location of the domain list in the rule. If not set, the default location is used.
|
|
2803
|
+
*
|
|
2804
|
+
* @returns Domain list AST.
|
|
2805
|
+
* @throws An {@link AdblockSyntaxError} if the domain list is syntactically invalid.
|
|
2566
2806
|
*/
|
|
2567
|
-
static parse(raw, separator =
|
|
2568
|
-
const
|
|
2569
|
-
|
|
2807
|
+
static parse(raw, separator = COMMA_DOMAIN_LIST_SEPARATOR, loc = defaultLocation) {
|
|
2808
|
+
const rawItems = parseListItems(raw, separator, loc);
|
|
2809
|
+
const children = rawItems.map((rawListItem) => ({
|
|
2810
|
+
...rawListItem,
|
|
2811
|
+
type: ListItemNodeType.Domain,
|
|
2812
|
+
}));
|
|
2813
|
+
return {
|
|
2814
|
+
type: ListNodeType.DomainList,
|
|
2570
2815
|
loc: locRange(loc, 0, raw.length),
|
|
2571
2816
|
separator,
|
|
2572
|
-
children
|
|
2817
|
+
children,
|
|
2573
2818
|
};
|
|
2574
|
-
// If the last character is a separator, then the domain list is invalid
|
|
2575
|
-
// and no need to continue parsing
|
|
2576
|
-
const realEndIndex = StringUtils.skipWSBack(raw);
|
|
2577
|
-
if (raw[realEndIndex] === separator) {
|
|
2578
|
-
throw new AdblockSyntaxError('Domain list cannot end with a separator', locRange(loc, realEndIndex, realEndIndex + 1));
|
|
2579
|
-
}
|
|
2580
|
-
let offset = 0;
|
|
2581
|
-
// Skip whitespace before the domain list
|
|
2582
|
-
offset = StringUtils.skipWS(raw, offset);
|
|
2583
|
-
// Split domains by unescaped separators
|
|
2584
|
-
while (offset < raw.length) {
|
|
2585
|
-
// Skip whitespace before the domain
|
|
2586
|
-
offset = StringUtils.skipWS(raw, offset);
|
|
2587
|
-
let domainStart = offset;
|
|
2588
|
-
// Find the index of the first unescaped separator character
|
|
2589
|
-
const separatorStartIndex = StringUtils.findNextUnescapedCharacter(raw, separator, offset);
|
|
2590
|
-
const domainEnd = separatorStartIndex === -1
|
|
2591
|
-
? StringUtils.skipWSBack(raw) + 1
|
|
2592
|
-
: StringUtils.skipWSBack(raw, separatorStartIndex - 1) + 1;
|
|
2593
|
-
const exception = raw[domainStart] === DOMAIN_EXCEPTION_MARKER;
|
|
2594
|
-
// Skip the exception marker
|
|
2595
|
-
if (exception) {
|
|
2596
|
-
domainStart += 1;
|
|
2597
|
-
// Exception marker cannot be followed by another exception marker
|
|
2598
|
-
if (raw[domainStart] === DOMAIN_EXCEPTION_MARKER) {
|
|
2599
|
-
throw new AdblockSyntaxError('Exception marker cannot be followed by another exception marker', locRange(loc, domainStart, domainStart + 1));
|
|
2600
|
-
}
|
|
2601
|
-
// Exception marker cannot be followed by a separator
|
|
2602
|
-
if (raw[domainStart] === separator) {
|
|
2603
|
-
throw new AdblockSyntaxError('Exception marker cannot be followed by a separator', locRange(loc, domainStart, domainStart + 1));
|
|
2604
|
-
}
|
|
2605
|
-
// Exception marker cannot be followed by whitespace
|
|
2606
|
-
if (StringUtils.isWhitespace(raw[domainStart])) {
|
|
2607
|
-
throw new AdblockSyntaxError('Exception marker cannot be followed by whitespace', locRange(loc, domainStart, domainStart + 1));
|
|
2608
|
-
}
|
|
2609
|
-
}
|
|
2610
|
-
// Domain can't be empty
|
|
2611
|
-
if (domainStart === domainEnd) {
|
|
2612
|
-
throw new AdblockSyntaxError('Empty domain specified', locRange(loc, domainStart, raw.length));
|
|
2613
|
-
}
|
|
2614
|
-
// Add the domain to the result
|
|
2615
|
-
result.children.push({
|
|
2616
|
-
type: 'Domain',
|
|
2617
|
-
loc: locRange(loc, domainStart, domainEnd),
|
|
2618
|
-
value: raw.substring(domainStart, domainEnd),
|
|
2619
|
-
exception,
|
|
2620
|
-
});
|
|
2621
|
-
// Increment the offset to the next domain (or the end of the string)
|
|
2622
|
-
offset = separatorStartIndex === -1 ? raw.length : separatorStartIndex + 1;
|
|
2623
|
-
}
|
|
2624
|
-
return result;
|
|
2625
2819
|
}
|
|
2626
2820
|
/**
|
|
2627
2821
|
* Converts a domain list AST to a string.
|
|
2628
2822
|
*
|
|
2629
|
-
* @param ast Domain list AST
|
|
2630
|
-
*
|
|
2823
|
+
* @param ast Domain list AST.
|
|
2824
|
+
*
|
|
2825
|
+
* @returns Raw string.
|
|
2631
2826
|
*/
|
|
2632
2827
|
static generate(ast) {
|
|
2633
2828
|
const result = ast.children
|
|
2634
2829
|
.map(({ value, exception }) => {
|
|
2635
2830
|
let subresult = EMPTY;
|
|
2636
2831
|
if (exception) {
|
|
2637
|
-
subresult +=
|
|
2832
|
+
subresult += NEGATION_MARKER;
|
|
2638
2833
|
}
|
|
2639
2834
|
subresult += value.trim();
|
|
2640
2835
|
return subresult;
|
|
@@ -2668,8 +2863,8 @@ class ModifierParser {
|
|
|
2668
2863
|
const modifierStart = offset;
|
|
2669
2864
|
// Check if the modifier is an exception
|
|
2670
2865
|
let exception = false;
|
|
2671
|
-
if (raw[offset] ===
|
|
2672
|
-
offset +=
|
|
2866
|
+
if (raw[offset] === NEGATION_MARKER) {
|
|
2867
|
+
offset += NEGATION_MARKER.length;
|
|
2673
2868
|
exception = true;
|
|
2674
2869
|
}
|
|
2675
2870
|
// Skip whitespace after the exception marker (if any)
|
|
@@ -2733,7 +2928,7 @@ class ModifierParser {
|
|
|
2733
2928
|
static generate(modifier) {
|
|
2734
2929
|
let result = EMPTY;
|
|
2735
2930
|
if (modifier.exception) {
|
|
2736
|
-
result +=
|
|
2931
|
+
result += NEGATION_MARKER;
|
|
2737
2932
|
}
|
|
2738
2933
|
result += modifier.modifier.value;
|
|
2739
2934
|
if (modifier.value !== undefined) {
|
|
@@ -5581,6 +5776,102 @@ class RuleParser {
|
|
|
5581
5776
|
}
|
|
5582
5777
|
}
|
|
5583
5778
|
|
|
5779
|
+
/**
|
|
5780
|
+
* `AppListParser` is responsible for parsing an app list.
|
|
5781
|
+
*
|
|
5782
|
+
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#app-modifier}
|
|
5783
|
+
*/
|
|
5784
|
+
class AppListParser {
|
|
5785
|
+
/**
|
|
5786
|
+
* Parses an app list which items are separated by `|`,
|
|
5787
|
+
* e.g. `Example.exe|com.example.osx`.
|
|
5788
|
+
*
|
|
5789
|
+
* @param raw Raw app list
|
|
5790
|
+
* @param loc Location of the app list in the rule. If not set, the default location is used.
|
|
5791
|
+
*
|
|
5792
|
+
* @returns App list AST.
|
|
5793
|
+
* @throws An {@link AdblockSyntaxError} if the app list is syntactically invalid.
|
|
5794
|
+
*/
|
|
5795
|
+
static parse(raw, loc = defaultLocation) {
|
|
5796
|
+
const separator = PIPE_MODIFIER_SEPARATOR;
|
|
5797
|
+
const rawItems = parseListItems(raw, separator, loc);
|
|
5798
|
+
const children = rawItems.map((rawListItem) => ({
|
|
5799
|
+
...rawListItem,
|
|
5800
|
+
type: ListItemNodeType.App,
|
|
5801
|
+
}));
|
|
5802
|
+
return {
|
|
5803
|
+
type: ListNodeType.AppList,
|
|
5804
|
+
loc: locRange(loc, 0, raw.length),
|
|
5805
|
+
separator,
|
|
5806
|
+
children,
|
|
5807
|
+
};
|
|
5808
|
+
}
|
|
5809
|
+
}
|
|
5810
|
+
|
|
5811
|
+
/**
|
|
5812
|
+
* `MethodListParser` is responsible for parsing a method list.
|
|
5813
|
+
*
|
|
5814
|
+
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#method-modifier}
|
|
5815
|
+
*/
|
|
5816
|
+
class MethodListParser {
|
|
5817
|
+
/**
|
|
5818
|
+
* Parses a method list which items are separated by `|`,
|
|
5819
|
+
* e.g. `get|post|put`.
|
|
5820
|
+
*
|
|
5821
|
+
* @param raw Raw method list
|
|
5822
|
+
* @param loc Location of the method list in the rule. If not set, the default location is used.
|
|
5823
|
+
*
|
|
5824
|
+
* @returns Method list AST.
|
|
5825
|
+
* @throws An {@link AdblockSyntaxError} if the method list is syntactically invalid.
|
|
5826
|
+
*/
|
|
5827
|
+
static parse(raw, loc = defaultLocation) {
|
|
5828
|
+
const separator = PIPE_MODIFIER_SEPARATOR;
|
|
5829
|
+
const rawItems = parseListItems(raw, separator, loc);
|
|
5830
|
+
const children = rawItems.map((rawListItem) => ({
|
|
5831
|
+
...rawListItem,
|
|
5832
|
+
type: ListItemNodeType.Method,
|
|
5833
|
+
}));
|
|
5834
|
+
return {
|
|
5835
|
+
type: ListNodeType.MethodList,
|
|
5836
|
+
loc: locRange(loc, 0, raw.length),
|
|
5837
|
+
separator,
|
|
5838
|
+
children,
|
|
5839
|
+
};
|
|
5840
|
+
}
|
|
5841
|
+
}
|
|
5842
|
+
|
|
5843
|
+
/**
|
|
5844
|
+
* `StealthOptionListParser` is responsible for parsing a list of stealth options.
|
|
5845
|
+
*
|
|
5846
|
+
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#stealth-modifier}
|
|
5847
|
+
*/
|
|
5848
|
+
class StealthOptionListParser {
|
|
5849
|
+
/**
|
|
5850
|
+
* Parses a stealth option list which items are separated by `|`,
|
|
5851
|
+
* e.g. `dpi|ip`.
|
|
5852
|
+
*
|
|
5853
|
+
* @param raw Raw list of stealth options.
|
|
5854
|
+
* @param loc Location of the stealth option list in the rule. If not set, the default location is used.
|
|
5855
|
+
*
|
|
5856
|
+
* @returns Stealth option list AST.
|
|
5857
|
+
* @throws An {@link AdblockSyntaxError} if the stealth option list is syntactically invalid.
|
|
5858
|
+
*/
|
|
5859
|
+
static parse(raw, loc = defaultLocation) {
|
|
5860
|
+
const separator = PIPE_MODIFIER_SEPARATOR;
|
|
5861
|
+
const rawItems = parseListItems(raw, separator, loc);
|
|
5862
|
+
const children = rawItems.map((rawListItem) => ({
|
|
5863
|
+
...rawListItem,
|
|
5864
|
+
type: ListItemNodeType.StealthOption,
|
|
5865
|
+
}));
|
|
5866
|
+
return {
|
|
5867
|
+
type: ListNodeType.StealthOptionList,
|
|
5868
|
+
loc: locRange(loc, 0, raw.length),
|
|
5869
|
+
separator,
|
|
5870
|
+
children,
|
|
5871
|
+
};
|
|
5872
|
+
}
|
|
5873
|
+
}
|
|
5874
|
+
|
|
5584
5875
|
/**
|
|
5585
5876
|
* `FilterListParser` is responsible for parsing a whole adblock filter list (list of rules).
|
|
5586
5877
|
* It is a wrapper around `RuleParser` which parses each line separately.
|
|
@@ -5791,7 +6082,8 @@ var data$S = { adg_os_any:{ name:"app",
|
|
|
5791
6082
|
description:"The `$app` modifier lets you narrow the rule coverage down to a specific application or a list of applications.\nThe modifier's behavior and syntax perfectly match the corresponding basic rules `$app` modifier.",
|
|
5792
6083
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#app-modifier",
|
|
5793
6084
|
assignable:true,
|
|
5794
|
-
negatable:false
|
|
6085
|
+
negatable:false,
|
|
6086
|
+
value_format:"pipe_separated_apps" } };
|
|
5795
6087
|
|
|
5796
6088
|
var data$R = { adg_os_any:{ name:"badfilter",
|
|
5797
6089
|
description:"The rules with the `$badfilter` modifier disable other basic rules to which they refer. It means that\nthe text of the disabled rule should match the text of the `$badfilter` rule (without the `$badfilter` modifier).",
|
|
@@ -5852,7 +6144,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
5852
6144
|
assignable:true,
|
|
5853
6145
|
negatable:false,
|
|
5854
6146
|
value_optional:true,
|
|
5855
|
-
value_format:"
|
|
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,}" },
|
|
5856
6148
|
adg_ext_any:{ name:"csp",
|
|
5857
6149
|
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.",
|
|
5858
6150
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#csp-modifier",
|
|
@@ -5864,7 +6156,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
5864
6156
|
assignable:true,
|
|
5865
6157
|
negatable:false,
|
|
5866
6158
|
value_optional:true,
|
|
5867
|
-
value_format:"
|
|
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,}" },
|
|
5868
6160
|
abp_ext_any:{ name:"csp",
|
|
5869
6161
|
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.",
|
|
5870
6162
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293-How-to-write-filters#content-security-policies",
|
|
@@ -5874,7 +6166,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
5874
6166
|
assignable:true,
|
|
5875
6167
|
negatable:false,
|
|
5876
6168
|
value_optional:true,
|
|
5877
|
-
value_format:"
|
|
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,}" },
|
|
5878
6170
|
ubo_ext_any:{ name:"csp",
|
|
5879
6171
|
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.",
|
|
5880
6172
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#csp",
|
|
@@ -5886,7 +6178,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
5886
6178
|
assignable:true,
|
|
5887
6179
|
negatable:false,
|
|
5888
6180
|
value_optional:true,
|
|
5889
|
-
value_format:"
|
|
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,}" } };
|
|
5890
6182
|
|
|
5891
6183
|
var data$M = { adg_os_any:{ name:"denyallow",
|
|
5892
6184
|
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.",
|
|
@@ -5894,35 +6186,35 @@ var data$M = { adg_os_any:{ name:"denyallow",
|
|
|
5894
6186
|
conflicts:[ "to" ],
|
|
5895
6187
|
assignable:true,
|
|
5896
6188
|
negatable:false,
|
|
5897
|
-
value_format:"
|
|
6189
|
+
value_format:"pipe_separated_denyallow_domains" },
|
|
5898
6190
|
adg_ext_any:{ name:"denyallow",
|
|
5899
6191
|
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.",
|
|
5900
6192
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#denyallow-modifier",
|
|
5901
6193
|
conflicts:[ "to" ],
|
|
5902
6194
|
assignable:true,
|
|
5903
6195
|
negatable:false,
|
|
5904
|
-
value_format:"
|
|
6196
|
+
value_format:"pipe_separated_denyallow_domains" },
|
|
5905
6197
|
adg_cb_ios:{ name:"denyallow",
|
|
5906
6198
|
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.",
|
|
5907
6199
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#denyallow-modifier",
|
|
5908
6200
|
conflicts:[ "to" ],
|
|
5909
6201
|
assignable:true,
|
|
5910
6202
|
negatable:false,
|
|
5911
|
-
value_format:"
|
|
6203
|
+
value_format:"pipe_separated_denyallow_domains" },
|
|
5912
6204
|
adg_cb_safari:{ name:"denyallow",
|
|
5913
6205
|
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.",
|
|
5914
6206
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#denyallow-modifier",
|
|
5915
6207
|
conflicts:[ "to" ],
|
|
5916
6208
|
assignable:true,
|
|
5917
6209
|
negatable:false,
|
|
5918
|
-
value_format:"
|
|
6210
|
+
value_format:"pipe_separated_denyallow_domains" },
|
|
5919
6211
|
ubo_ext_any:{ name:"denyallow",
|
|
5920
6212
|
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.",
|
|
5921
6213
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#denyallow",
|
|
5922
6214
|
conflicts:[ "to" ],
|
|
5923
6215
|
assignable:true,
|
|
5924
6216
|
negatable:false,
|
|
5925
|
-
value_format:"
|
|
6217
|
+
value_format:"pipe_separated_denyallow_domains" } };
|
|
5926
6218
|
|
|
5927
6219
|
var data$L = { adg_os_any:{ name:"document",
|
|
5928
6220
|
description:"The rule corresponds to the main frame document requests,\ni.e. HTML documents that are loaded in the browser tab.",
|
|
@@ -6138,17 +6430,17 @@ var data$C = { adg_os_any:{ name:"header",
|
|
|
6138
6430
|
description:"The `$header` modifier allows matching the HTTP response\nhaving a specific header with (optionally) a specific value.",
|
|
6139
6431
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#header-modifier",
|
|
6140
6432
|
assignable:true,
|
|
6141
|
-
value_format:"
|
|
6433
|
+
value_format:"(?xi)\n ^\n # header name\n [\\w-]+\n (\n :\n # header value: string or regexp\n (\\w+|\\/.+\\/)\n )?" },
|
|
6142
6434
|
adg_ext_any:{ name:"header",
|
|
6143
6435
|
description:"The `$header` modifier allows matching the HTTP response\nhaving a specific header with (optionally) a specific value.",
|
|
6144
6436
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#header-modifier",
|
|
6145
6437
|
assignable:true,
|
|
6146
|
-
value_format:"
|
|
6438
|
+
value_format:"(?xi)\n ^\n # header name\n [\\w-]+\n (\n :\n # header value: string or regexp\n (\\w+|\\/.+\\/)\n )?" },
|
|
6147
6439
|
ubo_ext_any:{ name:"header",
|
|
6148
6440
|
description:"The `$header` modifier allows matching the HTTP response\nhaving a specific header with (optionally) a specific value.",
|
|
6149
6441
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#header",
|
|
6150
6442
|
assignable:true,
|
|
6151
|
-
value_format:"
|
|
6443
|
+
value_format:"(?xi)\n ^\n # header name\n [\\w-]+\n (\n :\n # header value: string or regexp\n (\\w+|\\/.+\\/)\n )?" } };
|
|
6152
6444
|
|
|
6153
6445
|
var data$B = { adg_os_any:{ name:"hls",
|
|
6154
6446
|
description:"The `$hls` rules modify the response of a matching request.\nThey are intended as a convenient way to remove segments from HLS playlists (RFC 8216).",
|
|
@@ -6163,7 +6455,8 @@ var data$B = { adg_os_any:{ name:"hls",
|
|
|
6163
6455
|
inverse_conflicts:true,
|
|
6164
6456
|
assignable:true,
|
|
6165
6457
|
negatable:false,
|
|
6166
|
-
|
|
6458
|
+
value_optional:true,
|
|
6459
|
+
value_format:"(?xi)\n (\n # string pattern\n \\w+\n # or regexp pattern\n |\n # TODO: improve regexp pattern to invalidate unescaped `/`, `$`, and `,`\n \\/.+\\/\n # options\n ([ti]*)?\n )" } };
|
|
6167
6460
|
|
|
6168
6461
|
var data$A = { adg_any:{ name:"image",
|
|
6169
6462
|
description:"The rule corresponds to images requests.",
|
|
@@ -6268,7 +6561,8 @@ var data$v = { adg_os_any:{ name:"jsonprune",
|
|
|
6268
6561
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#jsonprune-modifier",
|
|
6269
6562
|
assignable:true,
|
|
6270
6563
|
negatable:false,
|
|
6271
|
-
|
|
6564
|
+
value_optional:true,
|
|
6565
|
+
value_format:"(?xi)\n ^\n # the expression always starts with a dollar sign (for root)\n # which should be escaped\n \\\\\n \\$\n \\.?\n # TODO: improve the expression to invalidate unescaped `$` and `,`\n .+\n $" } };
|
|
6272
6566
|
|
|
6273
6567
|
var data$u = { adg_any:{ name:"match-case",
|
|
6274
6568
|
description:"This modifier defines a rule which applies only to addresses that match the case.\nDefault rules are case-insensitive.",
|
|
@@ -6295,19 +6589,19 @@ var data$s = { adg_os_any:{ name:"method",
|
|
|
6295
6589
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#method-modifier",
|
|
6296
6590
|
negatable:false,
|
|
6297
6591
|
assignable:true,
|
|
6298
|
-
value_format:"
|
|
6592
|
+
value_format:"pipe_separated_methods" },
|
|
6299
6593
|
adg_ext_any:{ name:"method",
|
|
6300
6594
|
description:"This modifier limits the rule scope to requests that use the specified set of HTTP methods.\nNegated methods are allowed.",
|
|
6301
6595
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#method-modifier",
|
|
6302
6596
|
negatable:false,
|
|
6303
6597
|
assignable:true,
|
|
6304
|
-
value_format:"
|
|
6598
|
+
value_format:"pipe_separated_methods" },
|
|
6305
6599
|
ubo_ext_any:{ name:"method",
|
|
6306
6600
|
description:"This modifier limits the rule scope to requests that use the specified set of HTTP methods.\nNegated methods are allowed.",
|
|
6307
6601
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#method",
|
|
6308
6602
|
negatable:false,
|
|
6309
6603
|
assignable:true,
|
|
6310
|
-
value_format:"
|
|
6604
|
+
value_format:"pipe_separated_methods" } };
|
|
6311
6605
|
|
|
6312
6606
|
var data$r = { adg_os_any:{ name:"mp4",
|
|
6313
6607
|
description:"As a response to blocked request AdGuard returns a short video placeholder.\nRules with `$mp4` are still supported and being converted into `$redirect=noopmp4-1s` now\nbut the support shall be removed in the future.",
|
|
@@ -6391,7 +6685,7 @@ var data$l = { adg_os_any:{ name:"permissions",
|
|
|
6391
6685
|
inverse_conflicts:true,
|
|
6392
6686
|
assignable:true,
|
|
6393
6687
|
negatable:false,
|
|
6394
|
-
value_format:"(?x)\n
|
|
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 $" } };
|
|
6395
6689
|
|
|
6396
6690
|
var data$k = { adg_any:{ name:"ping",
|
|
6397
6691
|
description:"The rule corresponds to requests caused by either navigator.sendBeacon() or the ping attribute on links.",
|
|
@@ -6519,14 +6813,14 @@ var data$g = { adg_os_any:{ name:"redirect",
|
|
|
6519
6813
|
assignable:true,
|
|
6520
6814
|
negatable:false,
|
|
6521
6815
|
value_optional:true,
|
|
6522
|
-
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 )?$" },
|
|
6816
|
+
value_format:"(?x)\n ^(\n 1x1-transparent\\.gif|\n 2x2-transparent\\.png|\n 3x2-transparent\\.png|\n 32x32-transparent\\.png|\n noopframe|\n noopcss|\n noopjs|\n noopjson|\n nooptext|\n empty|\n noopvmap-1\\.0|\n noopvast-2\\.0|\n noopvast-3\\.0|\n noopvast-4\\.0|\n noopmp3-0\\.1s|\n noopmp4-1s|\n amazon-apstag|\n ati-smarttag|\n didomi-loader|\n fingerprintjs2|\n fingerprintjs3|\n gemius|\n google-analytics-ga|\n google-analytics|\n 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 )?$" },
|
|
6523
6817
|
adg_ext_any:{ name:"redirect",
|
|
6524
6818
|
description:"Used to redirect web requests to a local \"resource\".",
|
|
6525
6819
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#redirect-modifier",
|
|
6526
6820
|
assignable:true,
|
|
6527
6821
|
negatable:false,
|
|
6528
6822
|
value_optional:true,
|
|
6529
|
-
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 )?$" },
|
|
6823
|
+
value_format:"(?x)\n ^(\n 1x1-transparent\\.gif|\n 2x2-transparent\\.png|\n 3x2-transparent\\.png|\n 32x32-transparent\\.png|\n noopframe|\n noopcss|\n noopjs|\n noopjson|\n nooptext|\n empty|\n noopvmap-1\\.0|\n noopvast-2\\.0|\n noopvast-3\\.0|\n noopvast-4\\.0|\n noopmp3-0\\.1s|\n noopmp4-1s|\n amazon-apstag|\n ati-smarttag|\n didomi-loader|\n fingerprintjs2|\n fingerprintjs3|\n gemius|\n google-analytics-ga|\n google-analytics|\n 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 )?$" },
|
|
6530
6824
|
ubo_ext_any:{ name:"redirect",
|
|
6531
6825
|
description:"Used to redirect web requests to a local \"resource\".",
|
|
6532
6826
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#redirect",
|
|
@@ -6602,20 +6896,20 @@ var data$e = { adg_os_any:{ name:"removeparam",
|
|
|
6602
6896
|
assignable:true,
|
|
6603
6897
|
negatable:false,
|
|
6604
6898
|
value_optional:true,
|
|
6605
|
-
value_format:"
|
|
6899
|
+
value_format:"(?xi)\n (\n # string pattern\n \\w+\n # or regexp pattern\n |\n \\/.+\\/\n # flags\n ([gimuy]+)?\n )" },
|
|
6606
6900
|
adg_ext_any:{ name:"removeparam",
|
|
6607
6901
|
description:"Rules with the `$removeparam` modifier are intended to strip query parameters from requests' URLs.",
|
|
6608
6902
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#removeparam-modifier",
|
|
6609
6903
|
assignable:true,
|
|
6610
6904
|
negatable:false,
|
|
6611
6905
|
value_optional:true,
|
|
6612
|
-
value_format:"
|
|
6906
|
+
value_format:"(?xi)\n (\n # string pattern\n \\w+\n # or regexp pattern\n |\n \\/.+\\/\n # flags\n ([gimuy]+)?\n )" },
|
|
6613
6907
|
ubo_ext_any:{ name:"removeparam",
|
|
6614
6908
|
description:"Rules with the `$removeparam` modifier are intended to strip query parameters from requests' URLs.",
|
|
6615
6909
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#removeparam",
|
|
6616
6910
|
assignable:true,
|
|
6617
6911
|
negatable:false,
|
|
6618
|
-
value_format:"
|
|
6912
|
+
value_format:"(?xi)\n (\n # string pattern\n \\w+\n # or regexp pattern\n |\n \\/.+\\/\n # flags\n ([gimuy]+)?\n )" } };
|
|
6619
6913
|
|
|
6620
6914
|
var data$d = { adg_os_any:{ name:"replace",
|
|
6621
6915
|
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.",
|
|
@@ -6635,7 +6929,7 @@ var data$d = { adg_os_any:{ name:"replace",
|
|
|
6635
6929
|
inverse_conflicts:true,
|
|
6636
6930
|
assignable:true,
|
|
6637
6931
|
negatable:false,
|
|
6638
|
-
value_format:"
|
|
6932
|
+
value_format:"(?xi)\n ^\n \\/\n # the regexp to match by\n (.+)\n # separator\n \\/\n # replacement\n (.+)?\n \\/\n # flags\n ([gimuy]*)?\n $" },
|
|
6639
6933
|
adg_ext_firefox:{ name:"replace",
|
|
6640
6934
|
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.",
|
|
6641
6935
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#replace-modifier",
|
|
@@ -6653,7 +6947,7 @@ var data$d = { adg_os_any:{ name:"replace",
|
|
|
6653
6947
|
inverse_conflicts:true,
|
|
6654
6948
|
assignable:true,
|
|
6655
6949
|
negatable:false,
|
|
6656
|
-
value_format:"
|
|
6950
|
+
value_format:"(?xi)\n ^\n \\/\n # the regexp to match by\n (.+)\n # separator\n \\/\n # replacement\n (.+)?\n \\/\n # flags\n ([gimuy]*)?\n $" } };
|
|
6657
6951
|
|
|
6658
6952
|
var data$c = { adg_any:{ name:"script",
|
|
6659
6953
|
description:"The rule corresponds to script requests, e.g. javascript, vbscript.",
|
|
@@ -6697,7 +6991,7 @@ var data$a = { adg_os_any:{ name:"stealth",
|
|
|
6697
6991
|
negatable:false,
|
|
6698
6992
|
exception_only:true,
|
|
6699
6993
|
value_optional:true,
|
|
6700
|
-
value_format:"
|
|
6994
|
+
value_format:"pipe_separated_stealth_options" },
|
|
6701
6995
|
adg_ext_chrome:{ name:"stealth",
|
|
6702
6996
|
description:"Disables the Stealth Mode module for all corresponding pages and requests.",
|
|
6703
6997
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#stealth-modifier",
|
|
@@ -6705,7 +6999,7 @@ var data$a = { adg_os_any:{ name:"stealth",
|
|
|
6705
6999
|
negatable:false,
|
|
6706
7000
|
exception_only:true,
|
|
6707
7001
|
value_optional:true,
|
|
6708
|
-
value_format:"
|
|
7002
|
+
value_format:"pipe_separated_stealth_options" },
|
|
6709
7003
|
adg_ext_firefox:{ name:"stealth",
|
|
6710
7004
|
description:"Disables the Stealth Mode module for all corresponding pages and requests.",
|
|
6711
7005
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#stealth-modifier",
|
|
@@ -6713,7 +7007,7 @@ var data$a = { adg_os_any:{ name:"stealth",
|
|
|
6713
7007
|
negatable:false,
|
|
6714
7008
|
exception_only:true,
|
|
6715
7009
|
value_optional:true,
|
|
6716
|
-
value_format:"
|
|
7010
|
+
value_format:"pipe_separated_stealth_options" },
|
|
6717
7011
|
adg_ext_opera:{ name:"stealth",
|
|
6718
7012
|
description:"Disables the Stealth Mode module for all corresponding pages and requests.",
|
|
6719
7013
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#stealth-modifier",
|
|
@@ -6721,7 +7015,7 @@ var data$a = { adg_os_any:{ name:"stealth",
|
|
|
6721
7015
|
negatable:false,
|
|
6722
7016
|
exception_only:true,
|
|
6723
7017
|
value_optional:true,
|
|
6724
|
-
value_format:"
|
|
7018
|
+
value_format:"pipe_separated_stealth_options" },
|
|
6725
7019
|
adg_ext_edge:{ name:"stealth",
|
|
6726
7020
|
description:"Disables the Stealth Mode module for all corresponding pages and requests.",
|
|
6727
7021
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#stealth-modifier",
|
|
@@ -6729,7 +7023,7 @@ var data$a = { adg_os_any:{ name:"stealth",
|
|
|
6729
7023
|
negatable:false,
|
|
6730
7024
|
exception_only:true,
|
|
6731
7025
|
value_optional:true,
|
|
6732
|
-
value_format:"
|
|
7026
|
+
value_format:"pipe_separated_stealth_options" } };
|
|
6733
7027
|
|
|
6734
7028
|
var data$9 = { ubo_any:{ name:"strict1p",
|
|
6735
7029
|
description:"This new `strict1p` option can check for strict partyness.\nFor example, a network request qualifies as 1st-party if both the context and the request share the same hostname.",
|
|
@@ -6932,8 +7226,6 @@ var SpecificKey;
|
|
|
6932
7226
|
SpecificKey["Negatable"] = "negatable";
|
|
6933
7227
|
SpecificKey["BlockOnly"] = "block_only";
|
|
6934
7228
|
SpecificKey["ExceptionOnly"] = "exception_only";
|
|
6935
|
-
// TODO: consider removing this field
|
|
6936
|
-
// and handle whether the value is optional by `value_format`. AG-24028
|
|
6937
7229
|
SpecificKey["ValueOptional"] = "value_optional";
|
|
6938
7230
|
SpecificKey["ValueFormat"] = "value_format";
|
|
6939
7231
|
// TODO: following fields should be handled later
|
|
@@ -6997,25 +7289,123 @@ const getModifiersData = () => {
|
|
|
6997
7289
|
return dataMap;
|
|
6998
7290
|
};
|
|
6999
7291
|
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7292
|
+
/**
|
|
7293
|
+
* Prefixes for different adblockers to describe the platform-specific modifiers data
|
|
7294
|
+
* stored in the yaml files.
|
|
7295
|
+
*/
|
|
7296
|
+
const BLOCKER_PREFIX = {
|
|
7297
|
+
[exports.AdblockSyntax.Adg]: 'adg_',
|
|
7298
|
+
[exports.AdblockSyntax.Ubo]: 'ubo_',
|
|
7299
|
+
[exports.AdblockSyntax.Abp]: 'abp_',
|
|
7300
|
+
};
|
|
7301
|
+
/**
|
|
7302
|
+
* Set of all allowed characters for app name except the dot `.`.
|
|
7303
|
+
*/
|
|
7304
|
+
const APP_NAME_ALLOWED_CHARS = new Set([
|
|
7305
|
+
...CAPITAL_LETTERS,
|
|
7306
|
+
...SMALL_LETTERS,
|
|
7307
|
+
...NUMBERS,
|
|
7308
|
+
UNDERSCORE,
|
|
7309
|
+
]);
|
|
7310
|
+
/**
|
|
7311
|
+
* Allowed methods for $method modifier.
|
|
7312
|
+
*
|
|
7313
|
+
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#method-modifier}
|
|
7314
|
+
*/
|
|
7315
|
+
const ALLOWED_METHODS = new Set([
|
|
7316
|
+
'connect',
|
|
7317
|
+
'delete',
|
|
7318
|
+
'get',
|
|
7319
|
+
'head',
|
|
7320
|
+
'options',
|
|
7321
|
+
'patch',
|
|
7322
|
+
'post',
|
|
7323
|
+
'put',
|
|
7324
|
+
'trace',
|
|
7325
|
+
]);
|
|
7326
|
+
/**
|
|
7327
|
+
* Allowed stealth options for $stealth modifier.
|
|
7328
|
+
*
|
|
7329
|
+
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#stealth-modifier}
|
|
7330
|
+
*/
|
|
7331
|
+
const ALLOWED_STEALTH_OPTIONS = new Set([
|
|
7332
|
+
'searchqueries',
|
|
7333
|
+
'donottrack',
|
|
7334
|
+
'3p-cookie',
|
|
7335
|
+
'1p-cookie',
|
|
7336
|
+
'3p-cache',
|
|
7337
|
+
'3p-auth',
|
|
7338
|
+
'webrtc',
|
|
7339
|
+
'push',
|
|
7340
|
+
'location',
|
|
7341
|
+
'flash',
|
|
7342
|
+
'java',
|
|
7343
|
+
'referrer',
|
|
7344
|
+
'useragent',
|
|
7345
|
+
'ip',
|
|
7346
|
+
'xclientdata',
|
|
7347
|
+
'dpi',
|
|
7348
|
+
]);
|
|
7349
|
+
/**
|
|
7350
|
+
* Prefixes for error messages used in modifier validation.
|
|
7351
|
+
*/
|
|
7352
|
+
const VALIDATION_ERROR_PREFIX = {
|
|
7004
7353
|
BLOCK_ONLY: 'Only blocking rules may contain the modifier',
|
|
7005
7354
|
EXCEPTION_ONLY: 'Only exception rules may contain the modifier',
|
|
7006
|
-
|
|
7007
|
-
VALUE_REQUIRED: 'Value is required for the modifier',
|
|
7008
|
-
VALUE_FORBIDDEN: 'Value is not allowed for the modifier',
|
|
7355
|
+
INVALID_LIST_VALUES: 'Invalid values for the modifier',
|
|
7009
7356
|
INVALID_NOOP: 'Invalid noop modifier',
|
|
7357
|
+
MIXED_NEGATIONS: 'Simultaneous usage of negated and not negated values is forbidden for the modifier',
|
|
7358
|
+
NOT_EXISTENT: 'Non-existent modifier',
|
|
7359
|
+
NOT_NEGATABLE_MODIFIER: 'Non-negatable modifier',
|
|
7360
|
+
NOT_NEGATABLE_VALUE: 'Values cannot be negated for the modifier',
|
|
7361
|
+
NOT_SUPPORTED: 'The adblocker does not support the modifier',
|
|
7362
|
+
REMOVED: 'Removed and no longer supported modifier',
|
|
7363
|
+
VALUE_FORBIDDEN: 'Value is not allowed for the modifier',
|
|
7364
|
+
VALUE_INVALID: 'Value is invalid for the modifier',
|
|
7365
|
+
VALUE_REQUIRED: 'Value is required for the modifier',
|
|
7366
|
+
};
|
|
7367
|
+
/**
|
|
7368
|
+
* Prefixes for error messages related to issues in the source YAML files' data.
|
|
7369
|
+
*/
|
|
7370
|
+
const SOURCE_DATA_ERROR_PREFIX = {
|
|
7371
|
+
INVALID_VALUE_FORMAT_REGEXP: "Invalid regular expression in 'value_format' for the modifier",
|
|
7372
|
+
NO_DEPRECATION_MESSAGE: "Property 'deprecation_message' is required for the 'deprecated' modifier",
|
|
7373
|
+
NO_VALUE_FORMAT_FOR_ASSIGNABLE: "Property 'value_format' should be specified for the assignable modifier",
|
|
7010
7374
|
};
|
|
7011
7375
|
|
|
7012
7376
|
/**
|
|
7013
|
-
*
|
|
7377
|
+
* Validates the noop modifier (i.e. only underscores).
|
|
7378
|
+
*
|
|
7379
|
+
* @param value Value of the modifier.
|
|
7380
|
+
*
|
|
7381
|
+
* @returns True if the modifier is valid, false otherwise.
|
|
7014
7382
|
*/
|
|
7015
|
-
const
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7383
|
+
const isValidNoopModifier = (value) => {
|
|
7384
|
+
return value.split('').every((char) => char === UNDERSCORE);
|
|
7385
|
+
};
|
|
7386
|
+
/**
|
|
7387
|
+
* Returns invalid validation result with given error message.
|
|
7388
|
+
*
|
|
7389
|
+
* @param error Error message.
|
|
7390
|
+
*
|
|
7391
|
+
* @returns Validation result `{ valid: false, error }`.
|
|
7392
|
+
*/
|
|
7393
|
+
const getInvalidValidationResult = (error) => {
|
|
7394
|
+
return {
|
|
7395
|
+
valid: false,
|
|
7396
|
+
error,
|
|
7397
|
+
};
|
|
7398
|
+
};
|
|
7399
|
+
/**
|
|
7400
|
+
* Returns invalid validation result which uses {@link VALIDATION_ERROR_PREFIX.VALUE_REQUIRED} as prefix
|
|
7401
|
+
* and specifies the given `modifierName` in the error message.
|
|
7402
|
+
*
|
|
7403
|
+
* @param modifierName Modifier name.
|
|
7404
|
+
*
|
|
7405
|
+
* @returns Validation result `{ valid: false, error }`.
|
|
7406
|
+
*/
|
|
7407
|
+
const getValueRequiredValidationResult = (modifierName) => {
|
|
7408
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_REQUIRED}: '${modifierName}'`);
|
|
7019
7409
|
};
|
|
7020
7410
|
/**
|
|
7021
7411
|
* Collects names and aliases for all supported modifiers.
|
|
@@ -7067,116 +7457,620 @@ const getSpecificBlockerData = (modifiersData, blockerPrefix, modifierName) => {
|
|
|
7067
7457
|
});
|
|
7068
7458
|
return specificBlockerData;
|
|
7069
7459
|
};
|
|
7460
|
+
|
|
7070
7461
|
/**
|
|
7071
|
-
*
|
|
7072
|
-
*
|
|
7073
|
-
* @param error Error message.
|
|
7074
|
-
*
|
|
7075
|
-
* @returns Validation result `{ ok: false, error }`.
|
|
7462
|
+
* @file Utility functions for domain and hostname validation.
|
|
7076
7463
|
*/
|
|
7077
|
-
const getInvalidValidationResult = (error) => {
|
|
7078
|
-
return {
|
|
7079
|
-
ok: false,
|
|
7080
|
-
error,
|
|
7081
|
-
};
|
|
7082
|
-
};
|
|
7083
7464
|
/**
|
|
7084
|
-
*
|
|
7085
|
-
* is it supported by the blocker, deprecated, assignable, negatable, etc.
|
|
7465
|
+
* Marker for a wildcard top-level domain — `.*`.
|
|
7086
7466
|
*
|
|
7087
|
-
* @
|
|
7088
|
-
*
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
*
|
|
7467
|
+
* @example
|
|
7468
|
+
* `example.*` — matches with any TLD, e.g. `example.org`, `example.com`, etc.
|
|
7469
|
+
*/
|
|
7470
|
+
const WILDCARD_TLD = DOT + WILDCARD$1;
|
|
7471
|
+
/**
|
|
7472
|
+
* Marker for a wildcard subdomain — `*.`.
|
|
7093
7473
|
*
|
|
7094
|
-
* @
|
|
7474
|
+
* @example
|
|
7475
|
+
* `*.example.org` — matches with any subdomain, e.g. `foo.example.org` or `bar.example.org`
|
|
7095
7476
|
*/
|
|
7096
|
-
const
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.NOT_SUPPORTED}: '${modifierName}'`);
|
|
7110
|
-
}
|
|
7111
|
-
// e.g. 'object-subrequest'
|
|
7112
|
-
if (specificBlockerData.removed) {
|
|
7113
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.REMOVED}: '${modifierName}'`);
|
|
7114
|
-
}
|
|
7115
|
-
if (specificBlockerData.deprecated) {
|
|
7116
|
-
if (!specificBlockerData.deprecation_message) {
|
|
7117
|
-
throw new Error('Deprecation notice is required for deprecated modifier');
|
|
7477
|
+
const WILDCARD_SUBDOMAIN = WILDCARD$1 + DOT;
|
|
7478
|
+
class DomainUtils {
|
|
7479
|
+
/**
|
|
7480
|
+
* Check if the input is a valid domain or hostname.
|
|
7481
|
+
*
|
|
7482
|
+
* @param domain Domain to check
|
|
7483
|
+
* @returns `true` if the domain is valid, `false` otherwise
|
|
7484
|
+
*/
|
|
7485
|
+
static isValidDomainOrHostname(domain) {
|
|
7486
|
+
let domainToCheck = domain;
|
|
7487
|
+
// Wildcard-only domain, typically a generic rule
|
|
7488
|
+
if (domainToCheck === WILDCARD$1) {
|
|
7489
|
+
return true;
|
|
7118
7490
|
}
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
}
|
|
7124
|
-
if (specificBlockerData.block_only && isException) {
|
|
7125
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.BLOCK_ONLY}: '${modifierName}'`);
|
|
7126
|
-
}
|
|
7127
|
-
if (specificBlockerData.exception_only && !isException) {
|
|
7128
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.EXCEPTION_ONLY}: '${modifierName}'`);
|
|
7129
|
-
}
|
|
7130
|
-
// e.g. '~domain=example.com'
|
|
7131
|
-
if (!specificBlockerData.negatable && modifier.exception) {
|
|
7132
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.NOT_NEGATABLE}: '${modifierName}'`);
|
|
7133
|
-
}
|
|
7134
|
-
// e.g. 'domain'
|
|
7135
|
-
if (specificBlockerData.assignable) {
|
|
7136
|
-
/**
|
|
7137
|
-
* Some assignable modifiers can be used without a value,
|
|
7138
|
-
* e.g. '@@||example.com^$cookie'.
|
|
7139
|
-
*/
|
|
7140
|
-
if (!modifier.value
|
|
7141
|
-
// value should be specified if it is not optional
|
|
7142
|
-
&& !specificBlockerData.value_optional) {
|
|
7143
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.VALUE_REQUIRED}: '${modifierName}'`);
|
|
7491
|
+
// https://adguard.com/kb/general/ad-filtering/create-own-filters/#wildcard-for-tld
|
|
7492
|
+
if (domainToCheck.endsWith(WILDCARD_TLD)) {
|
|
7493
|
+
// Remove the wildcard TLD
|
|
7494
|
+
domainToCheck = domainToCheck.substring(0, domainToCheck.length - WILDCARD_TLD.length);
|
|
7144
7495
|
}
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.VALUE_FORBIDDEN}: '${modifierName}'`);
|
|
7496
|
+
if (domainToCheck.startsWith(WILDCARD_SUBDOMAIN)) {
|
|
7497
|
+
// Remove the wildcard subdomain
|
|
7498
|
+
domainToCheck = domainToCheck.substring(WILDCARD_SUBDOMAIN.length);
|
|
7499
|
+
}
|
|
7500
|
+
// Parse the domain with tldts
|
|
7501
|
+
const tldtsResult = tldts.parse(domainToCheck);
|
|
7502
|
+
// Check if the domain is valid
|
|
7503
|
+
return domainToCheck === tldtsResult.domain || domainToCheck === tldtsResult.hostname;
|
|
7154
7504
|
}
|
|
7155
|
-
|
|
7156
|
-
|
|
7505
|
+
}
|
|
7506
|
+
|
|
7157
7507
|
/**
|
|
7158
|
-
*
|
|
7159
|
-
*
|
|
7160
|
-
* @param modifiersData Parsed all modifiers data map.
|
|
7161
|
-
* @param blockerPrefix Prefix of the adblocker, e.g. 'adg_', 'ubo_', or 'abp_'.
|
|
7162
|
-
* @param modifier Parsed modifier AST node.
|
|
7163
|
-
*
|
|
7164
|
-
* @returns Documentation URL or `null` if not found.
|
|
7508
|
+
* @file Utility functions for working with quotes
|
|
7165
7509
|
*/
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7510
|
+
/**
|
|
7511
|
+
* Possible quote types for scriptlet parameters
|
|
7512
|
+
*/
|
|
7513
|
+
exports.QuoteType = void 0;
|
|
7514
|
+
(function (QuoteType) {
|
|
7515
|
+
/**
|
|
7516
|
+
* No quotes at all
|
|
7517
|
+
*/
|
|
7518
|
+
QuoteType["None"] = "none";
|
|
7519
|
+
/**
|
|
7520
|
+
* Single quotes (`'`)
|
|
7521
|
+
*/
|
|
7522
|
+
QuoteType["Single"] = "single";
|
|
7523
|
+
/**
|
|
7524
|
+
* Double quotes (`"`)
|
|
7525
|
+
*/
|
|
7526
|
+
QuoteType["Double"] = "double";
|
|
7527
|
+
})(exports.QuoteType || (exports.QuoteType = {}));
|
|
7528
|
+
/**
|
|
7529
|
+
* Utility functions for working with quotes
|
|
7530
|
+
*/
|
|
7531
|
+
class QuoteUtils {
|
|
7532
|
+
/**
|
|
7533
|
+
* Escape all unescaped occurrences of the character
|
|
7534
|
+
*
|
|
7535
|
+
* @param string String to escape
|
|
7536
|
+
* @param char Character to escape
|
|
7537
|
+
* @returns Escaped string
|
|
7538
|
+
*/
|
|
7539
|
+
static escapeUnescapedOccurrences(string, char) {
|
|
7540
|
+
let result = EMPTY;
|
|
7541
|
+
for (let i = 0; i < string.length; i += 1) {
|
|
7542
|
+
if (string[i] === char && (i === 0 || string[i - 1] !== ESCAPE_CHARACTER)) {
|
|
7543
|
+
result += ESCAPE_CHARACTER;
|
|
7544
|
+
}
|
|
7545
|
+
result += string[i];
|
|
7546
|
+
}
|
|
7547
|
+
return result;
|
|
7548
|
+
}
|
|
7549
|
+
/**
|
|
7550
|
+
* Unescape all single escaped occurrences of the character
|
|
7551
|
+
*
|
|
7552
|
+
* @param string String to unescape
|
|
7553
|
+
* @param char Character to unescape
|
|
7554
|
+
* @returns Unescaped string
|
|
7555
|
+
*/
|
|
7556
|
+
static unescapeSingleEscapedOccurrences(string, char) {
|
|
7557
|
+
let result = EMPTY;
|
|
7558
|
+
for (let i = 0; i < string.length; i += 1) {
|
|
7559
|
+
if (string[i] === char
|
|
7560
|
+
&& string[i - 1] === ESCAPE_CHARACTER
|
|
7561
|
+
&& (i === 1 || string[i - 2] !== ESCAPE_CHARACTER)) {
|
|
7562
|
+
result = result.slice(0, -1);
|
|
7563
|
+
}
|
|
7564
|
+
result += string[i];
|
|
7565
|
+
}
|
|
7566
|
+
return result;
|
|
7567
|
+
}
|
|
7568
|
+
/**
|
|
7569
|
+
* Get quote type of the string
|
|
7570
|
+
*
|
|
7571
|
+
* @param string String to check
|
|
7572
|
+
* @returns Quote type of the string
|
|
7573
|
+
*/
|
|
7574
|
+
static getStringQuoteType(string) {
|
|
7575
|
+
// Don't check 1-character strings to avoid false positives
|
|
7576
|
+
if (string.length > 1) {
|
|
7577
|
+
if (string.startsWith(SINGLE_QUOTE) && string.endsWith(SINGLE_QUOTE)) {
|
|
7578
|
+
return exports.QuoteType.Single;
|
|
7579
|
+
}
|
|
7580
|
+
if (string.startsWith(DOUBLE_QUOTE) && string.endsWith(DOUBLE_QUOTE)) {
|
|
7581
|
+
return exports.QuoteType.Double;
|
|
7582
|
+
}
|
|
7583
|
+
}
|
|
7584
|
+
return exports.QuoteType.None;
|
|
7585
|
+
}
|
|
7586
|
+
/**
|
|
7587
|
+
* Set quote type of the string
|
|
7588
|
+
*
|
|
7589
|
+
* @param string String to set quote type of
|
|
7590
|
+
* @param quoteType Quote type to set
|
|
7591
|
+
* @returns String with the specified quote type
|
|
7592
|
+
*/
|
|
7593
|
+
static setStringQuoteType(string, quoteType) {
|
|
7594
|
+
const actualQuoteType = QuoteUtils.getStringQuoteType(string);
|
|
7595
|
+
switch (quoteType) {
|
|
7596
|
+
case exports.QuoteType.None:
|
|
7597
|
+
if (actualQuoteType === exports.QuoteType.Single) {
|
|
7598
|
+
return QuoteUtils.escapeUnescapedOccurrences(string.slice(1, -1), SINGLE_QUOTE);
|
|
7599
|
+
}
|
|
7600
|
+
if (actualQuoteType === exports.QuoteType.Double) {
|
|
7601
|
+
return QuoteUtils.escapeUnescapedOccurrences(string.slice(1, -1), DOUBLE_QUOTE);
|
|
7602
|
+
}
|
|
7603
|
+
return string;
|
|
7604
|
+
case exports.QuoteType.Single:
|
|
7605
|
+
if (actualQuoteType === exports.QuoteType.None) {
|
|
7606
|
+
return SINGLE_QUOTE + QuoteUtils.escapeUnescapedOccurrences(string, SINGLE_QUOTE) + SINGLE_QUOTE;
|
|
7607
|
+
}
|
|
7608
|
+
if (actualQuoteType === exports.QuoteType.Double) {
|
|
7609
|
+
return SINGLE_QUOTE
|
|
7610
|
+
+ QuoteUtils.escapeUnescapedOccurrences(QuoteUtils.unescapeSingleEscapedOccurrences(string.slice(1, -1), DOUBLE_QUOTE), SINGLE_QUOTE) + SINGLE_QUOTE;
|
|
7611
|
+
}
|
|
7612
|
+
return string;
|
|
7613
|
+
case exports.QuoteType.Double:
|
|
7614
|
+
if (actualQuoteType === exports.QuoteType.None) {
|
|
7615
|
+
return DOUBLE_QUOTE + QuoteUtils.escapeUnescapedOccurrences(string, DOUBLE_QUOTE) + DOUBLE_QUOTE;
|
|
7616
|
+
}
|
|
7617
|
+
if (actualQuoteType !== exports.QuoteType.Double) {
|
|
7618
|
+
// eslint-disable-next-line max-len
|
|
7619
|
+
return DOUBLE_QUOTE
|
|
7620
|
+
+ QuoteUtils.escapeUnescapedOccurrences(QuoteUtils.unescapeSingleEscapedOccurrences(string.slice(1, -1), SINGLE_QUOTE), DOUBLE_QUOTE) + DOUBLE_QUOTE;
|
|
7621
|
+
}
|
|
7622
|
+
return string;
|
|
7623
|
+
default:
|
|
7624
|
+
return string;
|
|
7625
|
+
}
|
|
7626
|
+
}
|
|
7627
|
+
/**
|
|
7628
|
+
* Removes bounding quotes from a string, if any
|
|
7629
|
+
*
|
|
7630
|
+
* @param string Input string
|
|
7631
|
+
* @returns String without quotes
|
|
7632
|
+
*/
|
|
7633
|
+
static removeQuotes(string) {
|
|
7634
|
+
if (
|
|
7635
|
+
// We should check for string length to avoid false positives
|
|
7636
|
+
string.length > 1
|
|
7637
|
+
&& (string[0] === SINGLE_QUOTE || string[0] === DOUBLE_QUOTE)
|
|
7638
|
+
&& string[0] === string[string.length - 1]) {
|
|
7639
|
+
return string.slice(1, -1);
|
|
7640
|
+
}
|
|
7641
|
+
return string;
|
|
7642
|
+
}
|
|
7643
|
+
/**
|
|
7644
|
+
* Wraps given `strings` with `quote` (defaults to single quote `'`)
|
|
7645
|
+
* and joins them with `separator` (defaults to comma+space `, `).
|
|
7646
|
+
*
|
|
7647
|
+
* @param strings Strings to quote and join.
|
|
7648
|
+
* @param quoteType Quote to use.
|
|
7649
|
+
* @param separator Separator to use.
|
|
7650
|
+
*
|
|
7651
|
+
* @returns String with joined items.
|
|
7652
|
+
*
|
|
7653
|
+
* @example
|
|
7654
|
+
* ['abc', 'def']: strings[] -> "'abc', 'def'": string
|
|
7655
|
+
*/
|
|
7656
|
+
static quoteAndJoinStrings(strings, quoteType = exports.QuoteType.Single, separator = `${COMMA}${SPACE}`) {
|
|
7657
|
+
return strings
|
|
7658
|
+
.map((s) => QuoteUtils.setStringQuoteType(s, quoteType))
|
|
7659
|
+
.join(separator);
|
|
7660
|
+
}
|
|
7661
|
+
}
|
|
7662
|
+
|
|
7663
|
+
/**
|
|
7664
|
+
* Pre-defined available validators for modifiers with custom `value_format`.
|
|
7665
|
+
*/
|
|
7666
|
+
var CustomValueFormatValidatorName;
|
|
7667
|
+
(function (CustomValueFormatValidatorName) {
|
|
7668
|
+
CustomValueFormatValidatorName["App"] = "pipe_separated_apps";
|
|
7669
|
+
// there are some differences between $domain and $denyallow
|
|
7670
|
+
CustomValueFormatValidatorName["DenyAllow"] = "pipe_separated_denyallow_domains";
|
|
7671
|
+
CustomValueFormatValidatorName["Domain"] = "pipe_separated_domains";
|
|
7672
|
+
CustomValueFormatValidatorName["Method"] = "pipe_separated_methods";
|
|
7673
|
+
CustomValueFormatValidatorName["StealthOption"] = "pipe_separated_stealth_options";
|
|
7674
|
+
})(CustomValueFormatValidatorName || (CustomValueFormatValidatorName = {}));
|
|
7675
|
+
/**
|
|
7676
|
+
* Checks whether the `chunk` of app name (which if splitted by dot `.`) is valid.
|
|
7677
|
+
* Only letters, numbers, and underscore `_` are allowed.
|
|
7678
|
+
*
|
|
7679
|
+
* @param chunk Chunk of app name to check.
|
|
7680
|
+
*
|
|
7681
|
+
* @returns True if the `chunk` is valid part of app name, false otherwise.
|
|
7682
|
+
*/
|
|
7683
|
+
const isValidAppNameChunk = (chunk) => {
|
|
7684
|
+
// e.g. 'Example..exe'
|
|
7685
|
+
if (chunk.length === 0) {
|
|
7686
|
+
return false;
|
|
7687
|
+
}
|
|
7688
|
+
for (let i = 0; i < chunk.length; i += 1) {
|
|
7689
|
+
const char = chunk[i];
|
|
7690
|
+
if (!APP_NAME_ALLOWED_CHARS.has(char)) {
|
|
7691
|
+
return false;
|
|
7692
|
+
}
|
|
7693
|
+
}
|
|
7694
|
+
return true;
|
|
7169
7695
|
};
|
|
7170
7696
|
/**
|
|
7171
|
-
*
|
|
7697
|
+
* Checks whether the given `value` is valid app name as $app modifier value.
|
|
7172
7698
|
*
|
|
7173
|
-
* @param value
|
|
7699
|
+
* @param value App name to check.
|
|
7174
7700
|
*
|
|
7175
|
-
* @returns True if the
|
|
7701
|
+
* @returns True if the `value` is valid app name, false otherwise.
|
|
7176
7702
|
*/
|
|
7177
|
-
const
|
|
7178
|
-
|
|
7703
|
+
const isValidAppModifierValue = (value) => {
|
|
7704
|
+
// $app modifier does not support wildcard tld
|
|
7705
|
+
// https://adguard.app/kb/general/ad-filtering/create-own-filters/#app-modifier
|
|
7706
|
+
if (value.includes(WILDCARD$1)) {
|
|
7707
|
+
return false;
|
|
7708
|
+
}
|
|
7709
|
+
return value
|
|
7710
|
+
.split(DOT)
|
|
7711
|
+
.every((chunk) => isValidAppNameChunk(chunk));
|
|
7712
|
+
};
|
|
7713
|
+
/**
|
|
7714
|
+
* Checks whether the given `value` is valid HTTP method as $method modifier value.
|
|
7715
|
+
*
|
|
7716
|
+
* @param value Method to check.
|
|
7717
|
+
*
|
|
7718
|
+
* @returns True if the `value` is valid HTTP method, false otherwise.
|
|
7719
|
+
*/
|
|
7720
|
+
const isValidMethodModifierValue = (value) => {
|
|
7721
|
+
return ALLOWED_METHODS.has(value);
|
|
7722
|
+
};
|
|
7723
|
+
/**
|
|
7724
|
+
* Checks whether the given `value` is valid option as $stealth modifier value.
|
|
7725
|
+
*
|
|
7726
|
+
* @param value Stealth option to check.
|
|
7727
|
+
*
|
|
7728
|
+
* @returns True if the `value` is valid stealth option, false otherwise.
|
|
7729
|
+
*/
|
|
7730
|
+
const isValidStealthModifierValue = (value) => {
|
|
7731
|
+
return ALLOWED_STEALTH_OPTIONS.has(value);
|
|
7732
|
+
};
|
|
7733
|
+
/**
|
|
7734
|
+
* Checks whether the given `value` is valid domain as $denyallow modifier value.
|
|
7735
|
+
* Important: wildcard tld are not supported, compared to $domain.
|
|
7736
|
+
*
|
|
7737
|
+
* @param value Value to check.
|
|
7738
|
+
*
|
|
7739
|
+
* @returns True if the `value` is valid domain and does not contain wildcard `*`, false otherwise.
|
|
7740
|
+
*/
|
|
7741
|
+
const isValidDenyAllowModifierValue = (value) => {
|
|
7742
|
+
// $denyallow modifier does not support wildcard tld
|
|
7743
|
+
// https://adguard.app/kb/general/ad-filtering/create-own-filters/#denyallow-modifier
|
|
7744
|
+
// but here we are simply checking whether the value contains wildcard `*`, not ends with `.*`
|
|
7745
|
+
if (value.includes(WILDCARD$1)) {
|
|
7746
|
+
return false;
|
|
7747
|
+
}
|
|
7748
|
+
// TODO: add cache for domains validation
|
|
7749
|
+
return DomainUtils.isValidDomainOrHostname(value);
|
|
7750
|
+
};
|
|
7751
|
+
/**
|
|
7752
|
+
* Checks whether the given `value` is valid domain as $domain modifier value.
|
|
7753
|
+
*
|
|
7754
|
+
* @param value Value to check.
|
|
7755
|
+
*
|
|
7756
|
+
* @returns True if the `value` is valid domain, false otherwise.
|
|
7757
|
+
*/
|
|
7758
|
+
const isValidDomainModifierValue = (value) => {
|
|
7759
|
+
// TODO: add cache for domains validation
|
|
7760
|
+
return DomainUtils.isValidDomainOrHostname(value);
|
|
7761
|
+
};
|
|
7762
|
+
/**
|
|
7763
|
+
* Checks whether the all list items' exceptions are `false`.
|
|
7764
|
+
* Those items which `exception` is `true` is to be specified in the validation result error message.
|
|
7765
|
+
*
|
|
7766
|
+
* @param modifierName Modifier name.
|
|
7767
|
+
* @param listItems List items to check.
|
|
7768
|
+
*
|
|
7769
|
+
* @returns Validation result.
|
|
7770
|
+
*/
|
|
7771
|
+
const customNoNegatedListItemsValidator = (modifierName, listItems) => {
|
|
7772
|
+
const negatedValues = [];
|
|
7773
|
+
listItems.forEach((listItem) => {
|
|
7774
|
+
if (listItem.exception) {
|
|
7775
|
+
negatedValues.push(listItem.value);
|
|
7776
|
+
}
|
|
7777
|
+
});
|
|
7778
|
+
if (negatedValues.length > 0) {
|
|
7779
|
+
const valuesToStr = QuoteUtils.quoteAndJoinStrings(negatedValues);
|
|
7780
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NOT_NEGATABLE_VALUE}: '${modifierName}': ${valuesToStr}`);
|
|
7781
|
+
}
|
|
7782
|
+
return { valid: true };
|
|
7783
|
+
};
|
|
7784
|
+
/**
|
|
7785
|
+
* Checks whether the all list items' exceptions are consistent,
|
|
7786
|
+
* i.e. all items are either negated or not negated.
|
|
7787
|
+
*
|
|
7788
|
+
* The `exception` value of the first item is used as a reference, and all other items are checked against it.
|
|
7789
|
+
* Those items which `exception` is not consistent with the first item
|
|
7790
|
+
* is to be specified in the validation result error message.
|
|
7791
|
+
*
|
|
7792
|
+
* @see {@link https://adguard.com/kb/general/ad-filtering/create-own-filters/#method-modifier}
|
|
7793
|
+
*
|
|
7794
|
+
* @param modifierName Modifier name.
|
|
7795
|
+
* @param listItems List items to check.
|
|
7796
|
+
*
|
|
7797
|
+
* @returns Validation result.
|
|
7798
|
+
*/
|
|
7799
|
+
const customConsistentExceptionsValidator = (modifierName, listItems) => {
|
|
7800
|
+
const firstException = listItems[0].exception;
|
|
7801
|
+
const nonConsistentItemValues = [];
|
|
7802
|
+
listItems.forEach((listItem) => {
|
|
7803
|
+
if (listItem.exception !== firstException) {
|
|
7804
|
+
nonConsistentItemValues.push(listItem.value);
|
|
7805
|
+
}
|
|
7806
|
+
});
|
|
7807
|
+
if (nonConsistentItemValues.length > 0) {
|
|
7808
|
+
const valuesToStr = QuoteUtils.quoteAndJoinStrings(nonConsistentItemValues);
|
|
7809
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.MIXED_NEGATIONS}: '${modifierName}': ${valuesToStr}`);
|
|
7810
|
+
}
|
|
7811
|
+
return { valid: true };
|
|
7812
|
+
};
|
|
7813
|
+
/**
|
|
7814
|
+
* Checks whether the given `modifier` value is valid.
|
|
7815
|
+
* Supposed to validate the value of modifiers which values are lists separated by pipe `|` —
|
|
7816
|
+
* $app, $domain, $denyallow, $method.
|
|
7817
|
+
*
|
|
7818
|
+
* @param modifier Modifier AST node.
|
|
7819
|
+
* @param listParser Parser function for parsing modifier value
|
|
7820
|
+
* which is supposed to be a list separated by pipe `|`.
|
|
7821
|
+
* @param isValidListItem Predicate function for checking of modifier's list item validity,
|
|
7822
|
+
* e.g. $denyallow modifier does not support wildcard tld, but $domain does.
|
|
7823
|
+
* @param customListValidator Optional; custom validator for specific modifier,
|
|
7824
|
+
* e.g. $denyallow modifier does not support negated domains.
|
|
7825
|
+
*
|
|
7826
|
+
* @returns Result of modifier domains validation.
|
|
7827
|
+
*/
|
|
7828
|
+
const validateListItemsModifier = (modifier, listParser, isValidListItem, customListValidator) => {
|
|
7829
|
+
const modifierName = modifier.modifier.value;
|
|
7830
|
+
const defaultInvalidValueResult = getValueRequiredValidationResult(modifierName);
|
|
7831
|
+
if (!modifier.value?.value) {
|
|
7832
|
+
return defaultInvalidValueResult;
|
|
7833
|
+
}
|
|
7834
|
+
let theList;
|
|
7835
|
+
try {
|
|
7836
|
+
theList = listParser(modifier.value.value, PIPE_MODIFIER_SEPARATOR);
|
|
7837
|
+
}
|
|
7838
|
+
catch (e) {
|
|
7839
|
+
if (e instanceof AdblockSyntaxError) {
|
|
7840
|
+
return {
|
|
7841
|
+
valid: false,
|
|
7842
|
+
error: e.message,
|
|
7843
|
+
};
|
|
7844
|
+
}
|
|
7845
|
+
return defaultInvalidValueResult;
|
|
7846
|
+
}
|
|
7847
|
+
const invalidListItems = [];
|
|
7848
|
+
theList.children.forEach((item) => {
|
|
7849
|
+
// different validators are used for $denyallow and $domain modifiers
|
|
7850
|
+
// because of different requirements and restrictions
|
|
7851
|
+
if (!isValidListItem(item.value)) {
|
|
7852
|
+
invalidListItems.push(item.value);
|
|
7853
|
+
}
|
|
7854
|
+
});
|
|
7855
|
+
if (invalidListItems.length > 0) {
|
|
7856
|
+
const itemsToStr = QuoteUtils.quoteAndJoinStrings(invalidListItems);
|
|
7857
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.INVALID_LIST_VALUES}: '${modifierName}': ${itemsToStr}`);
|
|
7858
|
+
}
|
|
7859
|
+
// IMPORTANT: run custom validator after all other checks
|
|
7860
|
+
// Some lists should be fully checked, not just the list items:
|
|
7861
|
+
// e.g. Safari does not support allowed and disallowed domains for $domain in the same list
|
|
7862
|
+
// or domains cannot be negated for $denyallow modifier
|
|
7863
|
+
if (customListValidator) {
|
|
7864
|
+
return customListValidator(modifierName, theList.children);
|
|
7865
|
+
}
|
|
7866
|
+
return { valid: true };
|
|
7867
|
+
};
|
|
7868
|
+
/**
|
|
7869
|
+
* Validates 'pipe_separated_apps' custom value format.
|
|
7870
|
+
* Used for $app modifier.
|
|
7871
|
+
*
|
|
7872
|
+
* @param modifier Modifier AST node.
|
|
7873
|
+
*
|
|
7874
|
+
* @returns Validation result.
|
|
7875
|
+
*/
|
|
7876
|
+
const validatePipeSeparatedApps = (modifier) => {
|
|
7877
|
+
return validateListItemsModifier(modifier, (raw) => AppListParser.parse(raw), isValidAppModifierValue);
|
|
7878
|
+
};
|
|
7879
|
+
/**
|
|
7880
|
+
* Validates 'pipe_separated_denyallow_domains' custom value format.
|
|
7881
|
+
* Used for $denyallow modifier.
|
|
7882
|
+
*
|
|
7883
|
+
* @param modifier Modifier AST node.
|
|
7884
|
+
*
|
|
7885
|
+
* @returns Validation result.
|
|
7886
|
+
*/
|
|
7887
|
+
const validatePipeSeparatedDenyAllowDomains = (modifier) => {
|
|
7888
|
+
return validateListItemsModifier(modifier, DomainListParser.parse, isValidDenyAllowModifierValue, customNoNegatedListItemsValidator);
|
|
7889
|
+
};
|
|
7890
|
+
/**
|
|
7891
|
+
* Validates 'pipe_separated_domains' custom value format.
|
|
7892
|
+
* Used for $domains modifier.
|
|
7893
|
+
*
|
|
7894
|
+
* @param modifier Modifier AST node.
|
|
7895
|
+
*
|
|
7896
|
+
* @returns Validation result.
|
|
7897
|
+
*/
|
|
7898
|
+
const validatePipeSeparatedDomains = (modifier) => {
|
|
7899
|
+
return validateListItemsModifier(modifier, DomainListParser.parse, isValidDomainModifierValue);
|
|
7900
|
+
};
|
|
7901
|
+
/**
|
|
7902
|
+
* Validates 'pipe_separated_methods' custom value format.
|
|
7903
|
+
* Used for $method modifier.
|
|
7904
|
+
*
|
|
7905
|
+
* @param modifier Modifier AST node.
|
|
7906
|
+
*
|
|
7907
|
+
* @returns Validation result.
|
|
7908
|
+
*/
|
|
7909
|
+
const validatePipeSeparatedMethods = (modifier) => {
|
|
7910
|
+
return validateListItemsModifier(modifier, (raw) => MethodListParser.parse(raw), isValidMethodModifierValue, customConsistentExceptionsValidator);
|
|
7911
|
+
};
|
|
7912
|
+
/**
|
|
7913
|
+
* Validates 'pipe_separated_stealth_options' custom value format.
|
|
7914
|
+
* Used for $stealth modifier.
|
|
7915
|
+
*
|
|
7916
|
+
* @param modifier Modifier AST node.
|
|
7917
|
+
*
|
|
7918
|
+
* @returns Validation result.
|
|
7919
|
+
*/
|
|
7920
|
+
const validatePipeSeparatedStealthOptions = (modifier) => {
|
|
7921
|
+
return validateListItemsModifier(modifier, (raw) => StealthOptionListParser.parse(raw), isValidStealthModifierValue, customNoNegatedListItemsValidator);
|
|
7922
|
+
};
|
|
7923
|
+
/**
|
|
7924
|
+
* Map of all available pre-defined validators for modifiers with custom `value_format`.
|
|
7925
|
+
*/
|
|
7926
|
+
const CUSTOM_VALUE_FORMAT_MAP = {
|
|
7927
|
+
[CustomValueFormatValidatorName.App]: validatePipeSeparatedApps,
|
|
7928
|
+
[CustomValueFormatValidatorName.DenyAllow]: validatePipeSeparatedDenyAllowDomains,
|
|
7929
|
+
[CustomValueFormatValidatorName.Domain]: validatePipeSeparatedDomains,
|
|
7930
|
+
[CustomValueFormatValidatorName.Method]: validatePipeSeparatedMethods,
|
|
7931
|
+
[CustomValueFormatValidatorName.StealthOption]: validatePipeSeparatedStealthOptions,
|
|
7932
|
+
};
|
|
7933
|
+
/**
|
|
7934
|
+
* Returns whether the given `valueFormat` is a valid custom value format validator name.
|
|
7935
|
+
*
|
|
7936
|
+
* @param valueFormat Value format for the modifier.
|
|
7937
|
+
*
|
|
7938
|
+
* @returns True if `valueFormat` is a supported pre-defined value format validator name, false otherwise.
|
|
7939
|
+
*/
|
|
7940
|
+
const isCustomValueFormatValidator = (valueFormat) => {
|
|
7941
|
+
return Object.keys(CUSTOM_VALUE_FORMAT_MAP).includes(valueFormat);
|
|
7942
|
+
};
|
|
7943
|
+
/**
|
|
7944
|
+
* Checks whether the value for given `modifier` is valid.
|
|
7945
|
+
*
|
|
7946
|
+
* @param modifier Modifier AST node.
|
|
7947
|
+
* @param valueFormat Value format for the modifier.
|
|
7948
|
+
*
|
|
7949
|
+
* @returns Validation result.
|
|
7950
|
+
*/
|
|
7951
|
+
const validateValue = (modifier, valueFormat) => {
|
|
7952
|
+
if (isCustomValueFormatValidator(valueFormat)) {
|
|
7953
|
+
const validator = CUSTOM_VALUE_FORMAT_MAP[valueFormat];
|
|
7954
|
+
return validator(modifier);
|
|
7955
|
+
}
|
|
7956
|
+
const modifierName = modifier.modifier.value;
|
|
7957
|
+
if (!modifier.value?.value) {
|
|
7958
|
+
return getValueRequiredValidationResult(modifierName);
|
|
7959
|
+
}
|
|
7960
|
+
let xRegExp;
|
|
7961
|
+
try {
|
|
7962
|
+
xRegExp = XRegExp(valueFormat);
|
|
7963
|
+
}
|
|
7964
|
+
catch (e) {
|
|
7965
|
+
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.INVALID_VALUE_FORMAT_REGEXP}: '${modifierName}'`);
|
|
7966
|
+
}
|
|
7967
|
+
const isValid = xRegExp.test(modifier.value?.value);
|
|
7968
|
+
if (!isValid) {
|
|
7969
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}'`);
|
|
7970
|
+
}
|
|
7971
|
+
return { valid: true };
|
|
7179
7972
|
};
|
|
7973
|
+
|
|
7974
|
+
/**
|
|
7975
|
+
* @file Validator for modifiers.
|
|
7976
|
+
*/
|
|
7977
|
+
/**
|
|
7978
|
+
* Fully checks whether the given `modifier` valid for given blocker `syntax`:
|
|
7979
|
+
* is it supported by the blocker, deprecated, assignable, negatable, etc.
|
|
7980
|
+
*
|
|
7981
|
+
* @param modifiersData Parsed all modifiers data map.
|
|
7982
|
+
* @param syntax Adblock syntax to check the modifier for.
|
|
7983
|
+
* 'Common' is not supported, it should be specific — 'AdGuard', 'uBlockOrigin', or 'AdblockPlus'.
|
|
7984
|
+
* @param modifier Parsed modifier AST node.
|
|
7985
|
+
* @param isException Whether the modifier is used in exception rule.
|
|
7986
|
+
* Needed to check whether the modifier is allowed only in blocking or exception rules.
|
|
7987
|
+
*
|
|
7988
|
+
* @returns Result of modifier validation.
|
|
7989
|
+
*/
|
|
7990
|
+
const validateForSpecificSyntax = (modifiersData, syntax, modifier, isException) => {
|
|
7991
|
+
if (syntax === exports.AdblockSyntax.Common) {
|
|
7992
|
+
throw new Error(`Syntax should be specific, '${exports.AdblockSyntax.Common}' is not supported`);
|
|
7993
|
+
}
|
|
7994
|
+
const modifierName = modifier.modifier.value;
|
|
7995
|
+
const blockerPrefix = BLOCKER_PREFIX[syntax];
|
|
7996
|
+
if (!blockerPrefix) {
|
|
7997
|
+
throw new Error(`Unknown syntax: ${syntax}`);
|
|
7998
|
+
}
|
|
7999
|
+
// needed for validation of negation, assignment, etc.
|
|
8000
|
+
const specificBlockerData = getSpecificBlockerData(modifiersData, blockerPrefix, modifierName);
|
|
8001
|
+
// if no specific blocker data is found
|
|
8002
|
+
if (!specificBlockerData) {
|
|
8003
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NOT_SUPPORTED}: '${modifierName}'`);
|
|
8004
|
+
}
|
|
8005
|
+
// e.g. 'object-subrequest'
|
|
8006
|
+
if (specificBlockerData[SpecificKey.Removed]) {
|
|
8007
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.REMOVED}: '${modifierName}'`);
|
|
8008
|
+
}
|
|
8009
|
+
if (specificBlockerData[SpecificKey.Deprecated]) {
|
|
8010
|
+
if (!specificBlockerData[SpecificKey.DeprecationMessage]) {
|
|
8011
|
+
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.NO_DEPRECATION_MESSAGE}: '${modifierName}'`);
|
|
8012
|
+
}
|
|
8013
|
+
// prepare the message which is multiline in the yaml file
|
|
8014
|
+
const warn = specificBlockerData[SpecificKey.DeprecationMessage].replace(NEWLINE, SPACE);
|
|
8015
|
+
return {
|
|
8016
|
+
valid: true,
|
|
8017
|
+
warn,
|
|
8018
|
+
};
|
|
8019
|
+
}
|
|
8020
|
+
if (specificBlockerData[SpecificKey.BlockOnly] && isException) {
|
|
8021
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.BLOCK_ONLY}: '${modifierName}'`);
|
|
8022
|
+
}
|
|
8023
|
+
if (specificBlockerData[SpecificKey.ExceptionOnly] && !isException) {
|
|
8024
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.EXCEPTION_ONLY}: '${modifierName}'`);
|
|
8025
|
+
}
|
|
8026
|
+
// e.g. '~domain=example.com'
|
|
8027
|
+
if (!specificBlockerData[SpecificKey.Negatable] && modifier.exception) {
|
|
8028
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NOT_NEGATABLE_MODIFIER}: '${modifierName}'`);
|
|
8029
|
+
}
|
|
8030
|
+
// e.g. 'domain'
|
|
8031
|
+
if (specificBlockerData[SpecificKey.Assignable]) {
|
|
8032
|
+
if (!modifier.value) {
|
|
8033
|
+
/**
|
|
8034
|
+
* Some assignable modifiers can be used without a value,
|
|
8035
|
+
* e.g. '@@||example.com^$cookie'.
|
|
8036
|
+
*/
|
|
8037
|
+
if (specificBlockerData[SpecificKey.ValueOptional]) {
|
|
8038
|
+
return { valid: true };
|
|
8039
|
+
}
|
|
8040
|
+
// for other assignable modifiers the value is required
|
|
8041
|
+
return getValueRequiredValidationResult(modifierName);
|
|
8042
|
+
}
|
|
8043
|
+
/**
|
|
8044
|
+
* TODO: consider to return `{ valid: true, warn: 'Modifier value may be specified' }` (???)
|
|
8045
|
+
* for $stealth modifier without a value
|
|
8046
|
+
* but only after the extension will support value for $stealth:
|
|
8047
|
+
* https://github.com/AdguardTeam/AdguardBrowserExtension/issues/2107
|
|
8048
|
+
*/
|
|
8049
|
+
if (!specificBlockerData[SpecificKey.ValueFormat]) {
|
|
8050
|
+
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.NO_VALUE_FORMAT_FOR_ASSIGNABLE}: '${modifierName}'`);
|
|
8051
|
+
}
|
|
8052
|
+
return validateValue(modifier, specificBlockerData[SpecificKey.ValueFormat]);
|
|
8053
|
+
}
|
|
8054
|
+
if (modifier?.value) {
|
|
8055
|
+
// e.g. 'third-party=true'
|
|
8056
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_FORBIDDEN}: '${modifierName}'`);
|
|
8057
|
+
}
|
|
8058
|
+
return { valid: true };
|
|
8059
|
+
};
|
|
8060
|
+
/**
|
|
8061
|
+
* Returns documentation URL for given modifier and adblocker.
|
|
8062
|
+
*
|
|
8063
|
+
* @param modifiersData Parsed all modifiers data map.
|
|
8064
|
+
* @param blockerPrefix Prefix of the adblocker, e.g. 'adg_', 'ubo_', or 'abp_'.
|
|
8065
|
+
* @param modifier Parsed modifier AST node.
|
|
8066
|
+
*
|
|
8067
|
+
* @returns Documentation URL or `null` if not found.
|
|
8068
|
+
*/
|
|
8069
|
+
const getBlockerDocumentationLink = (modifiersData, blockerPrefix, modifier) => {
|
|
8070
|
+
const specificBlockerData = getSpecificBlockerData(modifiersData, blockerPrefix, modifier.modifier.value);
|
|
8071
|
+
return specificBlockerData?.docs || null;
|
|
8072
|
+
};
|
|
8073
|
+
// TODO: move to modifier.ts and use index.ts only for exporting
|
|
7180
8074
|
/**
|
|
7181
8075
|
* Modifier validator class.
|
|
7182
8076
|
*/
|
|
@@ -7229,18 +8123,18 @@ class ModifierValidator {
|
|
|
7229
8123
|
if (modifier.modifier.value.startsWith(UNDERSCORE)) {
|
|
7230
8124
|
// check whether the modifier value contains something else besides underscores
|
|
7231
8125
|
if (!isValidNoopModifier(modifier.modifier.value)) {
|
|
7232
|
-
return getInvalidValidationResult(`${
|
|
8126
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.INVALID_NOOP}: '${modifier.modifier.value}'`);
|
|
7233
8127
|
}
|
|
7234
8128
|
// otherwise, replace the modifier value with single underscore.
|
|
7235
8129
|
// it is needed to check whether the modifier is supported by specific adblocker due to the syntax
|
|
7236
8130
|
modifier.modifier.value = UNDERSCORE;
|
|
7237
8131
|
}
|
|
7238
8132
|
if (!this.exists(modifier)) {
|
|
7239
|
-
return getInvalidValidationResult(`${
|
|
8133
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NOT_EXISTENT}: '${modifier.modifier.value}'`);
|
|
7240
8134
|
}
|
|
7241
8135
|
// for 'Common' syntax we cannot check something more
|
|
7242
8136
|
if (syntax === exports.AdblockSyntax.Common) {
|
|
7243
|
-
return {
|
|
8137
|
+
return { valid: true };
|
|
7244
8138
|
}
|
|
7245
8139
|
return validateForSpecificSyntax(this.modifiersData, syntax, modifier, isException);
|
|
7246
8140
|
};
|
|
@@ -7598,7 +8492,7 @@ const MAX_LENGTH = 'max-length';
|
|
|
7598
8492
|
const MIN_LENGTH = 'min-length';
|
|
7599
8493
|
const MIN_TEXT_LENGTH = 'min-text-length';
|
|
7600
8494
|
const TAG_CONTENT = 'tag-content';
|
|
7601
|
-
const WILDCARD
|
|
8495
|
+
const WILDCARD = 'wildcard';
|
|
7602
8496
|
/**
|
|
7603
8497
|
* HTML filtering rule converter class
|
|
7604
8498
|
*
|
|
@@ -7669,7 +8563,7 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
7669
8563
|
maxLength = CssTree.parseAttributeSelectorValueAsNumber(node);
|
|
7670
8564
|
break;
|
|
7671
8565
|
case TAG_CONTENT:
|
|
7672
|
-
case WILDCARD
|
|
8566
|
+
case WILDCARD:
|
|
7673
8567
|
CssTree.assertAttributeSelectorHasStringValue(node);
|
|
7674
8568
|
convertedSelector.children.push(cloneDeep(node));
|
|
7675
8569
|
break;
|
|
@@ -7747,144 +8641,6 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
7747
8641
|
}
|
|
7748
8642
|
}
|
|
7749
8643
|
|
|
7750
|
-
/**
|
|
7751
|
-
* @file Utility functions for working with quotes
|
|
7752
|
-
*/
|
|
7753
|
-
/**
|
|
7754
|
-
* Possible quote types for scriptlet parameters
|
|
7755
|
-
*/
|
|
7756
|
-
exports.QuoteType = void 0;
|
|
7757
|
-
(function (QuoteType) {
|
|
7758
|
-
/**
|
|
7759
|
-
* No quotes at all
|
|
7760
|
-
*/
|
|
7761
|
-
QuoteType["None"] = "none";
|
|
7762
|
-
/**
|
|
7763
|
-
* Single quotes (`'`)
|
|
7764
|
-
*/
|
|
7765
|
-
QuoteType["Single"] = "single";
|
|
7766
|
-
/**
|
|
7767
|
-
* Double quotes (`"`)
|
|
7768
|
-
*/
|
|
7769
|
-
QuoteType["Double"] = "double";
|
|
7770
|
-
})(exports.QuoteType || (exports.QuoteType = {}));
|
|
7771
|
-
/**
|
|
7772
|
-
* Utility functions for working with quotes
|
|
7773
|
-
*/
|
|
7774
|
-
class QuoteUtils {
|
|
7775
|
-
/**
|
|
7776
|
-
* Escape all unescaped occurrences of the character
|
|
7777
|
-
*
|
|
7778
|
-
* @param string String to escape
|
|
7779
|
-
* @param char Character to escape
|
|
7780
|
-
* @returns Escaped string
|
|
7781
|
-
*/
|
|
7782
|
-
static escapeUnescapedOccurrences(string, char) {
|
|
7783
|
-
let result = EMPTY;
|
|
7784
|
-
for (let i = 0; i < string.length; i += 1) {
|
|
7785
|
-
if (string[i] === char && (i === 0 || string[i - 1] !== ESCAPE_CHARACTER)) {
|
|
7786
|
-
result += ESCAPE_CHARACTER;
|
|
7787
|
-
}
|
|
7788
|
-
result += string[i];
|
|
7789
|
-
}
|
|
7790
|
-
return result;
|
|
7791
|
-
}
|
|
7792
|
-
/**
|
|
7793
|
-
* Unescape all single escaped occurrences of the character
|
|
7794
|
-
*
|
|
7795
|
-
* @param string String to unescape
|
|
7796
|
-
* @param char Character to unescape
|
|
7797
|
-
* @returns Unescaped string
|
|
7798
|
-
*/
|
|
7799
|
-
static unescapeSingleEscapedOccurrences(string, char) {
|
|
7800
|
-
let result = EMPTY;
|
|
7801
|
-
for (let i = 0; i < string.length; i += 1) {
|
|
7802
|
-
if (string[i] === char
|
|
7803
|
-
&& string[i - 1] === ESCAPE_CHARACTER
|
|
7804
|
-
&& (i === 1 || string[i - 2] !== ESCAPE_CHARACTER)) {
|
|
7805
|
-
result = result.slice(0, -1);
|
|
7806
|
-
}
|
|
7807
|
-
result += string[i];
|
|
7808
|
-
}
|
|
7809
|
-
return result;
|
|
7810
|
-
}
|
|
7811
|
-
/**
|
|
7812
|
-
* Get quote type of the string
|
|
7813
|
-
*
|
|
7814
|
-
* @param string String to check
|
|
7815
|
-
* @returns Quote type of the string
|
|
7816
|
-
*/
|
|
7817
|
-
static getStringQuoteType(string) {
|
|
7818
|
-
// Don't check 1-character strings to avoid false positives
|
|
7819
|
-
if (string.length > 1) {
|
|
7820
|
-
if (string.startsWith(SINGLE_QUOTE) && string.endsWith(SINGLE_QUOTE)) {
|
|
7821
|
-
return exports.QuoteType.Single;
|
|
7822
|
-
}
|
|
7823
|
-
if (string.startsWith(DOUBLE_QUOTE) && string.endsWith(DOUBLE_QUOTE)) {
|
|
7824
|
-
return exports.QuoteType.Double;
|
|
7825
|
-
}
|
|
7826
|
-
}
|
|
7827
|
-
return exports.QuoteType.None;
|
|
7828
|
-
}
|
|
7829
|
-
/**
|
|
7830
|
-
* Set quote type of the string
|
|
7831
|
-
*
|
|
7832
|
-
* @param string String to set quote type of
|
|
7833
|
-
* @param quoteType Quote type to set
|
|
7834
|
-
* @returns String with the specified quote type
|
|
7835
|
-
*/
|
|
7836
|
-
static setStringQuoteType(string, quoteType) {
|
|
7837
|
-
const actualQuoteType = QuoteUtils.getStringQuoteType(string);
|
|
7838
|
-
switch (quoteType) {
|
|
7839
|
-
case exports.QuoteType.None:
|
|
7840
|
-
if (actualQuoteType === exports.QuoteType.Single) {
|
|
7841
|
-
return QuoteUtils.escapeUnescapedOccurrences(string.slice(1, -1), SINGLE_QUOTE);
|
|
7842
|
-
}
|
|
7843
|
-
if (actualQuoteType === exports.QuoteType.Double) {
|
|
7844
|
-
return QuoteUtils.escapeUnescapedOccurrences(string.slice(1, -1), DOUBLE_QUOTE);
|
|
7845
|
-
}
|
|
7846
|
-
return string;
|
|
7847
|
-
case exports.QuoteType.Single:
|
|
7848
|
-
if (actualQuoteType === exports.QuoteType.None) {
|
|
7849
|
-
return SINGLE_QUOTE + QuoteUtils.escapeUnescapedOccurrences(string, SINGLE_QUOTE) + SINGLE_QUOTE;
|
|
7850
|
-
}
|
|
7851
|
-
if (actualQuoteType === exports.QuoteType.Double) {
|
|
7852
|
-
return SINGLE_QUOTE
|
|
7853
|
-
+ QuoteUtils.escapeUnescapedOccurrences(QuoteUtils.unescapeSingleEscapedOccurrences(string.slice(1, -1), DOUBLE_QUOTE), SINGLE_QUOTE) + SINGLE_QUOTE;
|
|
7854
|
-
}
|
|
7855
|
-
return string;
|
|
7856
|
-
case exports.QuoteType.Double:
|
|
7857
|
-
if (actualQuoteType === exports.QuoteType.None) {
|
|
7858
|
-
return DOUBLE_QUOTE + QuoteUtils.escapeUnescapedOccurrences(string, DOUBLE_QUOTE) + DOUBLE_QUOTE;
|
|
7859
|
-
}
|
|
7860
|
-
if (actualQuoteType !== exports.QuoteType.Double) {
|
|
7861
|
-
// eslint-disable-next-line max-len
|
|
7862
|
-
return DOUBLE_QUOTE
|
|
7863
|
-
+ QuoteUtils.escapeUnescapedOccurrences(QuoteUtils.unescapeSingleEscapedOccurrences(string.slice(1, -1), SINGLE_QUOTE), DOUBLE_QUOTE) + DOUBLE_QUOTE;
|
|
7864
|
-
}
|
|
7865
|
-
return string;
|
|
7866
|
-
default:
|
|
7867
|
-
return string;
|
|
7868
|
-
}
|
|
7869
|
-
}
|
|
7870
|
-
/**
|
|
7871
|
-
* Removes bounding quotes from a string, if any
|
|
7872
|
-
*
|
|
7873
|
-
* @param string Input string
|
|
7874
|
-
* @returns String without quotes
|
|
7875
|
-
*/
|
|
7876
|
-
static removeQuotes(string) {
|
|
7877
|
-
if (
|
|
7878
|
-
// We should check for string length to avoid false positives
|
|
7879
|
-
string.length > 1
|
|
7880
|
-
&& (string[0] === SINGLE_QUOTE || string[0] === DOUBLE_QUOTE)
|
|
7881
|
-
&& string[0] === string[string.length - 1]) {
|
|
7882
|
-
return string.slice(1, -1);
|
|
7883
|
-
}
|
|
7884
|
-
return string;
|
|
7885
|
-
}
|
|
7886
|
-
}
|
|
7887
|
-
|
|
7888
8644
|
/**
|
|
7889
8645
|
* @file Utility functions for working with scriptlet nodes
|
|
7890
8646
|
*/
|
|
@@ -8781,41 +9537,6 @@ class FilterListConverter extends ConverterBase {
|
|
|
8781
9537
|
}
|
|
8782
9538
|
}
|
|
8783
9539
|
|
|
8784
|
-
/**
|
|
8785
|
-
* @file Utility functions for domain and hostname validation.
|
|
8786
|
-
*/
|
|
8787
|
-
const WILDCARD = ASTERISK; // *
|
|
8788
|
-
const WILDCARD_TLD = DOT + WILDCARD; // .*
|
|
8789
|
-
const WILDCARD_SUBDOMAIN = WILDCARD + DOT; // *.
|
|
8790
|
-
class DomainUtils {
|
|
8791
|
-
/**
|
|
8792
|
-
* Check if the input is a valid domain or hostname.
|
|
8793
|
-
*
|
|
8794
|
-
* @param domain Domain to check
|
|
8795
|
-
* @returns `true` if the domain is valid, `false` otherwise
|
|
8796
|
-
*/
|
|
8797
|
-
static isValidDomainOrHostname(domain) {
|
|
8798
|
-
let domainToCheck = domain;
|
|
8799
|
-
// Wildcard-only domain, typically a generic rule
|
|
8800
|
-
if (domainToCheck === WILDCARD) {
|
|
8801
|
-
return true;
|
|
8802
|
-
}
|
|
8803
|
-
// https://adguard.com/kb/general/ad-filtering/create-own-filters/#wildcard-for-tld
|
|
8804
|
-
if (domainToCheck.endsWith(WILDCARD_TLD)) {
|
|
8805
|
-
// Remove the wildcard TLD
|
|
8806
|
-
domainToCheck = domainToCheck.substring(0, domainToCheck.length - WILDCARD_TLD.length);
|
|
8807
|
-
}
|
|
8808
|
-
if (domainToCheck.startsWith(WILDCARD_SUBDOMAIN)) {
|
|
8809
|
-
// Remove the wildcard subdomain
|
|
8810
|
-
domainToCheck = domainToCheck.substring(WILDCARD_SUBDOMAIN.length);
|
|
8811
|
-
}
|
|
8812
|
-
// Parse the domain with tldts
|
|
8813
|
-
const tldtsResult = tldts.parse(domainToCheck);
|
|
8814
|
-
// Check if the domain is valid
|
|
8815
|
-
return domainToCheck === tldtsResult.domain || domainToCheck === tldtsResult.hostname;
|
|
8816
|
-
}
|
|
8817
|
-
}
|
|
8818
|
-
|
|
8819
9540
|
/**
|
|
8820
9541
|
* @file Utility functions for logical expression AST.
|
|
8821
9542
|
*/
|
|
@@ -8894,7 +9615,7 @@ class LogicalExpressionUtils {
|
|
|
8894
9615
|
}
|
|
8895
9616
|
}
|
|
8896
9617
|
|
|
8897
|
-
const version$1 = "1.1.
|
|
9618
|
+
const version$1 = "1.1.4";
|
|
8898
9619
|
|
|
8899
9620
|
/**
|
|
8900
9621
|
* @file AGTree version
|
|
@@ -8917,13 +9638,13 @@ exports.AGLINT_COMMAND_PREFIX = AGLINT_COMMAND_PREFIX;
|
|
|
8917
9638
|
exports.AdblockSyntaxError = AdblockSyntaxError;
|
|
8918
9639
|
exports.AgentCommentRuleParser = AgentCommentRuleParser;
|
|
8919
9640
|
exports.AgentParser = AgentParser;
|
|
8920
|
-
exports.
|
|
9641
|
+
exports.AppListParser = AppListParser;
|
|
9642
|
+
exports.COMMA_DOMAIN_LIST_SEPARATOR = COMMA_DOMAIN_LIST_SEPARATOR;
|
|
8921
9643
|
exports.CommentRuleParser = CommentRuleParser;
|
|
8922
9644
|
exports.ConfigCommentRuleParser = ConfigCommentRuleParser;
|
|
8923
9645
|
exports.CosmeticRuleParser = CosmeticRuleParser;
|
|
8924
9646
|
exports.CosmeticRuleSeparatorUtils = CosmeticRuleSeparatorUtils;
|
|
8925
9647
|
exports.CssTree = CssTree;
|
|
8926
|
-
exports.DOMAIN_EXCEPTION_MARKER = DOMAIN_EXCEPTION_MARKER;
|
|
8927
9648
|
exports.DomainListParser = DomainListParser;
|
|
8928
9649
|
exports.DomainUtils = DomainUtils;
|
|
8929
9650
|
exports.EXT_CSS_LEGACY_ATTRIBUTES = EXT_CSS_LEGACY_ATTRIBUTES;
|
|
@@ -8941,15 +9662,16 @@ exports.LogicalExpressionUtils = LogicalExpressionUtils;
|
|
|
8941
9662
|
exports.METADATA_HEADERS = METADATA_HEADERS;
|
|
8942
9663
|
exports.MODIFIERS_SEPARATOR = MODIFIERS_SEPARATOR;
|
|
8943
9664
|
exports.MODIFIER_ASSIGN_OPERATOR = MODIFIER_ASSIGN_OPERATOR;
|
|
8944
|
-
exports.MODIFIER_DOMAIN_SEPARATOR = MODIFIER_DOMAIN_SEPARATOR;
|
|
8945
|
-
exports.MODIFIER_EXCEPTION_MARKER = MODIFIER_EXCEPTION_MARKER;
|
|
8946
9665
|
exports.MetadataCommentRuleParser = MetadataCommentRuleParser;
|
|
9666
|
+
exports.MethodListParser = MethodListParser;
|
|
8947
9667
|
exports.ModifierListParser = ModifierListParser;
|
|
8948
9668
|
exports.ModifierParser = ModifierParser;
|
|
9669
|
+
exports.NEGATION_MARKER = NEGATION_MARKER;
|
|
8949
9670
|
exports.NETWORK_RULE_EXCEPTION_MARKER = NETWORK_RULE_EXCEPTION_MARKER;
|
|
8950
9671
|
exports.NETWORK_RULE_SEPARATOR = NETWORK_RULE_SEPARATOR;
|
|
8951
9672
|
exports.NetworkRuleParser = NetworkRuleParser;
|
|
8952
9673
|
exports.NotImplementedError = NotImplementedError;
|
|
9674
|
+
exports.PIPE_MODIFIER_SEPARATOR = PIPE_MODIFIER_SEPARATOR;
|
|
8953
9675
|
exports.PREPROCESSOR_MARKER = PREPROCESSOR_MARKER;
|
|
8954
9676
|
exports.ParameterListParser = ParameterListParser;
|
|
8955
9677
|
exports.PreProcessorCommentRuleParser = PreProcessorCommentRuleParser;
|
|
@@ -8960,6 +9682,7 @@ exports.RuleConverter = RuleConverter;
|
|
|
8960
9682
|
exports.RuleParser = RuleParser;
|
|
8961
9683
|
exports.SAFARI_CB_AFFINITY = SAFARI_CB_AFFINITY;
|
|
8962
9684
|
exports.SPECIAL_REGEX_SYMBOLS = SPECIAL_REGEX_SYMBOLS;
|
|
9685
|
+
exports.StealthOptionListParser = StealthOptionListParser;
|
|
8963
9686
|
exports.UBO_SCRIPTLET_MASK = UBO_SCRIPTLET_MASK;
|
|
8964
9687
|
exports.locRange = locRange;
|
|
8965
9688
|
exports.modifierValidator = modifierValidator;
|