@adguard/agtree 1.1.1 → 1.1.3
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 +18 -0
- package/dist/agtree.cjs +1125 -401
- package/dist/agtree.d.ts +233 -40
- package/dist/agtree.esm.js +1120 -398
- 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.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v1.1.
|
|
2
|
+
* AGTree v1.1.3 (build date: Mon, 28 Aug 2023 16:19:09 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 @@ import { walk, parse, toPlainObject, find, generate, fromPlainObject, List } fro
|
|
|
11
11
|
import * as ecssTree from '@adguard/ecss-tree';
|
|
12
12
|
export { ecssTree as ECSSTree };
|
|
13
13
|
import cloneDeep from 'clone-deep';
|
|
14
|
-
import
|
|
14
|
+
import XRegExp from 'xregexp';
|
|
15
15
|
import { parse as parse$1 } from 'tldts';
|
|
16
|
+
import { redirects } from '@adguard/scriptlets';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* @file Possible adblock syntaxes are listed here.
|
|
@@ -96,6 +97,7 @@ const CR = '\r';
|
|
|
96
97
|
const FF = '\f';
|
|
97
98
|
const LF = '\n';
|
|
98
99
|
const CRLF = CR + LF;
|
|
100
|
+
const NEWLINE = LF;
|
|
99
101
|
const DOUBLE_QUOTE = '"';
|
|
100
102
|
const SINGLE_QUOTE = '\'';
|
|
101
103
|
// Brackets
|
|
@@ -107,21 +109,157 @@ const OPEN_CURLY_BRACKET = '{';
|
|
|
107
109
|
const CLOSE_CURLY_BRACKET = '}';
|
|
108
110
|
// Letters
|
|
109
111
|
const SMALL_LETTER_A = 'a';
|
|
112
|
+
const SMALL_LETTER_B = 'b';
|
|
113
|
+
const SMALL_LETTER_C = 'c';
|
|
114
|
+
const SMALL_LETTER_D = 'd';
|
|
115
|
+
const SMALL_LETTER_E = 'e';
|
|
116
|
+
const SMALL_LETTER_F = 'f';
|
|
117
|
+
const SMALL_LETTER_G = 'g';
|
|
118
|
+
const SMALL_LETTER_H = 'h';
|
|
119
|
+
const SMALL_LETTER_I = 'i';
|
|
120
|
+
const SMALL_LETTER_J = 'j';
|
|
121
|
+
const SMALL_LETTER_K = 'k';
|
|
122
|
+
const SMALL_LETTER_L = 'l';
|
|
123
|
+
const SMALL_LETTER_M = 'm';
|
|
124
|
+
const SMALL_LETTER_N = 'n';
|
|
125
|
+
const SMALL_LETTER_O = 'o';
|
|
126
|
+
const SMALL_LETTER_P = 'p';
|
|
127
|
+
const SMALL_LETTER_Q = 'q';
|
|
128
|
+
const SMALL_LETTER_R = 'r';
|
|
129
|
+
const SMALL_LETTER_S = 's';
|
|
130
|
+
const SMALL_LETTER_T = 't';
|
|
131
|
+
const SMALL_LETTER_U = 'u';
|
|
132
|
+
const SMALL_LETTER_V = 'v';
|
|
133
|
+
const SMALL_LETTER_W = 'w';
|
|
134
|
+
const SMALL_LETTER_X = 'x';
|
|
135
|
+
const SMALL_LETTER_Y = 'y';
|
|
110
136
|
const SMALL_LETTER_Z = 'z';
|
|
137
|
+
/**
|
|
138
|
+
* Set of all small letters.
|
|
139
|
+
*/
|
|
140
|
+
const SMALL_LETTERS = new Set([
|
|
141
|
+
SMALL_LETTER_A,
|
|
142
|
+
SMALL_LETTER_B,
|
|
143
|
+
SMALL_LETTER_C,
|
|
144
|
+
SMALL_LETTER_D,
|
|
145
|
+
SMALL_LETTER_E,
|
|
146
|
+
SMALL_LETTER_F,
|
|
147
|
+
SMALL_LETTER_G,
|
|
148
|
+
SMALL_LETTER_H,
|
|
149
|
+
SMALL_LETTER_I,
|
|
150
|
+
SMALL_LETTER_J,
|
|
151
|
+
SMALL_LETTER_K,
|
|
152
|
+
SMALL_LETTER_L,
|
|
153
|
+
SMALL_LETTER_M,
|
|
154
|
+
SMALL_LETTER_N,
|
|
155
|
+
SMALL_LETTER_O,
|
|
156
|
+
SMALL_LETTER_P,
|
|
157
|
+
SMALL_LETTER_Q,
|
|
158
|
+
SMALL_LETTER_R,
|
|
159
|
+
SMALL_LETTER_S,
|
|
160
|
+
SMALL_LETTER_T,
|
|
161
|
+
SMALL_LETTER_U,
|
|
162
|
+
SMALL_LETTER_V,
|
|
163
|
+
SMALL_LETTER_W,
|
|
164
|
+
SMALL_LETTER_X,
|
|
165
|
+
SMALL_LETTER_Y,
|
|
166
|
+
SMALL_LETTER_Z,
|
|
167
|
+
]);
|
|
111
168
|
// Capital letters
|
|
112
169
|
const CAPITAL_LETTER_A = 'A';
|
|
170
|
+
const CAPITAL_LETTER_B = 'B';
|
|
171
|
+
const CAPITAL_LETTER_C = 'C';
|
|
172
|
+
const CAPITAL_LETTER_D = 'D';
|
|
173
|
+
const CAPITAL_LETTER_E = 'E';
|
|
174
|
+
const CAPITAL_LETTER_F = 'F';
|
|
175
|
+
const CAPITAL_LETTER_G = 'G';
|
|
176
|
+
const CAPITAL_LETTER_H = 'H';
|
|
177
|
+
const CAPITAL_LETTER_I = 'I';
|
|
178
|
+
const CAPITAL_LETTER_J = 'J';
|
|
179
|
+
const CAPITAL_LETTER_K = 'K';
|
|
180
|
+
const CAPITAL_LETTER_L = 'L';
|
|
181
|
+
const CAPITAL_LETTER_M = 'M';
|
|
182
|
+
const CAPITAL_LETTER_N = 'N';
|
|
183
|
+
const CAPITAL_LETTER_O = 'O';
|
|
184
|
+
const CAPITAL_LETTER_P = 'P';
|
|
185
|
+
const CAPITAL_LETTER_Q = 'Q';
|
|
186
|
+
const CAPITAL_LETTER_R = 'R';
|
|
187
|
+
const CAPITAL_LETTER_S = 'S';
|
|
188
|
+
const CAPITAL_LETTER_T = 'T';
|
|
189
|
+
const CAPITAL_LETTER_U = 'U';
|
|
190
|
+
const CAPITAL_LETTER_V = 'V';
|
|
191
|
+
const CAPITAL_LETTER_W = 'W';
|
|
192
|
+
const CAPITAL_LETTER_X = 'X';
|
|
193
|
+
const CAPITAL_LETTER_Y = 'Y';
|
|
113
194
|
const CAPITAL_LETTER_Z = 'Z';
|
|
195
|
+
/**
|
|
196
|
+
* Set of all capital letters.
|
|
197
|
+
*/
|
|
198
|
+
const CAPITAL_LETTERS = new Set([
|
|
199
|
+
CAPITAL_LETTER_A,
|
|
200
|
+
CAPITAL_LETTER_B,
|
|
201
|
+
CAPITAL_LETTER_C,
|
|
202
|
+
CAPITAL_LETTER_D,
|
|
203
|
+
CAPITAL_LETTER_E,
|
|
204
|
+
CAPITAL_LETTER_F,
|
|
205
|
+
CAPITAL_LETTER_G,
|
|
206
|
+
CAPITAL_LETTER_H,
|
|
207
|
+
CAPITAL_LETTER_I,
|
|
208
|
+
CAPITAL_LETTER_J,
|
|
209
|
+
CAPITAL_LETTER_K,
|
|
210
|
+
CAPITAL_LETTER_L,
|
|
211
|
+
CAPITAL_LETTER_M,
|
|
212
|
+
CAPITAL_LETTER_N,
|
|
213
|
+
CAPITAL_LETTER_O,
|
|
214
|
+
CAPITAL_LETTER_P,
|
|
215
|
+
CAPITAL_LETTER_Q,
|
|
216
|
+
CAPITAL_LETTER_R,
|
|
217
|
+
CAPITAL_LETTER_S,
|
|
218
|
+
CAPITAL_LETTER_T,
|
|
219
|
+
CAPITAL_LETTER_U,
|
|
220
|
+
CAPITAL_LETTER_V,
|
|
221
|
+
CAPITAL_LETTER_W,
|
|
222
|
+
CAPITAL_LETTER_X,
|
|
223
|
+
CAPITAL_LETTER_Y,
|
|
224
|
+
CAPITAL_LETTER_Z,
|
|
225
|
+
]);
|
|
114
226
|
// Numbers as strings
|
|
115
227
|
const NUMBER_0 = '0';
|
|
228
|
+
const NUMBER_1 = '1';
|
|
229
|
+
const NUMBER_2 = '2';
|
|
230
|
+
const NUMBER_3 = '3';
|
|
231
|
+
const NUMBER_4 = '4';
|
|
232
|
+
const NUMBER_5 = '5';
|
|
233
|
+
const NUMBER_6 = '6';
|
|
234
|
+
const NUMBER_7 = '7';
|
|
235
|
+
const NUMBER_8 = '8';
|
|
116
236
|
const NUMBER_9 = '9';
|
|
237
|
+
/**
|
|
238
|
+
* Set of all numbers as strings.
|
|
239
|
+
*/
|
|
240
|
+
const NUMBERS = new Set([
|
|
241
|
+
NUMBER_0,
|
|
242
|
+
NUMBER_1,
|
|
243
|
+
NUMBER_2,
|
|
244
|
+
NUMBER_3,
|
|
245
|
+
NUMBER_4,
|
|
246
|
+
NUMBER_5,
|
|
247
|
+
NUMBER_6,
|
|
248
|
+
NUMBER_7,
|
|
249
|
+
NUMBER_8,
|
|
250
|
+
NUMBER_9,
|
|
251
|
+
]);
|
|
117
252
|
const REGEX_MARKER = '/';
|
|
118
253
|
const ADG_SCRIPTLET_MASK = '//scriptlet';
|
|
119
254
|
const UBO_SCRIPTLET_MASK = 'js';
|
|
120
255
|
// Modifiers are separated by ",". For example: "script,domain=example.com"
|
|
121
256
|
const MODIFIERS_SEPARATOR = ',';
|
|
122
|
-
const MODIFIER_EXCEPTION_MARKER = '~';
|
|
123
257
|
const MODIFIER_ASSIGN_OPERATOR = '=';
|
|
124
|
-
const
|
|
258
|
+
const NEGATION_MARKER = '~';
|
|
259
|
+
/**
|
|
260
|
+
* The wildcard symbol — `*`.
|
|
261
|
+
*/
|
|
262
|
+
const WILDCARD$1 = ASTERISK;
|
|
125
263
|
/**
|
|
126
264
|
* Classic domain separator.
|
|
127
265
|
*
|
|
@@ -131,9 +269,9 @@ const DOMAIN_EXCEPTION_MARKER = '~';
|
|
|
131
269
|
* example.com,~example.org##.ads
|
|
132
270
|
* ```
|
|
133
271
|
*/
|
|
134
|
-
const
|
|
272
|
+
const COMMA_DOMAIN_LIST_SEPARATOR = ',';
|
|
135
273
|
/**
|
|
136
|
-
* Modifier domain
|
|
274
|
+
* Modifier separator for $app, $denyallow, $domain, $method.
|
|
137
275
|
*
|
|
138
276
|
* @example
|
|
139
277
|
* ```adblock
|
|
@@ -141,8 +279,7 @@ const CLASSIC_DOMAIN_SEPARATOR = ',';
|
|
|
141
279
|
* ads.js^$script,domains=example.com|~example.org
|
|
142
280
|
* ```
|
|
143
281
|
*/
|
|
144
|
-
const
|
|
145
|
-
const DOMAIN_LIST_TYPE = 'DomainList';
|
|
282
|
+
const PIPE_MODIFIER_SEPARATOR = '|';
|
|
146
283
|
const CSS_IMPORTANT = '!important';
|
|
147
284
|
const HINT_MARKER = '!+';
|
|
148
285
|
const HINT_MARKER_LEN = HINT_MARKER.length;
|
|
@@ -775,6 +912,27 @@ var RuleCategory;
|
|
|
775
912
|
*/
|
|
776
913
|
RuleCategory["Network"] = "Network";
|
|
777
914
|
})(RuleCategory || (RuleCategory = {}));
|
|
915
|
+
/**
|
|
916
|
+
* Represents similar types of modifiers values
|
|
917
|
+
* which may be separated by a comma `,` (only for DomainList) or a pipe `|`.
|
|
918
|
+
*/
|
|
919
|
+
var ListNodeType;
|
|
920
|
+
(function (ListNodeType) {
|
|
921
|
+
ListNodeType["AppList"] = "AppList";
|
|
922
|
+
ListNodeType["DomainList"] = "DomainList";
|
|
923
|
+
ListNodeType["MethodList"] = "MethodList";
|
|
924
|
+
ListNodeType["StealthOptionList"] = "StealthOptionList";
|
|
925
|
+
})(ListNodeType || (ListNodeType = {}));
|
|
926
|
+
/**
|
|
927
|
+
* Represents child items for {@link ListNodeType}.
|
|
928
|
+
*/
|
|
929
|
+
var ListItemNodeType;
|
|
930
|
+
(function (ListItemNodeType) {
|
|
931
|
+
ListItemNodeType["App"] = "App";
|
|
932
|
+
ListItemNodeType["Domain"] = "Domain";
|
|
933
|
+
ListItemNodeType["Method"] = "Method";
|
|
934
|
+
ListItemNodeType["StealthOption"] = "StealthOption";
|
|
935
|
+
})(ListItemNodeType || (ListItemNodeType = {}));
|
|
778
936
|
/**
|
|
779
937
|
* Represents possible comment types.
|
|
780
938
|
*/
|
|
@@ -2526,6 +2684,87 @@ class CommentRuleParser {
|
|
|
2526
2684
|
}
|
|
2527
2685
|
}
|
|
2528
2686
|
|
|
2687
|
+
/**
|
|
2688
|
+
* Prefixes for error messages which are used for parsing of value lists.
|
|
2689
|
+
*/
|
|
2690
|
+
const LIST_PARSE_ERROR_PREFIX = {
|
|
2691
|
+
EMPTY_ITEM: 'Empty value specified in the list',
|
|
2692
|
+
NO_MULTIPLE_NEGATION: 'Exception marker cannot be followed by another exception marker',
|
|
2693
|
+
NO_SEPARATOR_AFTER_NEGATION: 'Exception marker cannot be followed by a separator',
|
|
2694
|
+
NO_SEPARATOR_AT_THE_END: 'Value list cannot end with a separator',
|
|
2695
|
+
NO_WHITESPACE_AFTER_NEGATION: 'Exception marker cannot be followed by whitespace',
|
|
2696
|
+
};
|
|
2697
|
+
/**
|
|
2698
|
+
* Parses a `raw` modifier value which may be represented as a list of items separated by `separator`.
|
|
2699
|
+
* Needed for $app, $denyallow, $domain, $method.
|
|
2700
|
+
*
|
|
2701
|
+
* @param raw Raw modifier value.
|
|
2702
|
+
* @param separator Separator character.
|
|
2703
|
+
* @param loc Location of the modifier value.
|
|
2704
|
+
*
|
|
2705
|
+
* @returns List AST children — {@link App} | {@link Domain} | {@link Method} —
|
|
2706
|
+
* but with no `type` specified (see {@link ListItemNoType}).
|
|
2707
|
+
* @throws An {@link AdblockSyntaxError} if the list is syntactically invalid
|
|
2708
|
+
*
|
|
2709
|
+
* @example
|
|
2710
|
+
* - parses an app list — `com.example.app|Example.exe`
|
|
2711
|
+
* - parses a domain list — `example.com,example.org,~example.org` or `example.com|~example.org`
|
|
2712
|
+
* - parses a method list — `~post|~put`
|
|
2713
|
+
*/
|
|
2714
|
+
const parseListItems = (raw, separator, loc = defaultLocation) => {
|
|
2715
|
+
const rawListItems = [];
|
|
2716
|
+
// If the last character is a separator, then the list item is invalid
|
|
2717
|
+
// and no need to continue parsing
|
|
2718
|
+
const realEndIndex = StringUtils.skipWSBack(raw);
|
|
2719
|
+
if (raw[realEndIndex] === separator) {
|
|
2720
|
+
throw new AdblockSyntaxError(LIST_PARSE_ERROR_PREFIX.NO_SEPARATOR_AT_THE_END, locRange(loc, realEndIndex, realEndIndex + 1));
|
|
2721
|
+
}
|
|
2722
|
+
let offset = 0;
|
|
2723
|
+
// Skip whitespace before the list
|
|
2724
|
+
offset = StringUtils.skipWS(raw, offset);
|
|
2725
|
+
// Split list items by unescaped separators
|
|
2726
|
+
while (offset < raw.length) {
|
|
2727
|
+
// Skip whitespace before the list item
|
|
2728
|
+
offset = StringUtils.skipWS(raw, offset);
|
|
2729
|
+
let itemStart = offset;
|
|
2730
|
+
// Find the index of the first unescaped separator character
|
|
2731
|
+
const separatorStartIndex = StringUtils.findNextUnescapedCharacter(raw, separator, offset);
|
|
2732
|
+
const itemEnd = separatorStartIndex === -1
|
|
2733
|
+
? StringUtils.skipWSBack(raw) + 1
|
|
2734
|
+
: StringUtils.skipWSBack(raw, separatorStartIndex - 1) + 1;
|
|
2735
|
+
const exception = raw[itemStart] === NEGATION_MARKER;
|
|
2736
|
+
// Skip the exception marker
|
|
2737
|
+
if (exception) {
|
|
2738
|
+
itemStart += 1;
|
|
2739
|
+
// Exception marker cannot be followed by another exception marker
|
|
2740
|
+
if (raw[itemStart] === NEGATION_MARKER) {
|
|
2741
|
+
throw new AdblockSyntaxError(LIST_PARSE_ERROR_PREFIX.NO_MULTIPLE_NEGATION, locRange(loc, itemStart, itemStart + 1));
|
|
2742
|
+
}
|
|
2743
|
+
// Exception marker cannot be followed by a separator
|
|
2744
|
+
if (raw[itemStart] === separator) {
|
|
2745
|
+
throw new AdblockSyntaxError(LIST_PARSE_ERROR_PREFIX.NO_SEPARATOR_AFTER_NEGATION, locRange(loc, itemStart, itemStart + 1));
|
|
2746
|
+
}
|
|
2747
|
+
// Exception marker cannot be followed by whitespace
|
|
2748
|
+
if (StringUtils.isWhitespace(raw[itemStart])) {
|
|
2749
|
+
throw new AdblockSyntaxError(LIST_PARSE_ERROR_PREFIX.NO_WHITESPACE_AFTER_NEGATION, locRange(loc, itemStart, itemStart + 1));
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
// List item can't be empty
|
|
2753
|
+
if (itemStart === itemEnd) {
|
|
2754
|
+
throw new AdblockSyntaxError(LIST_PARSE_ERROR_PREFIX.EMPTY_ITEM, locRange(loc, itemStart, raw.length));
|
|
2755
|
+
}
|
|
2756
|
+
// Collect list item
|
|
2757
|
+
rawListItems.push({
|
|
2758
|
+
loc: locRange(loc, itemStart, itemEnd),
|
|
2759
|
+
value: raw.substring(itemStart, itemEnd),
|
|
2760
|
+
exception,
|
|
2761
|
+
});
|
|
2762
|
+
// Increment the offset to the next list item (or the end of the string)
|
|
2763
|
+
offset = separatorStartIndex === -1 ? raw.length : separatorStartIndex + 1;
|
|
2764
|
+
}
|
|
2765
|
+
return rawListItems;
|
|
2766
|
+
};
|
|
2767
|
+
|
|
2529
2768
|
/**
|
|
2530
2769
|
* `DomainListParser` is responsible for parsing a domain list.
|
|
2531
2770
|
*
|
|
@@ -2539,83 +2778,39 @@ class DomainListParser {
|
|
|
2539
2778
|
/**
|
|
2540
2779
|
* Parses a domain list, eg. `example.com,example.org,~example.org`
|
|
2541
2780
|
*
|
|
2542
|
-
* @param raw Raw domain list
|
|
2543
|
-
* @param separator Separator character
|
|
2544
|
-
* @param loc Location of the domain list
|
|
2545
|
-
*
|
|
2546
|
-
* @
|
|
2781
|
+
* @param raw Raw domain list.
|
|
2782
|
+
* @param separator Separator character.
|
|
2783
|
+
* @param loc Location of the domain list in the rule. If not set, the default location is used.
|
|
2784
|
+
*
|
|
2785
|
+
* @returns Domain list AST.
|
|
2786
|
+
* @throws An {@link AdblockSyntaxError} if the domain list is syntactically invalid.
|
|
2547
2787
|
*/
|
|
2548
|
-
static parse(raw, separator =
|
|
2549
|
-
const
|
|
2550
|
-
|
|
2788
|
+
static parse(raw, separator = COMMA_DOMAIN_LIST_SEPARATOR, loc = defaultLocation) {
|
|
2789
|
+
const rawItems = parseListItems(raw, separator, loc);
|
|
2790
|
+
const children = rawItems.map((rawListItem) => ({
|
|
2791
|
+
...rawListItem,
|
|
2792
|
+
type: ListItemNodeType.Domain,
|
|
2793
|
+
}));
|
|
2794
|
+
return {
|
|
2795
|
+
type: ListNodeType.DomainList,
|
|
2551
2796
|
loc: locRange(loc, 0, raw.length),
|
|
2552
2797
|
separator,
|
|
2553
|
-
children
|
|
2798
|
+
children,
|
|
2554
2799
|
};
|
|
2555
|
-
// If the last character is a separator, then the domain list is invalid
|
|
2556
|
-
// and no need to continue parsing
|
|
2557
|
-
const realEndIndex = StringUtils.skipWSBack(raw);
|
|
2558
|
-
if (raw[realEndIndex] === separator) {
|
|
2559
|
-
throw new AdblockSyntaxError('Domain list cannot end with a separator', locRange(loc, realEndIndex, realEndIndex + 1));
|
|
2560
|
-
}
|
|
2561
|
-
let offset = 0;
|
|
2562
|
-
// Skip whitespace before the domain list
|
|
2563
|
-
offset = StringUtils.skipWS(raw, offset);
|
|
2564
|
-
// Split domains by unescaped separators
|
|
2565
|
-
while (offset < raw.length) {
|
|
2566
|
-
// Skip whitespace before the domain
|
|
2567
|
-
offset = StringUtils.skipWS(raw, offset);
|
|
2568
|
-
let domainStart = offset;
|
|
2569
|
-
// Find the index of the first unescaped separator character
|
|
2570
|
-
const separatorStartIndex = StringUtils.findNextUnescapedCharacter(raw, separator, offset);
|
|
2571
|
-
const domainEnd = separatorStartIndex === -1
|
|
2572
|
-
? StringUtils.skipWSBack(raw) + 1
|
|
2573
|
-
: StringUtils.skipWSBack(raw, separatorStartIndex - 1) + 1;
|
|
2574
|
-
const exception = raw[domainStart] === DOMAIN_EXCEPTION_MARKER;
|
|
2575
|
-
// Skip the exception marker
|
|
2576
|
-
if (exception) {
|
|
2577
|
-
domainStart += 1;
|
|
2578
|
-
// Exception marker cannot be followed by another exception marker
|
|
2579
|
-
if (raw[domainStart] === DOMAIN_EXCEPTION_MARKER) {
|
|
2580
|
-
throw new AdblockSyntaxError('Exception marker cannot be followed by another exception marker', locRange(loc, domainStart, domainStart + 1));
|
|
2581
|
-
}
|
|
2582
|
-
// Exception marker cannot be followed by a separator
|
|
2583
|
-
if (raw[domainStart] === separator) {
|
|
2584
|
-
throw new AdblockSyntaxError('Exception marker cannot be followed by a separator', locRange(loc, domainStart, domainStart + 1));
|
|
2585
|
-
}
|
|
2586
|
-
// Exception marker cannot be followed by whitespace
|
|
2587
|
-
if (StringUtils.isWhitespace(raw[domainStart])) {
|
|
2588
|
-
throw new AdblockSyntaxError('Exception marker cannot be followed by whitespace', locRange(loc, domainStart, domainStart + 1));
|
|
2589
|
-
}
|
|
2590
|
-
}
|
|
2591
|
-
// Domain can't be empty
|
|
2592
|
-
if (domainStart === domainEnd) {
|
|
2593
|
-
throw new AdblockSyntaxError('Empty domain specified', locRange(loc, domainStart, raw.length));
|
|
2594
|
-
}
|
|
2595
|
-
// Add the domain to the result
|
|
2596
|
-
result.children.push({
|
|
2597
|
-
type: 'Domain',
|
|
2598
|
-
loc: locRange(loc, domainStart, domainEnd),
|
|
2599
|
-
value: raw.substring(domainStart, domainEnd),
|
|
2600
|
-
exception,
|
|
2601
|
-
});
|
|
2602
|
-
// Increment the offset to the next domain (or the end of the string)
|
|
2603
|
-
offset = separatorStartIndex === -1 ? raw.length : separatorStartIndex + 1;
|
|
2604
|
-
}
|
|
2605
|
-
return result;
|
|
2606
2800
|
}
|
|
2607
2801
|
/**
|
|
2608
2802
|
* Converts a domain list AST to a string.
|
|
2609
2803
|
*
|
|
2610
|
-
* @param ast Domain list AST
|
|
2611
|
-
*
|
|
2804
|
+
* @param ast Domain list AST.
|
|
2805
|
+
*
|
|
2806
|
+
* @returns Raw string.
|
|
2612
2807
|
*/
|
|
2613
2808
|
static generate(ast) {
|
|
2614
2809
|
const result = ast.children
|
|
2615
2810
|
.map(({ value, exception }) => {
|
|
2616
2811
|
let subresult = EMPTY;
|
|
2617
2812
|
if (exception) {
|
|
2618
|
-
subresult +=
|
|
2813
|
+
subresult += NEGATION_MARKER;
|
|
2619
2814
|
}
|
|
2620
2815
|
subresult += value.trim();
|
|
2621
2816
|
return subresult;
|
|
@@ -2649,8 +2844,8 @@ class ModifierParser {
|
|
|
2649
2844
|
const modifierStart = offset;
|
|
2650
2845
|
// Check if the modifier is an exception
|
|
2651
2846
|
let exception = false;
|
|
2652
|
-
if (raw[offset] ===
|
|
2653
|
-
offset +=
|
|
2847
|
+
if (raw[offset] === NEGATION_MARKER) {
|
|
2848
|
+
offset += NEGATION_MARKER.length;
|
|
2654
2849
|
exception = true;
|
|
2655
2850
|
}
|
|
2656
2851
|
// Skip whitespace after the exception marker (if any)
|
|
@@ -2714,7 +2909,7 @@ class ModifierParser {
|
|
|
2714
2909
|
static generate(modifier) {
|
|
2715
2910
|
let result = EMPTY;
|
|
2716
2911
|
if (modifier.exception) {
|
|
2717
|
-
result +=
|
|
2912
|
+
result += NEGATION_MARKER;
|
|
2718
2913
|
}
|
|
2719
2914
|
result += modifier.modifier.value;
|
|
2720
2915
|
if (modifier.value !== undefined) {
|
|
@@ -4308,21 +4503,15 @@ class ScriptletInjectionBodyParser {
|
|
|
4308
4503
|
}
|
|
4309
4504
|
// Save the offset of the opening parentheses
|
|
4310
4505
|
const openingParenthesesIndex = offset;
|
|
4311
|
-
//
|
|
4312
|
-
|
|
4313
|
-
const closingParenthesesIndex = StringUtils.findUnescapedNonStringNonRegexChar(raw, CLOSE_PARENTHESIS, openingParenthesesIndex + 1);
|
|
4506
|
+
// Skip whitespace from the end
|
|
4507
|
+
const closingParenthesesIndex = StringUtils.skipWSBack(raw, raw.length - 1);
|
|
4314
4508
|
// Closing parentheses should be present
|
|
4315
|
-
if (closingParenthesesIndex
|
|
4509
|
+
if (raw[closingParenthesesIndex] !== CLOSE_PARENTHESIS
|
|
4510
|
+
|| raw[closingParenthesesIndex - 1] === ESCAPE_CHARACTER) {
|
|
4316
4511
|
throw new AdblockSyntaxError(
|
|
4317
4512
|
// eslint-disable-next-line max-len
|
|
4318
4513
|
`Invalid AdGuard/uBlock scriptlet call, no closing parentheses '${CLOSE_PARENTHESIS}' found`, locRange(loc, offset, raw.length));
|
|
4319
4514
|
}
|
|
4320
|
-
// Shouldn't have any characters after the closing parentheses
|
|
4321
|
-
if (StringUtils.skipWSBack(raw) !== closingParenthesesIndex) {
|
|
4322
|
-
throw new AdblockSyntaxError(
|
|
4323
|
-
// eslint-disable-next-line max-len
|
|
4324
|
-
`Invalid AdGuard/uBlock scriptlet call, unexpected characters after the closing parentheses '${CLOSE_PARENTHESIS}'`, locRange(loc, closingParenthesesIndex + 1, raw.length));
|
|
4325
|
-
}
|
|
4326
4515
|
// Parse parameter list
|
|
4327
4516
|
const params = ParameterListParser.parse(raw.substring(openingParenthesesIndex + 1, closingParenthesesIndex), COMMA, shiftLoc(loc, openingParenthesesIndex + 1));
|
|
4328
4517
|
// Allow empty scritptlet call: js() or //scriptlet(), but not allow parameters
|
|
@@ -5568,6 +5757,102 @@ class RuleParser {
|
|
|
5568
5757
|
}
|
|
5569
5758
|
}
|
|
5570
5759
|
|
|
5760
|
+
/**
|
|
5761
|
+
* `AppListParser` is responsible for parsing an app list.
|
|
5762
|
+
*
|
|
5763
|
+
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#app-modifier}
|
|
5764
|
+
*/
|
|
5765
|
+
class AppListParser {
|
|
5766
|
+
/**
|
|
5767
|
+
* Parses an app list which items are separated by `|`,
|
|
5768
|
+
* e.g. `Example.exe|com.example.osx`.
|
|
5769
|
+
*
|
|
5770
|
+
* @param raw Raw app list
|
|
5771
|
+
* @param loc Location of the app list in the rule. If not set, the default location is used.
|
|
5772
|
+
*
|
|
5773
|
+
* @returns App list AST.
|
|
5774
|
+
* @throws An {@link AdblockSyntaxError} if the app list is syntactically invalid.
|
|
5775
|
+
*/
|
|
5776
|
+
static parse(raw, loc = defaultLocation) {
|
|
5777
|
+
const separator = PIPE_MODIFIER_SEPARATOR;
|
|
5778
|
+
const rawItems = parseListItems(raw, separator, loc);
|
|
5779
|
+
const children = rawItems.map((rawListItem) => ({
|
|
5780
|
+
...rawListItem,
|
|
5781
|
+
type: ListItemNodeType.App,
|
|
5782
|
+
}));
|
|
5783
|
+
return {
|
|
5784
|
+
type: ListNodeType.AppList,
|
|
5785
|
+
loc: locRange(loc, 0, raw.length),
|
|
5786
|
+
separator,
|
|
5787
|
+
children,
|
|
5788
|
+
};
|
|
5789
|
+
}
|
|
5790
|
+
}
|
|
5791
|
+
|
|
5792
|
+
/**
|
|
5793
|
+
* `MethodListParser` is responsible for parsing a method list.
|
|
5794
|
+
*
|
|
5795
|
+
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#method-modifier}
|
|
5796
|
+
*/
|
|
5797
|
+
class MethodListParser {
|
|
5798
|
+
/**
|
|
5799
|
+
* Parses a method list which items are separated by `|`,
|
|
5800
|
+
* e.g. `get|post|put`.
|
|
5801
|
+
*
|
|
5802
|
+
* @param raw Raw method list
|
|
5803
|
+
* @param loc Location of the method list in the rule. If not set, the default location is used.
|
|
5804
|
+
*
|
|
5805
|
+
* @returns Method list AST.
|
|
5806
|
+
* @throws An {@link AdblockSyntaxError} if the method list is syntactically invalid.
|
|
5807
|
+
*/
|
|
5808
|
+
static parse(raw, loc = defaultLocation) {
|
|
5809
|
+
const separator = PIPE_MODIFIER_SEPARATOR;
|
|
5810
|
+
const rawItems = parseListItems(raw, separator, loc);
|
|
5811
|
+
const children = rawItems.map((rawListItem) => ({
|
|
5812
|
+
...rawListItem,
|
|
5813
|
+
type: ListItemNodeType.Method,
|
|
5814
|
+
}));
|
|
5815
|
+
return {
|
|
5816
|
+
type: ListNodeType.MethodList,
|
|
5817
|
+
loc: locRange(loc, 0, raw.length),
|
|
5818
|
+
separator,
|
|
5819
|
+
children,
|
|
5820
|
+
};
|
|
5821
|
+
}
|
|
5822
|
+
}
|
|
5823
|
+
|
|
5824
|
+
/**
|
|
5825
|
+
* `StealthOptionListParser` is responsible for parsing a list of stealth options.
|
|
5826
|
+
*
|
|
5827
|
+
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#stealth-modifier}
|
|
5828
|
+
*/
|
|
5829
|
+
class StealthOptionListParser {
|
|
5830
|
+
/**
|
|
5831
|
+
* Parses a stealth option list which items are separated by `|`,
|
|
5832
|
+
* e.g. `dpi|ip`.
|
|
5833
|
+
*
|
|
5834
|
+
* @param raw Raw list of stealth options.
|
|
5835
|
+
* @param loc Location of the stealth option list in the rule. If not set, the default location is used.
|
|
5836
|
+
*
|
|
5837
|
+
* @returns Stealth option list AST.
|
|
5838
|
+
* @throws An {@link AdblockSyntaxError} if the stealth option list is syntactically invalid.
|
|
5839
|
+
*/
|
|
5840
|
+
static parse(raw, loc = defaultLocation) {
|
|
5841
|
+
const separator = PIPE_MODIFIER_SEPARATOR;
|
|
5842
|
+
const rawItems = parseListItems(raw, separator, loc);
|
|
5843
|
+
const children = rawItems.map((rawListItem) => ({
|
|
5844
|
+
...rawListItem,
|
|
5845
|
+
type: ListItemNodeType.StealthOption,
|
|
5846
|
+
}));
|
|
5847
|
+
return {
|
|
5848
|
+
type: ListNodeType.StealthOptionList,
|
|
5849
|
+
loc: locRange(loc, 0, raw.length),
|
|
5850
|
+
separator,
|
|
5851
|
+
children,
|
|
5852
|
+
};
|
|
5853
|
+
}
|
|
5854
|
+
}
|
|
5855
|
+
|
|
5571
5856
|
/**
|
|
5572
5857
|
* `FilterListParser` is responsible for parsing a whole adblock filter list (list of rules).
|
|
5573
5858
|
* It is a wrapper around `RuleParser` which parses each line separately.
|
|
@@ -5778,7 +6063,8 @@ var data$S = { adg_os_any:{ name:"app",
|
|
|
5778
6063
|
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.",
|
|
5779
6064
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#app-modifier",
|
|
5780
6065
|
assignable:true,
|
|
5781
|
-
negatable:false
|
|
6066
|
+
negatable:false,
|
|
6067
|
+
value_format:"pipe_separated_apps" } };
|
|
5782
6068
|
|
|
5783
6069
|
var data$R = { adg_os_any:{ name:"badfilter",
|
|
5784
6070
|
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).",
|
|
@@ -5839,7 +6125,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
5839
6125
|
assignable:true,
|
|
5840
6126
|
negatable:false,
|
|
5841
6127
|
value_optional:true,
|
|
5842
|
-
value_format:"
|
|
6128
|
+
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,}" },
|
|
5843
6129
|
adg_ext_any:{ name:"csp",
|
|
5844
6130
|
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.",
|
|
5845
6131
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#csp-modifier",
|
|
@@ -5851,7 +6137,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
5851
6137
|
assignable:true,
|
|
5852
6138
|
negatable:false,
|
|
5853
6139
|
value_optional:true,
|
|
5854
|
-
value_format:"
|
|
6140
|
+
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,}" },
|
|
5855
6141
|
abp_ext_any:{ name:"csp",
|
|
5856
6142
|
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.",
|
|
5857
6143
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293-How-to-write-filters#content-security-policies",
|
|
@@ -5861,7 +6147,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
5861
6147
|
assignable:true,
|
|
5862
6148
|
negatable:false,
|
|
5863
6149
|
value_optional:true,
|
|
5864
|
-
value_format:"
|
|
6150
|
+
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,}" },
|
|
5865
6151
|
ubo_ext_any:{ name:"csp",
|
|
5866
6152
|
description:"This modifier completely changes the rule behavior.\nIf it is applied to a rule, it will not block the matching request.\nThe response headers are going to be modified instead.",
|
|
5867
6153
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#csp",
|
|
@@ -5873,7 +6159,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
5873
6159
|
assignable:true,
|
|
5874
6160
|
negatable:false,
|
|
5875
6161
|
value_optional:true,
|
|
5876
|
-
value_format:"
|
|
6162
|
+
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,}" } };
|
|
5877
6163
|
|
|
5878
6164
|
var data$M = { adg_os_any:{ name:"denyallow",
|
|
5879
6165
|
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.",
|
|
@@ -5881,35 +6167,35 @@ var data$M = { adg_os_any:{ name:"denyallow",
|
|
|
5881
6167
|
conflicts:[ "to" ],
|
|
5882
6168
|
assignable:true,
|
|
5883
6169
|
negatable:false,
|
|
5884
|
-
value_format:"
|
|
6170
|
+
value_format:"pipe_separated_denyallow_domains" },
|
|
5885
6171
|
adg_ext_any:{ name:"denyallow",
|
|
5886
6172
|
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.",
|
|
5887
6173
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#denyallow-modifier",
|
|
5888
6174
|
conflicts:[ "to" ],
|
|
5889
6175
|
assignable:true,
|
|
5890
6176
|
negatable:false,
|
|
5891
|
-
value_format:"
|
|
6177
|
+
value_format:"pipe_separated_denyallow_domains" },
|
|
5892
6178
|
adg_cb_ios:{ name:"denyallow",
|
|
5893
6179
|
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
6180
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#denyallow-modifier",
|
|
5895
6181
|
conflicts:[ "to" ],
|
|
5896
6182
|
assignable:true,
|
|
5897
6183
|
negatable:false,
|
|
5898
|
-
value_format:"
|
|
6184
|
+
value_format:"pipe_separated_denyallow_domains" },
|
|
5899
6185
|
adg_cb_safari:{ name:"denyallow",
|
|
5900
6186
|
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.",
|
|
5901
6187
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#denyallow-modifier",
|
|
5902
6188
|
conflicts:[ "to" ],
|
|
5903
6189
|
assignable:true,
|
|
5904
6190
|
negatable:false,
|
|
5905
|
-
value_format:"
|
|
6191
|
+
value_format:"pipe_separated_denyallow_domains" },
|
|
5906
6192
|
ubo_ext_any:{ name:"denyallow",
|
|
5907
6193
|
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.",
|
|
5908
6194
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#denyallow",
|
|
5909
6195
|
conflicts:[ "to" ],
|
|
5910
6196
|
assignable:true,
|
|
5911
6197
|
negatable:false,
|
|
5912
|
-
value_format:"
|
|
6198
|
+
value_format:"pipe_separated_denyallow_domains" } };
|
|
5913
6199
|
|
|
5914
6200
|
var data$L = { adg_os_any:{ name:"document",
|
|
5915
6201
|
description:"The rule corresponds to the main frame document requests,\ni.e. HTML documents that are loaded in the browser tab.",
|
|
@@ -6125,17 +6411,17 @@ var data$C = { adg_os_any:{ name:"header",
|
|
|
6125
6411
|
description:"The `$header` modifier allows matching the HTTP response\nhaving a specific header with (optionally) a specific value.",
|
|
6126
6412
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#header-modifier",
|
|
6127
6413
|
assignable:true,
|
|
6128
|
-
value_format:"
|
|
6414
|
+
value_format:"(?xi)\n ^\n # header name\n [\\w-]+\n (\n :\n # header value: string or regexp\n (\\w+|\\/.+\\/)\n )?" },
|
|
6129
6415
|
adg_ext_any:{ name:"header",
|
|
6130
6416
|
description:"The `$header` modifier allows matching the HTTP response\nhaving a specific header with (optionally) a specific value.",
|
|
6131
6417
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#header-modifier",
|
|
6132
6418
|
assignable:true,
|
|
6133
|
-
value_format:"
|
|
6419
|
+
value_format:"(?xi)\n ^\n # header name\n [\\w-]+\n (\n :\n # header value: string or regexp\n (\\w+|\\/.+\\/)\n )?" },
|
|
6134
6420
|
ubo_ext_any:{ name:"header",
|
|
6135
6421
|
description:"The `$header` modifier allows matching the HTTP response\nhaving a specific header with (optionally) a specific value.",
|
|
6136
6422
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#header",
|
|
6137
6423
|
assignable:true,
|
|
6138
|
-
value_format:"
|
|
6424
|
+
value_format:"(?xi)\n ^\n # header name\n [\\w-]+\n (\n :\n # header value: string or regexp\n (\\w+|\\/.+\\/)\n )?" } };
|
|
6139
6425
|
|
|
6140
6426
|
var data$B = { adg_os_any:{ name:"hls",
|
|
6141
6427
|
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).",
|
|
@@ -6150,7 +6436,8 @@ var data$B = { adg_os_any:{ name:"hls",
|
|
|
6150
6436
|
inverse_conflicts:true,
|
|
6151
6437
|
assignable:true,
|
|
6152
6438
|
negatable:false,
|
|
6153
|
-
|
|
6439
|
+
value_optional:true,
|
|
6440
|
+
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 )" } };
|
|
6154
6441
|
|
|
6155
6442
|
var data$A = { adg_any:{ name:"image",
|
|
6156
6443
|
description:"The rule corresponds to images requests.",
|
|
@@ -6255,7 +6542,8 @@ var data$v = { adg_os_any:{ name:"jsonprune",
|
|
|
6255
6542
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#jsonprune-modifier",
|
|
6256
6543
|
assignable:true,
|
|
6257
6544
|
negatable:false,
|
|
6258
|
-
|
|
6545
|
+
value_optional:true,
|
|
6546
|
+
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 $" } };
|
|
6259
6547
|
|
|
6260
6548
|
var data$u = { adg_any:{ name:"match-case",
|
|
6261
6549
|
description:"This modifier defines a rule which applies only to addresses that match the case.\nDefault rules are case-insensitive.",
|
|
@@ -6282,19 +6570,19 @@ var data$s = { adg_os_any:{ name:"method",
|
|
|
6282
6570
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#method-modifier",
|
|
6283
6571
|
negatable:false,
|
|
6284
6572
|
assignable:true,
|
|
6285
|
-
value_format:"
|
|
6573
|
+
value_format:"pipe_separated_methods" },
|
|
6286
6574
|
adg_ext_any:{ name:"method",
|
|
6287
6575
|
description:"This modifier limits the rule scope to requests that use the specified set of HTTP methods.\nNegated methods are allowed.",
|
|
6288
6576
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#method-modifier",
|
|
6289
6577
|
negatable:false,
|
|
6290
6578
|
assignable:true,
|
|
6291
|
-
value_format:"
|
|
6579
|
+
value_format:"pipe_separated_methods" },
|
|
6292
6580
|
ubo_ext_any:{ name:"method",
|
|
6293
6581
|
description:"This modifier limits the rule scope to requests that use the specified set of HTTP methods.\nNegated methods are allowed.",
|
|
6294
6582
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#method",
|
|
6295
6583
|
negatable:false,
|
|
6296
6584
|
assignable:true,
|
|
6297
|
-
value_format:"
|
|
6585
|
+
value_format:"pipe_separated_methods" } };
|
|
6298
6586
|
|
|
6299
6587
|
var data$r = { adg_os_any:{ name:"mp4",
|
|
6300
6588
|
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.",
|
|
@@ -6378,7 +6666,7 @@ var data$l = { adg_os_any:{ name:"permissions",
|
|
|
6378
6666
|
inverse_conflicts:true,
|
|
6379
6667
|
assignable:true,
|
|
6380
6668
|
negatable:false,
|
|
6381
|
-
value_format:"(?x)\n
|
|
6669
|
+
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 $" } };
|
|
6382
6670
|
|
|
6383
6671
|
var data$k = { adg_any:{ name:"ping",
|
|
6384
6672
|
description:"The rule corresponds to requests caused by either navigator.sendBeacon() or the ping attribute on links.",
|
|
@@ -6520,7 +6808,13 @@ var data$g = { adg_os_any:{ name:"redirect",
|
|
|
6520
6808
|
assignable:true,
|
|
6521
6809
|
negatable:false,
|
|
6522
6810
|
value_optional:true,
|
|
6523
|
-
value_format:"(?x)\n ^(\n 1x1\\.gif|\n 2x2\\.png|\n 3x2\\.png|\n 32x32\\.png|\n noop\\.css|\n noop\\.html|\n noopframe|\n noop\\.js|\n noop\\.txt|\n noop-0\\.1s\\.mp3|\n noop-0\\.5s\\.mp3|\n noop-1s\\.mp4|\n none|\n click2load\\.html|\n addthis_widget\\.js|\n amazon_ads\\.js|\n amazon_apstag\\.js|\n monkeybroker\\.js|\n doubleclick_instream_ad_status|\n google-analytics_ga\\.js|\n google-analytics_analytics\\.js|\n google-analytics_inpage_linkid\\.js|\n google-analytics_cx_api\\.js|\n google-ima\\.js|\n googletagservices_gpt\\.js|\n googletagmanager_gtm\\.js|\n googlesyndication_adsbygoogle\\.js|\n scorecardresearch_beacon\\.js|\n outbrain-widget\\.js|\n hd-main\\.js\n )\n (:[0-9]+)?$" }
|
|
6811
|
+
value_format:"(?x)\n ^(\n 1x1\\.gif|\n 2x2\\.png|\n 3x2\\.png|\n 32x32\\.png|\n noop\\.css|\n noop\\.html|\n noopframe|\n noop\\.js|\n noop\\.txt|\n noop-0\\.1s\\.mp3|\n noop-0\\.5s\\.mp3|\n noop-1s\\.mp4|\n none|\n click2load\\.html|\n addthis_widget\\.js|\n amazon_ads\\.js|\n amazon_apstag\\.js|\n monkeybroker\\.js|\n doubleclick_instream_ad_status|\n google-analytics_ga\\.js|\n google-analytics_analytics\\.js|\n google-analytics_inpage_linkid\\.js|\n google-analytics_cx_api\\.js|\n google-ima\\.js|\n googletagservices_gpt\\.js|\n googletagmanager_gtm\\.js|\n googlesyndication_adsbygoogle\\.js|\n scorecardresearch_beacon\\.js|\n outbrain-widget\\.js|\n hd-main\\.js\n )\n (:[0-9]+)?$" },
|
|
6812
|
+
abp_ext_any:{ name:"rewrite",
|
|
6813
|
+
description:"The `rewrite=` option allows the rewriting of URLs (or redirecting requests) to an internal\nresource in order to deactivate it without causing an error. Indicate the internal resource\nby name and prefix `abp-resource:` in order to be recognized. For example\n`$rewrite=abp-resource:blank-js` sends an empty JavaScript.",
|
|
6814
|
+
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293#rewrite",
|
|
6815
|
+
assignable:true,
|
|
6816
|
+
negatable:false,
|
|
6817
|
+
value_format:"(?x)\n # ABP resources always starts with the `abp-resource:` prefix\n ^abp-resource:\n # Possible resource names\n (\n blank-text|\n blank-css|\n blank-js|\n blank-html|\n blank-mp3|\n 1x1-transparent-gif|\n 2x2-transparent-png|\n 3x2-transparent-png|\n 32x32-transparent-png\n )$" } };
|
|
6524
6818
|
|
|
6525
6819
|
var data$f = { adg_os_any:{ name:"removeheader",
|
|
6526
6820
|
description:"Rules with the `$removeheader` modifier are intended to remove headers from HTTP requests and responses.",
|
|
@@ -6583,20 +6877,20 @@ var data$e = { adg_os_any:{ name:"removeparam",
|
|
|
6583
6877
|
assignable:true,
|
|
6584
6878
|
negatable:false,
|
|
6585
6879
|
value_optional:true,
|
|
6586
|
-
value_format:"
|
|
6880
|
+
value_format:"(?xi)\n (\n # string pattern\n \\w+\n # or regexp pattern\n |\n \\/.+\\/\n # flags\n ([gimuy]+)?\n )" },
|
|
6587
6881
|
adg_ext_any:{ name:"removeparam",
|
|
6588
6882
|
description:"Rules with the `$removeparam` modifier are intended to strip query parameters from requests' URLs.",
|
|
6589
6883
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#removeparam-modifier",
|
|
6590
6884
|
assignable:true,
|
|
6591
6885
|
negatable:false,
|
|
6592
6886
|
value_optional:true,
|
|
6593
|
-
value_format:"
|
|
6887
|
+
value_format:"(?xi)\n (\n # string pattern\n \\w+\n # or regexp pattern\n |\n \\/.+\\/\n # flags\n ([gimuy]+)?\n )" },
|
|
6594
6888
|
ubo_ext_any:{ name:"removeparam",
|
|
6595
6889
|
description:"Rules with the `$removeparam` modifier are intended to strip query parameters from requests' URLs.",
|
|
6596
6890
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#removeparam",
|
|
6597
6891
|
assignable:true,
|
|
6598
6892
|
negatable:false,
|
|
6599
|
-
value_format:"
|
|
6893
|
+
value_format:"(?xi)\n (\n # string pattern\n \\w+\n # or regexp pattern\n |\n \\/.+\\/\n # flags\n ([gimuy]+)?\n )" } };
|
|
6600
6894
|
|
|
6601
6895
|
var data$d = { adg_os_any:{ name:"replace",
|
|
6602
6896
|
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.",
|
|
@@ -6616,7 +6910,7 @@ var data$d = { adg_os_any:{ name:"replace",
|
|
|
6616
6910
|
inverse_conflicts:true,
|
|
6617
6911
|
assignable:true,
|
|
6618
6912
|
negatable:false,
|
|
6619
|
-
value_format:"
|
|
6913
|
+
value_format:"(?xi)\n ^\n \\/\n # the regexp to match by\n (.+)\n # separator\n \\/\n # replacement\n (.+?)\n \\/\n # flags\n ([gimuy]*)?\n $" },
|
|
6620
6914
|
adg_ext_firefox:{ 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.",
|
|
6622
6916
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#replace-modifier",
|
|
@@ -6634,7 +6928,7 @@ var data$d = { adg_os_any:{ name:"replace",
|
|
|
6634
6928
|
inverse_conflicts:true,
|
|
6635
6929
|
assignable:true,
|
|
6636
6930
|
negatable:false,
|
|
6637
|
-
value_format:"
|
|
6931
|
+
value_format:"(?xi)\n ^\n \\/\n # the regexp to match by\n (.+)\n # separator\n \\/\n # replacement\n (.+?)\n \\/\n # flags\n ([gimuy]*)?\n $" } };
|
|
6638
6932
|
|
|
6639
6933
|
var data$c = { adg_any:{ name:"script",
|
|
6640
6934
|
description:"The rule corresponds to script requests, e.g. javascript, vbscript.",
|
|
@@ -6678,7 +6972,7 @@ var data$a = { adg_os_any:{ name:"stealth",
|
|
|
6678
6972
|
negatable:false,
|
|
6679
6973
|
exception_only:true,
|
|
6680
6974
|
value_optional:true,
|
|
6681
|
-
value_format:"
|
|
6975
|
+
value_format:"pipe_separated_stealth_options" },
|
|
6682
6976
|
adg_ext_chrome:{ name:"stealth",
|
|
6683
6977
|
description:"Disables the Stealth Mode module for all corresponding pages and requests.",
|
|
6684
6978
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#stealth-modifier",
|
|
@@ -6686,7 +6980,7 @@ var data$a = { adg_os_any:{ name:"stealth",
|
|
|
6686
6980
|
negatable:false,
|
|
6687
6981
|
exception_only:true,
|
|
6688
6982
|
value_optional:true,
|
|
6689
|
-
value_format:"
|
|
6983
|
+
value_format:"pipe_separated_stealth_options" },
|
|
6690
6984
|
adg_ext_firefox:{ name:"stealth",
|
|
6691
6985
|
description:"Disables the Stealth Mode module for all corresponding pages and requests.",
|
|
6692
6986
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#stealth-modifier",
|
|
@@ -6694,7 +6988,7 @@ var data$a = { adg_os_any:{ name:"stealth",
|
|
|
6694
6988
|
negatable:false,
|
|
6695
6989
|
exception_only:true,
|
|
6696
6990
|
value_optional:true,
|
|
6697
|
-
value_format:"
|
|
6991
|
+
value_format:"pipe_separated_stealth_options" },
|
|
6698
6992
|
adg_ext_opera:{ name:"stealth",
|
|
6699
6993
|
description:"Disables the Stealth Mode module for all corresponding pages and requests.",
|
|
6700
6994
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#stealth-modifier",
|
|
@@ -6702,7 +6996,7 @@ var data$a = { adg_os_any:{ name:"stealth",
|
|
|
6702
6996
|
negatable:false,
|
|
6703
6997
|
exception_only:true,
|
|
6704
6998
|
value_optional:true,
|
|
6705
|
-
value_format:"
|
|
6999
|
+
value_format:"pipe_separated_stealth_options" },
|
|
6706
7000
|
adg_ext_edge:{ name:"stealth",
|
|
6707
7001
|
description:"Disables the Stealth Mode module for all corresponding pages and requests.",
|
|
6708
7002
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#stealth-modifier",
|
|
@@ -6710,7 +7004,7 @@ var data$a = { adg_os_any:{ name:"stealth",
|
|
|
6710
7004
|
negatable:false,
|
|
6711
7005
|
exception_only:true,
|
|
6712
7006
|
value_optional:true,
|
|
6713
|
-
value_format:"
|
|
7007
|
+
value_format:"pipe_separated_stealth_options" } };
|
|
6714
7008
|
|
|
6715
7009
|
var data$9 = { ubo_any:{ name:"strict1p",
|
|
6716
7010
|
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.",
|
|
@@ -6913,8 +7207,6 @@ var SpecificKey;
|
|
|
6913
7207
|
SpecificKey["Negatable"] = "negatable";
|
|
6914
7208
|
SpecificKey["BlockOnly"] = "block_only";
|
|
6915
7209
|
SpecificKey["ExceptionOnly"] = "exception_only";
|
|
6916
|
-
// TODO: consider removing this field
|
|
6917
|
-
// and handle whether the value is optional by `value_format`. AG-24028
|
|
6918
7210
|
SpecificKey["ValueOptional"] = "value_optional";
|
|
6919
7211
|
SpecificKey["ValueFormat"] = "value_format";
|
|
6920
7212
|
// TODO: following fields should be handled later
|
|
@@ -6978,25 +7270,123 @@ const getModifiersData = () => {
|
|
|
6978
7270
|
return dataMap;
|
|
6979
7271
|
};
|
|
6980
7272
|
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
7273
|
+
/**
|
|
7274
|
+
* Prefixes for different adblockers to describe the platform-specific modifiers data
|
|
7275
|
+
* stored in the yaml files.
|
|
7276
|
+
*/
|
|
7277
|
+
const BLOCKER_PREFIX = {
|
|
7278
|
+
[AdblockSyntax.Adg]: 'adg_',
|
|
7279
|
+
[AdblockSyntax.Ubo]: 'ubo_',
|
|
7280
|
+
[AdblockSyntax.Abp]: 'abp_',
|
|
7281
|
+
};
|
|
7282
|
+
/**
|
|
7283
|
+
* Set of all allowed characters for app name except the dot `.`.
|
|
7284
|
+
*/
|
|
7285
|
+
const APP_NAME_ALLOWED_CHARS = new Set([
|
|
7286
|
+
...CAPITAL_LETTERS,
|
|
7287
|
+
...SMALL_LETTERS,
|
|
7288
|
+
...NUMBERS,
|
|
7289
|
+
UNDERSCORE,
|
|
7290
|
+
]);
|
|
7291
|
+
/**
|
|
7292
|
+
* Allowed methods for $method modifier.
|
|
7293
|
+
*
|
|
7294
|
+
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#method-modifier}
|
|
7295
|
+
*/
|
|
7296
|
+
const ALLOWED_METHODS = new Set([
|
|
7297
|
+
'connect',
|
|
7298
|
+
'delete',
|
|
7299
|
+
'get',
|
|
7300
|
+
'head',
|
|
7301
|
+
'options',
|
|
7302
|
+
'patch',
|
|
7303
|
+
'post',
|
|
7304
|
+
'put',
|
|
7305
|
+
'trace',
|
|
7306
|
+
]);
|
|
7307
|
+
/**
|
|
7308
|
+
* Allowed stealth options for $stealth modifier.
|
|
7309
|
+
*
|
|
7310
|
+
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#stealth-modifier}
|
|
7311
|
+
*/
|
|
7312
|
+
const ALLOWED_STEALTH_OPTIONS = new Set([
|
|
7313
|
+
'searchqueries',
|
|
7314
|
+
'donottrack',
|
|
7315
|
+
'3p-cookie',
|
|
7316
|
+
'1p-cookie',
|
|
7317
|
+
'3p-cache',
|
|
7318
|
+
'3p-auth',
|
|
7319
|
+
'webrtc',
|
|
7320
|
+
'push',
|
|
7321
|
+
'location',
|
|
7322
|
+
'flash',
|
|
7323
|
+
'java',
|
|
7324
|
+
'referrer',
|
|
7325
|
+
'useragent',
|
|
7326
|
+
'ip',
|
|
7327
|
+
'xclientdata',
|
|
7328
|
+
'dpi',
|
|
7329
|
+
]);
|
|
7330
|
+
/**
|
|
7331
|
+
* Prefixes for error messages used in modifier validation.
|
|
7332
|
+
*/
|
|
7333
|
+
const VALIDATION_ERROR_PREFIX = {
|
|
6985
7334
|
BLOCK_ONLY: 'Only blocking rules may contain the modifier',
|
|
6986
7335
|
EXCEPTION_ONLY: 'Only exception rules may contain the modifier',
|
|
6987
|
-
|
|
6988
|
-
VALUE_REQUIRED: 'Value is required for the modifier',
|
|
6989
|
-
VALUE_FORBIDDEN: 'Value is not allowed for the modifier',
|
|
7336
|
+
INVALID_LIST_VALUES: 'Invalid values for the modifier',
|
|
6990
7337
|
INVALID_NOOP: 'Invalid noop modifier',
|
|
7338
|
+
MIXED_NEGATIONS: 'Simultaneous usage of negated and not negated values is forbidden for the modifier',
|
|
7339
|
+
NOT_EXISTENT: 'Non-existent modifier',
|
|
7340
|
+
NOT_NEGATABLE_MODIFIER: 'Non-negatable modifier',
|
|
7341
|
+
NOT_NEGATABLE_VALUE: 'Values cannot be negated for the modifier',
|
|
7342
|
+
NOT_SUPPORTED: 'The adblocker does not support the modifier',
|
|
7343
|
+
REMOVED: 'Removed and no longer supported modifier',
|
|
7344
|
+
VALUE_FORBIDDEN: 'Value is not allowed for the modifier',
|
|
7345
|
+
VALUE_INVALID: 'Value is invalid for the modifier',
|
|
7346
|
+
VALUE_REQUIRED: 'Value is required for the modifier',
|
|
7347
|
+
};
|
|
7348
|
+
/**
|
|
7349
|
+
* Prefixes for error messages related to issues in the source YAML files' data.
|
|
7350
|
+
*/
|
|
7351
|
+
const SOURCE_DATA_ERROR_PREFIX = {
|
|
7352
|
+
INVALID_VALUE_FORMAT_REGEXP: "Invalid regular expression in 'value_format' for the modifier",
|
|
7353
|
+
NO_DEPRECATION_MESSAGE: "Property 'deprecation_message' is required for the 'deprecated' modifier",
|
|
7354
|
+
NO_VALUE_FORMAT_FOR_ASSIGNABLE: "Property 'value_format' should be specified for the assignable modifier",
|
|
6991
7355
|
};
|
|
6992
7356
|
|
|
6993
7357
|
/**
|
|
6994
|
-
*
|
|
7358
|
+
* Validates the noop modifier (i.e. only underscores).
|
|
7359
|
+
*
|
|
7360
|
+
* @param value Value of the modifier.
|
|
7361
|
+
*
|
|
7362
|
+
* @returns True if the modifier is valid, false otherwise.
|
|
6995
7363
|
*/
|
|
6996
|
-
const
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7364
|
+
const isValidNoopModifier = (value) => {
|
|
7365
|
+
return value.split('').every((char) => char === UNDERSCORE);
|
|
7366
|
+
};
|
|
7367
|
+
/**
|
|
7368
|
+
* Returns invalid validation result with given error message.
|
|
7369
|
+
*
|
|
7370
|
+
* @param error Error message.
|
|
7371
|
+
*
|
|
7372
|
+
* @returns Validation result `{ valid: false, error }`.
|
|
7373
|
+
*/
|
|
7374
|
+
const getInvalidValidationResult = (error) => {
|
|
7375
|
+
return {
|
|
7376
|
+
valid: false,
|
|
7377
|
+
error,
|
|
7378
|
+
};
|
|
7379
|
+
};
|
|
7380
|
+
/**
|
|
7381
|
+
* Returns invalid validation result which uses {@link VALIDATION_ERROR_PREFIX.VALUE_REQUIRED} as prefix
|
|
7382
|
+
* and specifies the given `modifierName` in the error message.
|
|
7383
|
+
*
|
|
7384
|
+
* @param modifierName Modifier name.
|
|
7385
|
+
*
|
|
7386
|
+
* @returns Validation result `{ valid: false, error }`.
|
|
7387
|
+
*/
|
|
7388
|
+
const getValueRequiredValidationResult = (modifierName) => {
|
|
7389
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_REQUIRED}: '${modifierName}'`);
|
|
7000
7390
|
};
|
|
7001
7391
|
/**
|
|
7002
7392
|
* Collects names and aliases for all supported modifiers.
|
|
@@ -7048,116 +7438,621 @@ const getSpecificBlockerData = (modifiersData, blockerPrefix, modifierName) => {
|
|
|
7048
7438
|
});
|
|
7049
7439
|
return specificBlockerData;
|
|
7050
7440
|
};
|
|
7441
|
+
|
|
7051
7442
|
/**
|
|
7052
|
-
*
|
|
7053
|
-
*
|
|
7054
|
-
* @param error Error message.
|
|
7055
|
-
*
|
|
7056
|
-
* @returns Validation result `{ ok: false, error }`.
|
|
7443
|
+
* @file Utility functions for domain and hostname validation.
|
|
7057
7444
|
*/
|
|
7058
|
-
const getInvalidValidationResult = (error) => {
|
|
7059
|
-
return {
|
|
7060
|
-
ok: false,
|
|
7061
|
-
error,
|
|
7062
|
-
};
|
|
7063
|
-
};
|
|
7064
7445
|
/**
|
|
7065
|
-
*
|
|
7066
|
-
* is it supported by the blocker, deprecated, assignable, negatable, etc.
|
|
7446
|
+
* Marker for a wildcard top-level domain — `.*`.
|
|
7067
7447
|
*
|
|
7068
|
-
* @
|
|
7069
|
-
*
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
*
|
|
7448
|
+
* @example
|
|
7449
|
+
* `example.*` — matches with any TLD, e.g. `example.org`, `example.com`, etc.
|
|
7450
|
+
*/
|
|
7451
|
+
const WILDCARD_TLD = DOT + WILDCARD$1;
|
|
7452
|
+
/**
|
|
7453
|
+
* Marker for a wildcard subdomain — `*.`.
|
|
7074
7454
|
*
|
|
7075
|
-
* @
|
|
7455
|
+
* @example
|
|
7456
|
+
* `*.example.org` — matches with any subdomain, e.g. `foo.example.org` or `bar.example.org`
|
|
7076
7457
|
*/
|
|
7077
|
-
const
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
|
|
7082
|
-
|
|
7083
|
-
|
|
7084
|
-
|
|
7085
|
-
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.NOT_SUPPORTED}: '${modifierName}'`);
|
|
7091
|
-
}
|
|
7092
|
-
// e.g. 'object-subrequest'
|
|
7093
|
-
if (specificBlockerData.removed) {
|
|
7094
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.REMOVED}: '${modifierName}'`);
|
|
7095
|
-
}
|
|
7096
|
-
if (specificBlockerData.deprecated) {
|
|
7097
|
-
if (!specificBlockerData.deprecation_message) {
|
|
7098
|
-
throw new Error('Deprecation notice is required for deprecated modifier');
|
|
7458
|
+
const WILDCARD_SUBDOMAIN = WILDCARD$1 + DOT;
|
|
7459
|
+
class DomainUtils {
|
|
7460
|
+
/**
|
|
7461
|
+
* Check if the input is a valid domain or hostname.
|
|
7462
|
+
*
|
|
7463
|
+
* @param domain Domain to check
|
|
7464
|
+
* @returns `true` if the domain is valid, `false` otherwise
|
|
7465
|
+
*/
|
|
7466
|
+
static isValidDomainOrHostname(domain) {
|
|
7467
|
+
let domainToCheck = domain;
|
|
7468
|
+
// Wildcard-only domain, typically a generic rule
|
|
7469
|
+
if (domainToCheck === WILDCARD$1) {
|
|
7470
|
+
return true;
|
|
7099
7471
|
}
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
}
|
|
7105
|
-
if (specificBlockerData.block_only && isException) {
|
|
7106
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.BLOCK_ONLY}: '${modifierName}'`);
|
|
7107
|
-
}
|
|
7108
|
-
if (specificBlockerData.exception_only && !isException) {
|
|
7109
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.EXCEPTION_ONLY}: '${modifierName}'`);
|
|
7110
|
-
}
|
|
7111
|
-
// e.g. '~domain=example.com'
|
|
7112
|
-
if (!specificBlockerData.negatable && modifier.exception) {
|
|
7113
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.NOT_NEGATABLE}: '${modifierName}'`);
|
|
7114
|
-
}
|
|
7115
|
-
// e.g. 'domain'
|
|
7116
|
-
if (specificBlockerData.assignable) {
|
|
7117
|
-
/**
|
|
7118
|
-
* Some assignable modifiers can be used without a value,
|
|
7119
|
-
* e.g. '@@||example.com^$cookie'.
|
|
7120
|
-
*/
|
|
7121
|
-
if (!modifier.value
|
|
7122
|
-
// value should be specified if it is not optional
|
|
7123
|
-
&& !specificBlockerData.value_optional) {
|
|
7124
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.VALUE_REQUIRED}: '${modifierName}'`);
|
|
7472
|
+
// https://adguard.com/kb/general/ad-filtering/create-own-filters/#wildcard-for-tld
|
|
7473
|
+
if (domainToCheck.endsWith(WILDCARD_TLD)) {
|
|
7474
|
+
// Remove the wildcard TLD
|
|
7475
|
+
domainToCheck = domainToCheck.substring(0, domainToCheck.length - WILDCARD_TLD.length);
|
|
7125
7476
|
}
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
return getInvalidValidationResult(`${INVALID_ERROR_PREFIX.VALUE_FORBIDDEN}: '${modifierName}'`);
|
|
7477
|
+
if (domainToCheck.startsWith(WILDCARD_SUBDOMAIN)) {
|
|
7478
|
+
// Remove the wildcard subdomain
|
|
7479
|
+
domainToCheck = domainToCheck.substring(WILDCARD_SUBDOMAIN.length);
|
|
7480
|
+
}
|
|
7481
|
+
// Parse the domain with tldts
|
|
7482
|
+
const tldtsResult = parse$1(domainToCheck);
|
|
7483
|
+
// Check if the domain is valid
|
|
7484
|
+
return domainToCheck === tldtsResult.domain || domainToCheck === tldtsResult.hostname;
|
|
7135
7485
|
}
|
|
7136
|
-
|
|
7137
|
-
|
|
7486
|
+
}
|
|
7487
|
+
|
|
7138
7488
|
/**
|
|
7139
|
-
*
|
|
7140
|
-
|
|
7489
|
+
* @file Utility functions for working with quotes
|
|
7490
|
+
*/
|
|
7491
|
+
/**
|
|
7492
|
+
* Possible quote types for scriptlet parameters
|
|
7493
|
+
*/
|
|
7494
|
+
var QuoteType;
|
|
7495
|
+
(function (QuoteType) {
|
|
7496
|
+
/**
|
|
7497
|
+
* No quotes at all
|
|
7498
|
+
*/
|
|
7499
|
+
QuoteType["None"] = "none";
|
|
7500
|
+
/**
|
|
7501
|
+
* Single quotes (`'`)
|
|
7502
|
+
*/
|
|
7503
|
+
QuoteType["Single"] = "single";
|
|
7504
|
+
/**
|
|
7505
|
+
* Double quotes (`"`)
|
|
7506
|
+
*/
|
|
7507
|
+
QuoteType["Double"] = "double";
|
|
7508
|
+
})(QuoteType || (QuoteType = {}));
|
|
7509
|
+
/**
|
|
7510
|
+
* Utility functions for working with quotes
|
|
7511
|
+
*/
|
|
7512
|
+
class QuoteUtils {
|
|
7513
|
+
/**
|
|
7514
|
+
* Escape all unescaped occurrences of the character
|
|
7515
|
+
*
|
|
7516
|
+
* @param string String to escape
|
|
7517
|
+
* @param char Character to escape
|
|
7518
|
+
* @returns Escaped string
|
|
7519
|
+
*/
|
|
7520
|
+
static escapeUnescapedOccurrences(string, char) {
|
|
7521
|
+
let result = EMPTY;
|
|
7522
|
+
for (let i = 0; i < string.length; i += 1) {
|
|
7523
|
+
if (string[i] === char && (i === 0 || string[i - 1] !== ESCAPE_CHARACTER)) {
|
|
7524
|
+
result += ESCAPE_CHARACTER;
|
|
7525
|
+
}
|
|
7526
|
+
result += string[i];
|
|
7527
|
+
}
|
|
7528
|
+
return result;
|
|
7529
|
+
}
|
|
7530
|
+
/**
|
|
7531
|
+
* Unescape all single escaped occurrences of the character
|
|
7532
|
+
*
|
|
7533
|
+
* @param string String to unescape
|
|
7534
|
+
* @param char Character to unescape
|
|
7535
|
+
* @returns Unescaped string
|
|
7536
|
+
*/
|
|
7537
|
+
static unescapeSingleEscapedOccurrences(string, char) {
|
|
7538
|
+
let result = EMPTY;
|
|
7539
|
+
for (let i = 0; i < string.length; i += 1) {
|
|
7540
|
+
if (string[i] === char
|
|
7541
|
+
&& string[i - 1] === ESCAPE_CHARACTER
|
|
7542
|
+
&& (i === 1 || string[i - 2] !== ESCAPE_CHARACTER)) {
|
|
7543
|
+
result = result.slice(0, -1);
|
|
7544
|
+
}
|
|
7545
|
+
result += string[i];
|
|
7546
|
+
}
|
|
7547
|
+
return result;
|
|
7548
|
+
}
|
|
7549
|
+
/**
|
|
7550
|
+
* Get quote type of the string
|
|
7551
|
+
*
|
|
7552
|
+
* @param string String to check
|
|
7553
|
+
* @returns Quote type of the string
|
|
7554
|
+
*/
|
|
7555
|
+
static getStringQuoteType(string) {
|
|
7556
|
+
// Don't check 1-character strings to avoid false positives
|
|
7557
|
+
if (string.length > 1) {
|
|
7558
|
+
if (string.startsWith(SINGLE_QUOTE) && string.endsWith(SINGLE_QUOTE)) {
|
|
7559
|
+
return QuoteType.Single;
|
|
7560
|
+
}
|
|
7561
|
+
if (string.startsWith(DOUBLE_QUOTE) && string.endsWith(DOUBLE_QUOTE)) {
|
|
7562
|
+
return QuoteType.Double;
|
|
7563
|
+
}
|
|
7564
|
+
}
|
|
7565
|
+
return QuoteType.None;
|
|
7566
|
+
}
|
|
7567
|
+
/**
|
|
7568
|
+
* Set quote type of the string
|
|
7569
|
+
*
|
|
7570
|
+
* @param string String to set quote type of
|
|
7571
|
+
* @param quoteType Quote type to set
|
|
7572
|
+
* @returns String with the specified quote type
|
|
7573
|
+
*/
|
|
7574
|
+
static setStringQuoteType(string, quoteType) {
|
|
7575
|
+
const actualQuoteType = QuoteUtils.getStringQuoteType(string);
|
|
7576
|
+
switch (quoteType) {
|
|
7577
|
+
case QuoteType.None:
|
|
7578
|
+
if (actualQuoteType === QuoteType.Single) {
|
|
7579
|
+
return QuoteUtils.escapeUnescapedOccurrences(string.slice(1, -1), SINGLE_QUOTE);
|
|
7580
|
+
}
|
|
7581
|
+
if (actualQuoteType === QuoteType.Double) {
|
|
7582
|
+
return QuoteUtils.escapeUnescapedOccurrences(string.slice(1, -1), DOUBLE_QUOTE);
|
|
7583
|
+
}
|
|
7584
|
+
return string;
|
|
7585
|
+
case QuoteType.Single:
|
|
7586
|
+
if (actualQuoteType === QuoteType.None) {
|
|
7587
|
+
return SINGLE_QUOTE + QuoteUtils.escapeUnescapedOccurrences(string, SINGLE_QUOTE) + SINGLE_QUOTE;
|
|
7588
|
+
}
|
|
7589
|
+
if (actualQuoteType === QuoteType.Double) {
|
|
7590
|
+
return SINGLE_QUOTE
|
|
7591
|
+
+ QuoteUtils.escapeUnescapedOccurrences(QuoteUtils.unescapeSingleEscapedOccurrences(string.slice(1, -1), DOUBLE_QUOTE), SINGLE_QUOTE) + SINGLE_QUOTE;
|
|
7592
|
+
}
|
|
7593
|
+
return string;
|
|
7594
|
+
case QuoteType.Double:
|
|
7595
|
+
if (actualQuoteType === QuoteType.None) {
|
|
7596
|
+
return DOUBLE_QUOTE + QuoteUtils.escapeUnescapedOccurrences(string, DOUBLE_QUOTE) + DOUBLE_QUOTE;
|
|
7597
|
+
}
|
|
7598
|
+
if (actualQuoteType !== QuoteType.Double) {
|
|
7599
|
+
// eslint-disable-next-line max-len
|
|
7600
|
+
return DOUBLE_QUOTE
|
|
7601
|
+
+ QuoteUtils.escapeUnescapedOccurrences(QuoteUtils.unescapeSingleEscapedOccurrences(string.slice(1, -1), SINGLE_QUOTE), DOUBLE_QUOTE) + DOUBLE_QUOTE;
|
|
7602
|
+
}
|
|
7603
|
+
return string;
|
|
7604
|
+
default:
|
|
7605
|
+
return string;
|
|
7606
|
+
}
|
|
7607
|
+
}
|
|
7608
|
+
/**
|
|
7609
|
+
* Removes bounding quotes from a string, if any
|
|
7610
|
+
*
|
|
7611
|
+
* @param string Input string
|
|
7612
|
+
* @returns String without quotes
|
|
7613
|
+
*/
|
|
7614
|
+
static removeQuotes(string) {
|
|
7615
|
+
if (
|
|
7616
|
+
// We should check for string length to avoid false positives
|
|
7617
|
+
string.length > 1
|
|
7618
|
+
&& (string[0] === SINGLE_QUOTE || string[0] === DOUBLE_QUOTE)
|
|
7619
|
+
&& string[0] === string[string.length - 1]) {
|
|
7620
|
+
return string.slice(1, -1);
|
|
7621
|
+
}
|
|
7622
|
+
return string;
|
|
7623
|
+
}
|
|
7624
|
+
/**
|
|
7625
|
+
* Wraps given `strings` with `quote` (defaults to single quote `'`)
|
|
7626
|
+
* and joins them with `separator` (defaults to comma+space `, `).
|
|
7627
|
+
*
|
|
7628
|
+
* @param strings Strings to quote and join.
|
|
7629
|
+
* @param quoteType Quote to use.
|
|
7630
|
+
* @param separator Separator to use.
|
|
7631
|
+
*
|
|
7632
|
+
* @returns String with joined items.
|
|
7633
|
+
*
|
|
7634
|
+
* @example
|
|
7635
|
+
* ['abc', 'def']: strings[] -> "'abc', 'def'": string
|
|
7636
|
+
*/
|
|
7637
|
+
static quoteAndJoinStrings(strings, quoteType = QuoteType.Single, separator = `${COMMA}${SPACE}`) {
|
|
7638
|
+
return strings
|
|
7639
|
+
.map((s) => QuoteUtils.setStringQuoteType(s, quoteType))
|
|
7640
|
+
.join(separator);
|
|
7641
|
+
}
|
|
7642
|
+
}
|
|
7643
|
+
|
|
7644
|
+
/**
|
|
7645
|
+
* Pre-defined available validators for modifiers with custom `value_format`.
|
|
7646
|
+
*/
|
|
7647
|
+
var CustomValueFormatValidatorName;
|
|
7648
|
+
(function (CustomValueFormatValidatorName) {
|
|
7649
|
+
CustomValueFormatValidatorName["App"] = "pipe_separated_apps";
|
|
7650
|
+
// there are some differences between $domain and $denyallow
|
|
7651
|
+
CustomValueFormatValidatorName["DenyAllow"] = "pipe_separated_denyallow_domains";
|
|
7652
|
+
CustomValueFormatValidatorName["Domain"] = "pipe_separated_domains";
|
|
7653
|
+
CustomValueFormatValidatorName["Method"] = "pipe_separated_methods";
|
|
7654
|
+
CustomValueFormatValidatorName["StealthOption"] = "pipe_separated_stealth_options";
|
|
7655
|
+
})(CustomValueFormatValidatorName || (CustomValueFormatValidatorName = {}));
|
|
7656
|
+
/**
|
|
7657
|
+
* Checks whether the `chunk` of app name (which if splitted by dot `.`) is valid.
|
|
7658
|
+
* Only letters, numbers, and underscore `_` are allowed.
|
|
7659
|
+
*
|
|
7660
|
+
* @param chunk Chunk of app name to check.
|
|
7661
|
+
*
|
|
7662
|
+
* @returns True if the `chunk` is valid part of app name, false otherwise.
|
|
7663
|
+
*/
|
|
7664
|
+
const isValidAppNameChunk = (chunk) => {
|
|
7665
|
+
// e.g. 'Example..exe'
|
|
7666
|
+
if (chunk.length === 0) {
|
|
7667
|
+
return false;
|
|
7668
|
+
}
|
|
7669
|
+
for (let i = 0; i < chunk.length; i += 1) {
|
|
7670
|
+
const char = chunk[i];
|
|
7671
|
+
if (!APP_NAME_ALLOWED_CHARS.has(char)) {
|
|
7672
|
+
return false;
|
|
7673
|
+
}
|
|
7674
|
+
}
|
|
7675
|
+
return true;
|
|
7676
|
+
};
|
|
7677
|
+
/**
|
|
7678
|
+
* Checks whether the given `value` is valid app name as $app modifier value.
|
|
7679
|
+
*
|
|
7680
|
+
* @param value App name to check.
|
|
7681
|
+
*
|
|
7682
|
+
* @returns True if the `value` is valid app name, false otherwise.
|
|
7683
|
+
*/
|
|
7684
|
+
const isValidAppModifierValue = (value) => {
|
|
7685
|
+
// $app modifier does not support wildcard tld
|
|
7686
|
+
// https://adguard.app/kb/general/ad-filtering/create-own-filters/#app-modifier
|
|
7687
|
+
if (value.includes(WILDCARD$1)) {
|
|
7688
|
+
return false;
|
|
7689
|
+
}
|
|
7690
|
+
return value
|
|
7691
|
+
.split(DOT)
|
|
7692
|
+
.every((chunk) => isValidAppNameChunk(chunk));
|
|
7693
|
+
};
|
|
7694
|
+
/**
|
|
7695
|
+
* Checks whether the given `value` is valid HTTP method as $method modifier value.
|
|
7696
|
+
*
|
|
7697
|
+
* @param value Method to check.
|
|
7698
|
+
*
|
|
7699
|
+
* @returns True if the `value` is valid HTTP method, false otherwise.
|
|
7700
|
+
*/
|
|
7701
|
+
const isValidMethodModifierValue = (value) => {
|
|
7702
|
+
return ALLOWED_METHODS.has(value);
|
|
7703
|
+
};
|
|
7704
|
+
/**
|
|
7705
|
+
* Checks whether the given `value` is valid option as $stealth modifier value.
|
|
7706
|
+
*
|
|
7707
|
+
* @param value Stealth option to check.
|
|
7708
|
+
*
|
|
7709
|
+
* @returns True if the `value` is valid stealth option, false otherwise.
|
|
7710
|
+
*/
|
|
7711
|
+
const isValidStealthModifierValue = (value) => {
|
|
7712
|
+
return ALLOWED_STEALTH_OPTIONS.has(value);
|
|
7713
|
+
};
|
|
7714
|
+
/**
|
|
7715
|
+
* Checks whether the given `value` is valid domain as $denyallow modifier value.
|
|
7716
|
+
* Important: wildcard tld are not supported, compared to $domain.
|
|
7717
|
+
*
|
|
7718
|
+
* @param value Value to check.
|
|
7719
|
+
*
|
|
7720
|
+
* @returns True if the `value` is valid domain and does not contain wildcard `*`, false otherwise.
|
|
7721
|
+
*/
|
|
7722
|
+
const isValidDenyAllowModifierValue = (value) => {
|
|
7723
|
+
// $denyallow modifier does not support wildcard tld
|
|
7724
|
+
// https://adguard.app/kb/general/ad-filtering/create-own-filters/#denyallow-modifier
|
|
7725
|
+
// but here we are simply checking whether the value contains wildcard `*`, not ends with `.*`
|
|
7726
|
+
if (value.includes(WILDCARD$1)) {
|
|
7727
|
+
return false;
|
|
7728
|
+
}
|
|
7729
|
+
// TODO: add cache for domains validation
|
|
7730
|
+
return DomainUtils.isValidDomainOrHostname(value);
|
|
7731
|
+
};
|
|
7732
|
+
/**
|
|
7733
|
+
* Checks whether the given `value` is valid domain as $domain modifier value.
|
|
7734
|
+
*
|
|
7735
|
+
* @param value Value to check.
|
|
7736
|
+
*
|
|
7737
|
+
* @returns True if the `value` is valid domain, false otherwise.
|
|
7738
|
+
*/
|
|
7739
|
+
const isValidDomainModifierValue = (value) => {
|
|
7740
|
+
// TODO: add cache for domains validation
|
|
7741
|
+
return DomainUtils.isValidDomainOrHostname(value);
|
|
7742
|
+
};
|
|
7743
|
+
/**
|
|
7744
|
+
* Checks whether the all list items' exceptions are `false`.
|
|
7745
|
+
* Those items which `exception` is `true` is to be specified in the validation result error message.
|
|
7746
|
+
*
|
|
7747
|
+
* @param modifierName Modifier name.
|
|
7748
|
+
* @param listItems List items to check.
|
|
7749
|
+
*
|
|
7750
|
+
* @returns Validation result.
|
|
7751
|
+
*/
|
|
7752
|
+
const customNoNegatedListItemsValidator = (modifierName, listItems) => {
|
|
7753
|
+
const negatedValues = [];
|
|
7754
|
+
listItems.forEach((listItem) => {
|
|
7755
|
+
if (listItem.exception) {
|
|
7756
|
+
negatedValues.push(listItem.value);
|
|
7757
|
+
}
|
|
7758
|
+
});
|
|
7759
|
+
if (negatedValues.length > 0) {
|
|
7760
|
+
const valuesToStr = QuoteUtils.quoteAndJoinStrings(negatedValues);
|
|
7761
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NOT_NEGATABLE_VALUE}: '${modifierName}': ${valuesToStr}`);
|
|
7762
|
+
}
|
|
7763
|
+
return { valid: true };
|
|
7764
|
+
};
|
|
7765
|
+
/**
|
|
7766
|
+
* Checks whether the all list items' exceptions are consistent,
|
|
7767
|
+
* i.e. all items are either negated or not negated.
|
|
7768
|
+
*
|
|
7769
|
+
* The `exception` value of the first item is used as a reference, and all other items are checked against it.
|
|
7770
|
+
* Those items which `exception` is not consistent with the first item
|
|
7771
|
+
* is to be specified in the validation result error message.
|
|
7772
|
+
*
|
|
7773
|
+
* @see {@link https://adguard.com/kb/general/ad-filtering/create-own-filters/#method-modifier}
|
|
7774
|
+
*
|
|
7775
|
+
* @param modifierName Modifier name.
|
|
7776
|
+
* @param listItems List items to check.
|
|
7777
|
+
*
|
|
7778
|
+
* @returns Validation result.
|
|
7779
|
+
*/
|
|
7780
|
+
const customConsistentExceptionsValidator = (modifierName, listItems) => {
|
|
7781
|
+
const firstException = listItems[0].exception;
|
|
7782
|
+
const nonConsistentItemValues = [];
|
|
7783
|
+
listItems.forEach((listItem) => {
|
|
7784
|
+
if (listItem.exception !== firstException) {
|
|
7785
|
+
nonConsistentItemValues.push(listItem.value);
|
|
7786
|
+
}
|
|
7787
|
+
});
|
|
7788
|
+
if (nonConsistentItemValues.length > 0) {
|
|
7789
|
+
const valuesToStr = QuoteUtils.quoteAndJoinStrings(nonConsistentItemValues);
|
|
7790
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.MIXED_NEGATIONS}: '${modifierName}': ${valuesToStr}`);
|
|
7791
|
+
}
|
|
7792
|
+
return { valid: true };
|
|
7793
|
+
};
|
|
7794
|
+
/**
|
|
7795
|
+
* Checks whether the given `modifier` value is valid.
|
|
7796
|
+
* Supposed to validate the value of modifiers which values are lists separated by pipe `|` —
|
|
7797
|
+
* $app, $domain, $denyallow, $method.
|
|
7798
|
+
*
|
|
7799
|
+
* @param modifier Modifier AST node.
|
|
7800
|
+
* @param listParser Parser function for parsing modifier value
|
|
7801
|
+
* which is supposed to be a list separated by pipe `|`.
|
|
7802
|
+
* @param isValidListItem Predicate function for checking of modifier's list item validity,
|
|
7803
|
+
* e.g. $denyallow modifier does not support wildcard tld, but $domain does.
|
|
7804
|
+
* @param customListValidator Optional; custom validator for specific modifier,
|
|
7805
|
+
* e.g. $denyallow modifier does not support negated domains.
|
|
7806
|
+
*
|
|
7807
|
+
* @returns Result of modifier domains validation.
|
|
7808
|
+
*/
|
|
7809
|
+
const validateListItemsModifier = (modifier, listParser, isValidListItem, customListValidator) => {
|
|
7810
|
+
const modifierName = modifier.modifier.value;
|
|
7811
|
+
const defaultInvalidValueResult = getValueRequiredValidationResult(modifierName);
|
|
7812
|
+
if (!modifier.value?.value) {
|
|
7813
|
+
return defaultInvalidValueResult;
|
|
7814
|
+
}
|
|
7815
|
+
let theList;
|
|
7816
|
+
try {
|
|
7817
|
+
theList = listParser(modifier.value.value, PIPE_MODIFIER_SEPARATOR);
|
|
7818
|
+
}
|
|
7819
|
+
catch (e) {
|
|
7820
|
+
if (e instanceof AdblockSyntaxError) {
|
|
7821
|
+
return {
|
|
7822
|
+
valid: false,
|
|
7823
|
+
error: e.message,
|
|
7824
|
+
};
|
|
7825
|
+
}
|
|
7826
|
+
return defaultInvalidValueResult;
|
|
7827
|
+
}
|
|
7828
|
+
const invalidListItems = [];
|
|
7829
|
+
theList.children.forEach((item) => {
|
|
7830
|
+
// different validators are used for $denyallow and $domain modifiers
|
|
7831
|
+
// because of different requirements and restrictions
|
|
7832
|
+
if (!isValidListItem(item.value)) {
|
|
7833
|
+
invalidListItems.push(item.value);
|
|
7834
|
+
}
|
|
7835
|
+
});
|
|
7836
|
+
if (invalidListItems.length > 0) {
|
|
7837
|
+
const itemsToStr = QuoteUtils.quoteAndJoinStrings(invalidListItems);
|
|
7838
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.INVALID_LIST_VALUES}: '${modifierName}': ${itemsToStr}`);
|
|
7839
|
+
}
|
|
7840
|
+
// IMPORTANT: run custom validator after all other checks
|
|
7841
|
+
// Some lists should be fully checked, not just the list items:
|
|
7842
|
+
// e.g. Safari does not support allowed and disallowed domains for $domain in the same list
|
|
7843
|
+
// or domains cannot be negated for $denyallow modifier
|
|
7844
|
+
if (customListValidator) {
|
|
7845
|
+
return customListValidator(modifierName, theList.children);
|
|
7846
|
+
}
|
|
7847
|
+
return { valid: true };
|
|
7848
|
+
};
|
|
7849
|
+
/**
|
|
7850
|
+
* Validates 'pipe_separated_apps' custom value format.
|
|
7851
|
+
* Used for $app modifier.
|
|
7852
|
+
*
|
|
7853
|
+
* @param modifier Modifier AST node.
|
|
7854
|
+
*
|
|
7855
|
+
* @returns Validation result.
|
|
7856
|
+
*/
|
|
7857
|
+
const validatePipeSeparatedApps = (modifier) => {
|
|
7858
|
+
return validateListItemsModifier(modifier, (raw) => AppListParser.parse(raw), isValidAppModifierValue);
|
|
7859
|
+
};
|
|
7860
|
+
/**
|
|
7861
|
+
* Validates 'pipe_separated_denyallow_domains' custom value format.
|
|
7862
|
+
* Used for $denyallow modifier.
|
|
7863
|
+
*
|
|
7864
|
+
* @param modifier Modifier AST node.
|
|
7865
|
+
*
|
|
7866
|
+
* @returns Validation result.
|
|
7867
|
+
*/
|
|
7868
|
+
const validatePipeSeparatedDenyAllowDomains = (modifier) => {
|
|
7869
|
+
return validateListItemsModifier(modifier, DomainListParser.parse, isValidDenyAllowModifierValue, customNoNegatedListItemsValidator);
|
|
7870
|
+
};
|
|
7871
|
+
/**
|
|
7872
|
+
* Validates 'pipe_separated_domains' custom value format.
|
|
7873
|
+
* Used for $domains modifier.
|
|
7874
|
+
*
|
|
7875
|
+
* @param modifier Modifier AST node.
|
|
7876
|
+
*
|
|
7877
|
+
* @returns Validation result.
|
|
7878
|
+
*/
|
|
7879
|
+
const validatePipeSeparatedDomains = (modifier) => {
|
|
7880
|
+
return validateListItemsModifier(modifier, DomainListParser.parse, isValidDomainModifierValue);
|
|
7881
|
+
};
|
|
7882
|
+
/**
|
|
7883
|
+
* Validates 'pipe_separated_methods' custom value format.
|
|
7884
|
+
* Used for $method modifier.
|
|
7885
|
+
*
|
|
7886
|
+
* @param modifier Modifier AST node.
|
|
7887
|
+
*
|
|
7888
|
+
* @returns Validation result.
|
|
7889
|
+
*/
|
|
7890
|
+
const validatePipeSeparatedMethods = (modifier) => {
|
|
7891
|
+
return validateListItemsModifier(modifier, (raw) => MethodListParser.parse(raw), isValidMethodModifierValue, customConsistentExceptionsValidator);
|
|
7892
|
+
};
|
|
7893
|
+
/**
|
|
7894
|
+
* Validates 'pipe_separated_stealth_options' custom value format.
|
|
7895
|
+
* Used for $stealth modifier.
|
|
7896
|
+
*
|
|
7897
|
+
* @param modifier Modifier AST node.
|
|
7898
|
+
*
|
|
7899
|
+
* @returns Validation result.
|
|
7900
|
+
*/
|
|
7901
|
+
const validatePipeSeparatedStealthOptions = (modifier) => {
|
|
7902
|
+
return validateListItemsModifier(modifier, (raw) => StealthOptionListParser.parse(raw), isValidStealthModifierValue, customNoNegatedListItemsValidator);
|
|
7903
|
+
};
|
|
7904
|
+
/**
|
|
7905
|
+
* Map of all available pre-defined validators for modifiers with custom `value_format`.
|
|
7906
|
+
*/
|
|
7907
|
+
const CUSTOM_VALUE_FORMAT_MAP = {
|
|
7908
|
+
[CustomValueFormatValidatorName.App]: validatePipeSeparatedApps,
|
|
7909
|
+
[CustomValueFormatValidatorName.DenyAllow]: validatePipeSeparatedDenyAllowDomains,
|
|
7910
|
+
[CustomValueFormatValidatorName.Domain]: validatePipeSeparatedDomains,
|
|
7911
|
+
[CustomValueFormatValidatorName.Method]: validatePipeSeparatedMethods,
|
|
7912
|
+
[CustomValueFormatValidatorName.StealthOption]: validatePipeSeparatedStealthOptions,
|
|
7913
|
+
};
|
|
7914
|
+
/**
|
|
7915
|
+
* Returns whether the given `valueFormat` is a valid custom value format validator name.
|
|
7916
|
+
*
|
|
7917
|
+
* @param valueFormat Value format for the modifier.
|
|
7918
|
+
*
|
|
7919
|
+
* @returns True if `valueFormat` is a supported pre-defined value format validator name, false otherwise.
|
|
7920
|
+
*/
|
|
7921
|
+
const isCustomValueFormatValidator = (valueFormat) => {
|
|
7922
|
+
return Object.keys(CUSTOM_VALUE_FORMAT_MAP).includes(valueFormat);
|
|
7923
|
+
};
|
|
7924
|
+
/**
|
|
7925
|
+
* Checks whether the value for given `modifier` is valid.
|
|
7926
|
+
*
|
|
7927
|
+
* @param modifier Modifier AST node.
|
|
7928
|
+
* @param valueFormat Value format for the modifier.
|
|
7929
|
+
*
|
|
7930
|
+
* @returns Validation result.
|
|
7931
|
+
*/
|
|
7932
|
+
const validateValue = (modifier, valueFormat) => {
|
|
7933
|
+
if (isCustomValueFormatValidator(valueFormat)) {
|
|
7934
|
+
const validator = CUSTOM_VALUE_FORMAT_MAP[valueFormat];
|
|
7935
|
+
return validator(modifier);
|
|
7936
|
+
}
|
|
7937
|
+
const modifierName = modifier.modifier.value;
|
|
7938
|
+
const defaultInvalidValueResult = getValueRequiredValidationResult(modifierName);
|
|
7939
|
+
if (!modifier.value?.value) {
|
|
7940
|
+
return defaultInvalidValueResult;
|
|
7941
|
+
}
|
|
7942
|
+
let xRegExp;
|
|
7943
|
+
try {
|
|
7944
|
+
xRegExp = XRegExp(valueFormat);
|
|
7945
|
+
}
|
|
7946
|
+
catch (e) {
|
|
7947
|
+
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.INVALID_VALUE_FORMAT_REGEXP}: '${modifierName}'`);
|
|
7948
|
+
}
|
|
7949
|
+
const isValid = xRegExp.test(modifier.value?.value);
|
|
7950
|
+
if (!isValid) {
|
|
7951
|
+
return defaultInvalidValueResult;
|
|
7952
|
+
}
|
|
7953
|
+
return { valid: true };
|
|
7954
|
+
};
|
|
7955
|
+
|
|
7956
|
+
/**
|
|
7957
|
+
* @file Validator for modifiers.
|
|
7958
|
+
*/
|
|
7959
|
+
/**
|
|
7960
|
+
* Fully checks whether the given `modifier` valid for given blocker `syntax`:
|
|
7961
|
+
* is it supported by the blocker, deprecated, assignable, negatable, etc.
|
|
7962
|
+
*
|
|
7141
7963
|
* @param modifiersData Parsed all modifiers data map.
|
|
7142
|
-
* @param
|
|
7964
|
+
* @param syntax Adblock syntax to check the modifier for.
|
|
7965
|
+
* 'Common' is not supported, it should be specific — 'AdGuard', 'uBlockOrigin', or 'AdblockPlus'.
|
|
7143
7966
|
* @param modifier Parsed modifier AST node.
|
|
7967
|
+
* @param isException Whether the modifier is used in exception rule.
|
|
7968
|
+
* Needed to check whether the modifier is allowed only in blocking or exception rules.
|
|
7144
7969
|
*
|
|
7145
|
-
* @returns
|
|
7970
|
+
* @returns Result of modifier validation.
|
|
7146
7971
|
*/
|
|
7147
|
-
const
|
|
7148
|
-
|
|
7149
|
-
|
|
7972
|
+
const validateForSpecificSyntax = (modifiersData, syntax, modifier, isException) => {
|
|
7973
|
+
if (syntax === AdblockSyntax.Common) {
|
|
7974
|
+
throw new Error(`Syntax should be specific, '${AdblockSyntax.Common}' is not supported`);
|
|
7975
|
+
}
|
|
7976
|
+
const modifierName = modifier.modifier.value;
|
|
7977
|
+
const blockerPrefix = BLOCKER_PREFIX[syntax];
|
|
7978
|
+
if (!blockerPrefix) {
|
|
7979
|
+
throw new Error(`Unknown syntax: ${syntax}`);
|
|
7980
|
+
}
|
|
7981
|
+
// needed for validation of negation, assignment, etc.
|
|
7982
|
+
const specificBlockerData = getSpecificBlockerData(modifiersData, blockerPrefix, modifierName);
|
|
7983
|
+
// if no specific blocker data is found
|
|
7984
|
+
if (!specificBlockerData) {
|
|
7985
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NOT_SUPPORTED}: '${modifierName}'`);
|
|
7986
|
+
}
|
|
7987
|
+
// e.g. 'object-subrequest'
|
|
7988
|
+
if (specificBlockerData[SpecificKey.Removed]) {
|
|
7989
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.REMOVED}: '${modifierName}'`);
|
|
7990
|
+
}
|
|
7991
|
+
if (specificBlockerData[SpecificKey.Deprecated]) {
|
|
7992
|
+
if (!specificBlockerData[SpecificKey.DeprecationMessage]) {
|
|
7993
|
+
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.NO_DEPRECATION_MESSAGE}: '${modifierName}'`);
|
|
7994
|
+
}
|
|
7995
|
+
// prepare the message which is multiline in the yaml file
|
|
7996
|
+
const warn = specificBlockerData[SpecificKey.DeprecationMessage].replace(NEWLINE, SPACE);
|
|
7997
|
+
return {
|
|
7998
|
+
valid: true,
|
|
7999
|
+
warn,
|
|
8000
|
+
};
|
|
8001
|
+
}
|
|
8002
|
+
if (specificBlockerData[SpecificKey.BlockOnly] && isException) {
|
|
8003
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.BLOCK_ONLY}: '${modifierName}'`);
|
|
8004
|
+
}
|
|
8005
|
+
if (specificBlockerData[SpecificKey.ExceptionOnly] && !isException) {
|
|
8006
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.EXCEPTION_ONLY}: '${modifierName}'`);
|
|
8007
|
+
}
|
|
8008
|
+
// e.g. '~domain=example.com'
|
|
8009
|
+
if (!specificBlockerData[SpecificKey.Negatable] && modifier.exception) {
|
|
8010
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NOT_NEGATABLE_MODIFIER}: '${modifierName}'`);
|
|
8011
|
+
}
|
|
8012
|
+
// e.g. 'domain'
|
|
8013
|
+
if (specificBlockerData[SpecificKey.Assignable]) {
|
|
8014
|
+
if (!modifier.value) {
|
|
8015
|
+
/**
|
|
8016
|
+
* Some assignable modifiers can be used without a value,
|
|
8017
|
+
* e.g. '@@||example.com^$cookie'.
|
|
8018
|
+
*/
|
|
8019
|
+
if (specificBlockerData[SpecificKey.ValueOptional]) {
|
|
8020
|
+
return { valid: true };
|
|
8021
|
+
}
|
|
8022
|
+
// for other assignable modifiers the value is required
|
|
8023
|
+
return getValueRequiredValidationResult(modifierName);
|
|
8024
|
+
}
|
|
8025
|
+
/**
|
|
8026
|
+
* TODO: consider to return `{ valid: true, warn: 'Modifier value may be specified' }` (???)
|
|
8027
|
+
* for $stealth modifier without a value
|
|
8028
|
+
* but only after the extension will support value for $stealth:
|
|
8029
|
+
* https://github.com/AdguardTeam/AdguardBrowserExtension/issues/2107
|
|
8030
|
+
*/
|
|
8031
|
+
if (!specificBlockerData[SpecificKey.ValueFormat]) {
|
|
8032
|
+
throw new Error(`${SOURCE_DATA_ERROR_PREFIX.NO_VALUE_FORMAT_FOR_ASSIGNABLE}: '${modifierName}'`);
|
|
8033
|
+
}
|
|
8034
|
+
return validateValue(modifier, specificBlockerData[SpecificKey.ValueFormat]);
|
|
8035
|
+
}
|
|
8036
|
+
if (modifier?.value) {
|
|
8037
|
+
// e.g. 'third-party=true'
|
|
8038
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_FORBIDDEN}: '${modifierName}'`);
|
|
8039
|
+
}
|
|
8040
|
+
return { valid: true };
|
|
7150
8041
|
};
|
|
7151
8042
|
/**
|
|
7152
|
-
*
|
|
8043
|
+
* Returns documentation URL for given modifier and adblocker.
|
|
7153
8044
|
*
|
|
7154
|
-
* @param
|
|
8045
|
+
* @param modifiersData Parsed all modifiers data map.
|
|
8046
|
+
* @param blockerPrefix Prefix of the adblocker, e.g. 'adg_', 'ubo_', or 'abp_'.
|
|
8047
|
+
* @param modifier Parsed modifier AST node.
|
|
7155
8048
|
*
|
|
7156
|
-
* @returns
|
|
8049
|
+
* @returns Documentation URL or `null` if not found.
|
|
7157
8050
|
*/
|
|
7158
|
-
const
|
|
7159
|
-
|
|
8051
|
+
const getBlockerDocumentationLink = (modifiersData, blockerPrefix, modifier) => {
|
|
8052
|
+
const specificBlockerData = getSpecificBlockerData(modifiersData, blockerPrefix, modifier.modifier.value);
|
|
8053
|
+
return specificBlockerData?.docs || null;
|
|
7160
8054
|
};
|
|
8055
|
+
// TODO: move to modifier.ts and use index.ts only for exporting
|
|
7161
8056
|
/**
|
|
7162
8057
|
* Modifier validator class.
|
|
7163
8058
|
*/
|
|
@@ -7210,18 +8105,18 @@ class ModifierValidator {
|
|
|
7210
8105
|
if (modifier.modifier.value.startsWith(UNDERSCORE)) {
|
|
7211
8106
|
// check whether the modifier value contains something else besides underscores
|
|
7212
8107
|
if (!isValidNoopModifier(modifier.modifier.value)) {
|
|
7213
|
-
return getInvalidValidationResult(`${
|
|
8108
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.INVALID_NOOP}: '${modifier.modifier.value}'`);
|
|
7214
8109
|
}
|
|
7215
8110
|
// otherwise, replace the modifier value with single underscore.
|
|
7216
8111
|
// it is needed to check whether the modifier is supported by specific adblocker due to the syntax
|
|
7217
8112
|
modifier.modifier.value = UNDERSCORE;
|
|
7218
8113
|
}
|
|
7219
8114
|
if (!this.exists(modifier)) {
|
|
7220
|
-
return getInvalidValidationResult(`${
|
|
8115
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.NOT_EXISTENT}: '${modifier.modifier.value}'`);
|
|
7221
8116
|
}
|
|
7222
8117
|
// for 'Common' syntax we cannot check something more
|
|
7223
8118
|
if (syntax === AdblockSyntax.Common) {
|
|
7224
|
-
return {
|
|
8119
|
+
return { valid: true };
|
|
7225
8120
|
}
|
|
7226
8121
|
return validateForSpecificSyntax(this.modifiersData, syntax, modifier, isException);
|
|
7227
8122
|
};
|
|
@@ -7579,7 +8474,7 @@ const MAX_LENGTH = 'max-length';
|
|
|
7579
8474
|
const MIN_LENGTH = 'min-length';
|
|
7580
8475
|
const MIN_TEXT_LENGTH = 'min-text-length';
|
|
7581
8476
|
const TAG_CONTENT = 'tag-content';
|
|
7582
|
-
const WILDCARD
|
|
8477
|
+
const WILDCARD = 'wildcard';
|
|
7583
8478
|
/**
|
|
7584
8479
|
* HTML filtering rule converter class
|
|
7585
8480
|
*
|
|
@@ -7650,7 +8545,7 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
7650
8545
|
maxLength = CssTree.parseAttributeSelectorValueAsNumber(node);
|
|
7651
8546
|
break;
|
|
7652
8547
|
case TAG_CONTENT:
|
|
7653
|
-
case WILDCARD
|
|
8548
|
+
case WILDCARD:
|
|
7654
8549
|
CssTree.assertAttributeSelectorHasStringValue(node);
|
|
7655
8550
|
convertedSelector.children.push(cloneDeep(node));
|
|
7656
8551
|
break;
|
|
@@ -7728,144 +8623,6 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
7728
8623
|
}
|
|
7729
8624
|
}
|
|
7730
8625
|
|
|
7731
|
-
/**
|
|
7732
|
-
* @file Utility functions for working with quotes
|
|
7733
|
-
*/
|
|
7734
|
-
/**
|
|
7735
|
-
* Possible quote types for scriptlet parameters
|
|
7736
|
-
*/
|
|
7737
|
-
var QuoteType;
|
|
7738
|
-
(function (QuoteType) {
|
|
7739
|
-
/**
|
|
7740
|
-
* No quotes at all
|
|
7741
|
-
*/
|
|
7742
|
-
QuoteType["None"] = "none";
|
|
7743
|
-
/**
|
|
7744
|
-
* Single quotes (`'`)
|
|
7745
|
-
*/
|
|
7746
|
-
QuoteType["Single"] = "single";
|
|
7747
|
-
/**
|
|
7748
|
-
* Double quotes (`"`)
|
|
7749
|
-
*/
|
|
7750
|
-
QuoteType["Double"] = "double";
|
|
7751
|
-
})(QuoteType || (QuoteType = {}));
|
|
7752
|
-
/**
|
|
7753
|
-
* Utility functions for working with quotes
|
|
7754
|
-
*/
|
|
7755
|
-
class QuoteUtils {
|
|
7756
|
-
/**
|
|
7757
|
-
* Escape all unescaped occurrences of the character
|
|
7758
|
-
*
|
|
7759
|
-
* @param string String to escape
|
|
7760
|
-
* @param char Character to escape
|
|
7761
|
-
* @returns Escaped string
|
|
7762
|
-
*/
|
|
7763
|
-
static escapeUnescapedOccurrences(string, char) {
|
|
7764
|
-
let result = EMPTY;
|
|
7765
|
-
for (let i = 0; i < string.length; i += 1) {
|
|
7766
|
-
if (string[i] === char && (i === 0 || string[i - 1] !== ESCAPE_CHARACTER)) {
|
|
7767
|
-
result += ESCAPE_CHARACTER;
|
|
7768
|
-
}
|
|
7769
|
-
result += string[i];
|
|
7770
|
-
}
|
|
7771
|
-
return result;
|
|
7772
|
-
}
|
|
7773
|
-
/**
|
|
7774
|
-
* Unescape all single escaped occurrences of the character
|
|
7775
|
-
*
|
|
7776
|
-
* @param string String to unescape
|
|
7777
|
-
* @param char Character to unescape
|
|
7778
|
-
* @returns Unescaped string
|
|
7779
|
-
*/
|
|
7780
|
-
static unescapeSingleEscapedOccurrences(string, char) {
|
|
7781
|
-
let result = EMPTY;
|
|
7782
|
-
for (let i = 0; i < string.length; i += 1) {
|
|
7783
|
-
if (string[i] === char
|
|
7784
|
-
&& string[i - 1] === ESCAPE_CHARACTER
|
|
7785
|
-
&& (i === 1 || string[i - 2] !== ESCAPE_CHARACTER)) {
|
|
7786
|
-
result = result.slice(0, -1);
|
|
7787
|
-
}
|
|
7788
|
-
result += string[i];
|
|
7789
|
-
}
|
|
7790
|
-
return result;
|
|
7791
|
-
}
|
|
7792
|
-
/**
|
|
7793
|
-
* Get quote type of the string
|
|
7794
|
-
*
|
|
7795
|
-
* @param string String to check
|
|
7796
|
-
* @returns Quote type of the string
|
|
7797
|
-
*/
|
|
7798
|
-
static getStringQuoteType(string) {
|
|
7799
|
-
// Don't check 1-character strings to avoid false positives
|
|
7800
|
-
if (string.length > 1) {
|
|
7801
|
-
if (string.startsWith(SINGLE_QUOTE) && string.endsWith(SINGLE_QUOTE)) {
|
|
7802
|
-
return QuoteType.Single;
|
|
7803
|
-
}
|
|
7804
|
-
if (string.startsWith(DOUBLE_QUOTE) && string.endsWith(DOUBLE_QUOTE)) {
|
|
7805
|
-
return QuoteType.Double;
|
|
7806
|
-
}
|
|
7807
|
-
}
|
|
7808
|
-
return QuoteType.None;
|
|
7809
|
-
}
|
|
7810
|
-
/**
|
|
7811
|
-
* Set quote type of the string
|
|
7812
|
-
*
|
|
7813
|
-
* @param string String to set quote type of
|
|
7814
|
-
* @param quoteType Quote type to set
|
|
7815
|
-
* @returns String with the specified quote type
|
|
7816
|
-
*/
|
|
7817
|
-
static setStringQuoteType(string, quoteType) {
|
|
7818
|
-
const actualQuoteType = QuoteUtils.getStringQuoteType(string);
|
|
7819
|
-
switch (quoteType) {
|
|
7820
|
-
case QuoteType.None:
|
|
7821
|
-
if (actualQuoteType === QuoteType.Single) {
|
|
7822
|
-
return QuoteUtils.escapeUnescapedOccurrences(string.slice(1, -1), SINGLE_QUOTE);
|
|
7823
|
-
}
|
|
7824
|
-
if (actualQuoteType === QuoteType.Double) {
|
|
7825
|
-
return QuoteUtils.escapeUnescapedOccurrences(string.slice(1, -1), DOUBLE_QUOTE);
|
|
7826
|
-
}
|
|
7827
|
-
return string;
|
|
7828
|
-
case QuoteType.Single:
|
|
7829
|
-
if (actualQuoteType === QuoteType.None) {
|
|
7830
|
-
return SINGLE_QUOTE + QuoteUtils.escapeUnescapedOccurrences(string, SINGLE_QUOTE) + SINGLE_QUOTE;
|
|
7831
|
-
}
|
|
7832
|
-
if (actualQuoteType === QuoteType.Double) {
|
|
7833
|
-
return SINGLE_QUOTE
|
|
7834
|
-
+ QuoteUtils.escapeUnescapedOccurrences(QuoteUtils.unescapeSingleEscapedOccurrences(string.slice(1, -1), DOUBLE_QUOTE), SINGLE_QUOTE) + SINGLE_QUOTE;
|
|
7835
|
-
}
|
|
7836
|
-
return string;
|
|
7837
|
-
case QuoteType.Double:
|
|
7838
|
-
if (actualQuoteType === QuoteType.None) {
|
|
7839
|
-
return DOUBLE_QUOTE + QuoteUtils.escapeUnescapedOccurrences(string, DOUBLE_QUOTE) + DOUBLE_QUOTE;
|
|
7840
|
-
}
|
|
7841
|
-
if (actualQuoteType !== QuoteType.Double) {
|
|
7842
|
-
// eslint-disable-next-line max-len
|
|
7843
|
-
return DOUBLE_QUOTE
|
|
7844
|
-
+ QuoteUtils.escapeUnescapedOccurrences(QuoteUtils.unescapeSingleEscapedOccurrences(string.slice(1, -1), SINGLE_QUOTE), DOUBLE_QUOTE) + DOUBLE_QUOTE;
|
|
7845
|
-
}
|
|
7846
|
-
return string;
|
|
7847
|
-
default:
|
|
7848
|
-
return string;
|
|
7849
|
-
}
|
|
7850
|
-
}
|
|
7851
|
-
/**
|
|
7852
|
-
* Removes bounding quotes from a string, if any
|
|
7853
|
-
*
|
|
7854
|
-
* @param string Input string
|
|
7855
|
-
* @returns String without quotes
|
|
7856
|
-
*/
|
|
7857
|
-
static removeQuotes(string) {
|
|
7858
|
-
if (
|
|
7859
|
-
// We should check for string length to avoid false positives
|
|
7860
|
-
string.length > 1
|
|
7861
|
-
&& (string[0] === SINGLE_QUOTE || string[0] === DOUBLE_QUOTE)
|
|
7862
|
-
&& string[0] === string[string.length - 1]) {
|
|
7863
|
-
return string.slice(1, -1);
|
|
7864
|
-
}
|
|
7865
|
-
return string;
|
|
7866
|
-
}
|
|
7867
|
-
}
|
|
7868
|
-
|
|
7869
8626
|
/**
|
|
7870
8627
|
* @file Utility functions for working with scriptlet nodes
|
|
7871
8628
|
*/
|
|
@@ -8762,41 +9519,6 @@ class FilterListConverter extends ConverterBase {
|
|
|
8762
9519
|
}
|
|
8763
9520
|
}
|
|
8764
9521
|
|
|
8765
|
-
/**
|
|
8766
|
-
* @file Utility functions for domain and hostname validation.
|
|
8767
|
-
*/
|
|
8768
|
-
const WILDCARD = ASTERISK; // *
|
|
8769
|
-
const WILDCARD_TLD = DOT + WILDCARD; // .*
|
|
8770
|
-
const WILDCARD_SUBDOMAIN = WILDCARD + DOT; // *.
|
|
8771
|
-
class DomainUtils {
|
|
8772
|
-
/**
|
|
8773
|
-
* Check if the input is a valid domain or hostname.
|
|
8774
|
-
*
|
|
8775
|
-
* @param domain Domain to check
|
|
8776
|
-
* @returns `true` if the domain is valid, `false` otherwise
|
|
8777
|
-
*/
|
|
8778
|
-
static isValidDomainOrHostname(domain) {
|
|
8779
|
-
let domainToCheck = domain;
|
|
8780
|
-
// Wildcard-only domain, typically a generic rule
|
|
8781
|
-
if (domainToCheck === WILDCARD) {
|
|
8782
|
-
return true;
|
|
8783
|
-
}
|
|
8784
|
-
// https://adguard.com/kb/general/ad-filtering/create-own-filters/#wildcard-for-tld
|
|
8785
|
-
if (domainToCheck.endsWith(WILDCARD_TLD)) {
|
|
8786
|
-
// Remove the wildcard TLD
|
|
8787
|
-
domainToCheck = domainToCheck.substring(0, domainToCheck.length - WILDCARD_TLD.length);
|
|
8788
|
-
}
|
|
8789
|
-
if (domainToCheck.startsWith(WILDCARD_SUBDOMAIN)) {
|
|
8790
|
-
// Remove the wildcard subdomain
|
|
8791
|
-
domainToCheck = domainToCheck.substring(WILDCARD_SUBDOMAIN.length);
|
|
8792
|
-
}
|
|
8793
|
-
// Parse the domain with tldts
|
|
8794
|
-
const tldtsResult = parse$1(domainToCheck);
|
|
8795
|
-
// Check if the domain is valid
|
|
8796
|
-
return domainToCheck === tldtsResult.domain || domainToCheck === tldtsResult.hostname;
|
|
8797
|
-
}
|
|
8798
|
-
}
|
|
8799
|
-
|
|
8800
9522
|
/**
|
|
8801
9523
|
* @file Utility functions for logical expression AST.
|
|
8802
9524
|
*/
|
|
@@ -8875,7 +9597,7 @@ class LogicalExpressionUtils {
|
|
|
8875
9597
|
}
|
|
8876
9598
|
}
|
|
8877
9599
|
|
|
8878
|
-
const version$1 = "1.1.
|
|
9600
|
+
const version$1 = "1.1.3";
|
|
8879
9601
|
|
|
8880
9602
|
/**
|
|
8881
9603
|
* @file AGTree version
|
|
@@ -8886,4 +9608,4 @@ const version$1 = "1.1.1";
|
|
|
8886
9608
|
// with wrong relative path to `package.json`. So we need this little "hack"
|
|
8887
9609
|
const version = version$1;
|
|
8888
9610
|
|
|
8889
|
-
export { ADBLOCK_URL_SEPARATOR, ADBLOCK_URL_SEPARATOR_REGEX, ADBLOCK_URL_START, ADBLOCK_URL_START_REGEX, ADBLOCK_WILDCARD, ADBLOCK_WILDCARD_REGEX, ADG_SCRIPTLET_MASK, AGLINT_COMMAND_PREFIX, AdblockSyntax, AdblockSyntaxError, AgentCommentRuleParser, AgentParser,
|
|
9611
|
+
export { ADBLOCK_URL_SEPARATOR, ADBLOCK_URL_SEPARATOR_REGEX, ADBLOCK_URL_START, ADBLOCK_URL_START_REGEX, ADBLOCK_WILDCARD, ADBLOCK_WILDCARD_REGEX, ADG_SCRIPTLET_MASK, AGLINT_COMMAND_PREFIX, AdblockSyntax, AdblockSyntaxError, AgentCommentRuleParser, AgentParser, AppListParser, COMMA_DOMAIN_LIST_SEPARATOR, CommentMarker, CommentRuleParser, CommentRuleType, ConfigCommentRuleParser, CosmeticRuleParser, CosmeticRuleSeparator, CosmeticRuleSeparatorUtils, CosmeticRuleType, CssTree, CssTreeNodeType, CssTreeParserContext, DomainListParser, DomainUtils, EXT_CSS_LEGACY_ATTRIBUTES, EXT_CSS_PSEUDO_CLASSES, FORBIDDEN_CSS_FUNCTIONS, FilterListConverter, FilterListParser, HINT_MARKER, HintCommentRuleParser, HintParser, IF, INCLUDE, LogicalExpressionParser, LogicalExpressionUtils, METADATA_HEADERS, MODIFIERS_SEPARATOR, MODIFIER_ASSIGN_OPERATOR, MetadataCommentRuleParser, MethodListParser, ModifierListParser, ModifierParser, NEGATION_MARKER, NETWORK_RULE_EXCEPTION_MARKER, NETWORK_RULE_SEPARATOR, NetworkRuleParser, NotImplementedError, PIPE_MODIFIER_SEPARATOR, PREPROCESSOR_MARKER, ParameterListParser, PreProcessorCommentRuleParser, QuoteType, QuoteUtils, RegExpUtils, RuleCategory, RuleConversionError, RuleConverter, RuleParser, SAFARI_CB_AFFINITY, SPECIAL_REGEX_SYMBOLS, StealthOptionListParser, UBO_SCRIPTLET_MASK, locRange, modifierValidator, shiftLoc, version };
|