@adguard/filters-compiler 3.1.2 → 3.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/dist/build.txt +1 -1
- package/dist/index.cjs +299 -212
- package/dist/index.js +300 -213
- package/package.json +1 -1
package/dist/build.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
version=3.1.
|
|
1
|
+
version=3.1.3
|
package/dist/index.cjs
CHANGED
|
@@ -6,10 +6,10 @@ var fs$1 = require('fs');
|
|
|
6
6
|
var md5 = require('md5');
|
|
7
7
|
var filtersDownloader = require('@adguard/filters-downloader');
|
|
8
8
|
var agtree = require('@adguard/agtree');
|
|
9
|
+
var logger$1 = require('@adguard/logger');
|
|
9
10
|
var fs = require('node:fs');
|
|
10
11
|
var os = require('node:os');
|
|
11
12
|
var path = require('node:path');
|
|
12
|
-
var logger$1 = require('@adguard/logger');
|
|
13
13
|
var parser = require('@adguard/agtree/parser');
|
|
14
14
|
var ecssTree = require('@adguard/ecss-tree');
|
|
15
15
|
var util = require('util');
|
|
@@ -203,6 +203,242 @@ const RuleMasks = {
|
|
|
203
203
|
MASK_TRUSTED_SCRIPTLET: 'trusted-',
|
|
204
204
|
};
|
|
205
205
|
|
|
206
|
+
// TODO: consider refactoring this file
|
|
207
|
+
// because agtree provides modern approach to solve such problems
|
|
208
|
+
|
|
209
|
+
// Based on: https://github.com/github/linguist/pull/5968/commits/f7c5c39139945576a5f9ff0b41c990e6b6019232
|
|
210
|
+
// eslint-disable-next-line max-len
|
|
211
|
+
const ADBLOCK_AGENT_PATTERN = /^(?:!|#)?\s*\[(?<AdblockInfo>\s*(?:[Aa]d[Bb]lock(?:\s+[Pp]lus)?|u[Bb]lock(?:\s+[Oo]rigin)?|[Aa]d[Gg]uard)(?:\s+(?:\d\.?)+)?\s*)(?:;\g<AdblockInfo>)*\]\s*$/;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* CSS rules with width and height attributes break SVG rendering
|
|
215
|
+
* https://github.com/AdguardTeam/AdguardBrowserExtension/issues/683
|
|
216
|
+
*
|
|
217
|
+
* @param ruleText Rule text
|
|
218
|
+
*/
|
|
219
|
+
const fixCssRuleAttributesForEdge = function (ruleText) {
|
|
220
|
+
if (ruleText.includes(RuleMasks.MASK_CSS)
|
|
221
|
+
|| ruleText.includes(RuleMasks.MASK_CSS_EXCEPTION)
|
|
222
|
+
|| ruleText.includes(RuleMasks.MASK_ELEMENT_HIDING)
|
|
223
|
+
|| ruleText.includes(RuleMasks.MASK_ELEMENT_HIDING_EXCEPTION)) {
|
|
224
|
+
ruleText = ruleText.replace(/\[width=/gi, '[Width=');
|
|
225
|
+
ruleText = ruleText.replace('/[height=/gi', '[Height=');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return ruleText;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Updates rule text
|
|
233
|
+
*/
|
|
234
|
+
const overrideRule = function (ruleText, platform) {
|
|
235
|
+
if (platform === 'ext_edge') {
|
|
236
|
+
ruleText = fixCssRuleAttributesForEdge(ruleText);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return ruleText;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Modifies header for AdGuard Base filter
|
|
244
|
+
* https://github.com/AdguardTeam/FiltersCompiler/issues/78
|
|
245
|
+
* @param {array} header
|
|
246
|
+
* @param {boolean} optimized
|
|
247
|
+
*/
|
|
248
|
+
const modifyBaseFilterHeader = (header, optimized) => {
|
|
249
|
+
header[0] = `! Title: AdGuard Base filter + EasyList${optimized ? ' (Optimized)' : ''}`;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Rewrites title and description
|
|
254
|
+
* https://github.com/AdguardTeam/AdguardFilters/issues/5138#issuecomment-328847738
|
|
255
|
+
*/
|
|
256
|
+
const rewriteHeader = function (header) {
|
|
257
|
+
const result = [];
|
|
258
|
+
header.forEach((line) => {
|
|
259
|
+
if (line.startsWith('! Title: ')) {
|
|
260
|
+
line = '! Title: AdGuard Base filter';
|
|
261
|
+
} else if (line.startsWith('! Description: ')) {
|
|
262
|
+
line = '! Description: This filter is necessary for quality ad blocking.';
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
result.push(line);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
return result;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Filters easylist block from list of rules
|
|
273
|
+
* https://github.com/AdguardTeam/AdguardFilters/issues/5138#issuecomment-328847738
|
|
274
|
+
*/
|
|
275
|
+
const rewriteRules = function (rules) {
|
|
276
|
+
const filtered = [];
|
|
277
|
+
let flag = -1;
|
|
278
|
+
for (let i = 0; i < rules.length; i += 1) {
|
|
279
|
+
const rule = rules[i];
|
|
280
|
+
|
|
281
|
+
if (flag >= 0 && rule.startsWith('!------------------')) {
|
|
282
|
+
if (flag !== i - 1) {
|
|
283
|
+
// we skip next line after block header
|
|
284
|
+
// looking for the end of easylist block
|
|
285
|
+
flag = -1;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (rule.startsWith('!------------------ EasyList rules')) {
|
|
292
|
+
flag = i;
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (flag < 0) {
|
|
297
|
+
filtered.push(rule);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return filtered;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Replaces Version: with OriginalVersion: comments in case of some client cannot afford it.
|
|
306
|
+
*
|
|
307
|
+
* @param rules
|
|
308
|
+
*/
|
|
309
|
+
const fixVersionComments = (rules) => {
|
|
310
|
+
return rules.map((x) => {
|
|
311
|
+
if (x.startsWith('! Version:')) {
|
|
312
|
+
return x.replace('! Version:', '! OriginalVersion:');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return x;
|
|
316
|
+
});
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// TODO: use agtree to remove adblock agent strings
|
|
320
|
+
/**
|
|
321
|
+
* Removes adblock agent strings, for example:
|
|
322
|
+
* - [AdBlock],
|
|
323
|
+
* - [Adblock Plus],
|
|
324
|
+
* - [Adblock Plus 2.0],
|
|
325
|
+
* - [AdGuard],
|
|
326
|
+
* - [uBlock] / [uBlock Origin], etc.
|
|
327
|
+
*
|
|
328
|
+
* @param lines
|
|
329
|
+
*/
|
|
330
|
+
const removeAdblockVersion = (lines) => {
|
|
331
|
+
return lines.filter((line) => !line.trim().match(ADBLOCK_AGENT_PATTERN));
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Removes scriptlet rules
|
|
336
|
+
* @param {array} rules
|
|
337
|
+
* @return {array} rules
|
|
338
|
+
*/
|
|
339
|
+
const removeScriptletRules = (rules) => rules.filter((rule) => !rule.script.startsWith(agtree.ADG_SCRIPTLET_MASK));
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Removes `groupDescription` field from `groups`.
|
|
343
|
+
*
|
|
344
|
+
* @param rawGroups
|
|
345
|
+
* @returns Corrected groups
|
|
346
|
+
*/
|
|
347
|
+
const removeGroupDescriptions$1 = function (rawGroups) {
|
|
348
|
+
const groups = [];
|
|
349
|
+
|
|
350
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
351
|
+
for (const g of rawGroups) {
|
|
352
|
+
const copy = { ...g };
|
|
353
|
+
|
|
354
|
+
delete copy.groupDescription;
|
|
355
|
+
|
|
356
|
+
groups.push(copy);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return groups;
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Corrects metadata for backward compatibility with old clients on MAC (v1) platform
|
|
364
|
+
* Hides tag fields
|
|
365
|
+
*
|
|
366
|
+
* @param metadata
|
|
367
|
+
* @returns Corrected metadata
|
|
368
|
+
*/
|
|
369
|
+
const rewriteMetadataForOldMacV1 = function (metadata) {
|
|
370
|
+
const result = {
|
|
371
|
+
groups: removeGroupDescriptions$1(metadata.groups.slice(0)),
|
|
372
|
+
filters: [],
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
376
|
+
for (const f of metadata.filters) {
|
|
377
|
+
const copy = { ...f };
|
|
378
|
+
delete copy.tags;
|
|
379
|
+
delete copy.timeAdded;
|
|
380
|
+
delete copy.trustLevel;
|
|
381
|
+
delete copy.downloadUrl;
|
|
382
|
+
delete copy.deprecated;
|
|
383
|
+
|
|
384
|
+
result.filters.push(copy);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return result;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Corrects metadata for backward compatibility with old clients on MAC_V2 platform —
|
|
392
|
+
* removed `groupDescription` field from `groups`.
|
|
393
|
+
*
|
|
394
|
+
* @param metadata
|
|
395
|
+
* @returns Corrected metadata
|
|
396
|
+
*/
|
|
397
|
+
const rewriteMetadataForOldMacV2 = function (metadata) {
|
|
398
|
+
const result = { ...metadata };
|
|
399
|
+
result.groups = removeGroupDescriptions$1(result.groups.slice(0));
|
|
400
|
+
return result;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Markers of pseudo-classes for HTML filtering rules.
|
|
405
|
+
*
|
|
406
|
+
* Already supported by the AdGuard apps, but not implemented for the extension yet.
|
|
407
|
+
* TODO: AG-24662.
|
|
408
|
+
*
|
|
409
|
+
* @see {@link https://adgkb.service.agrd.dev/kb/general/ad-filtering/create-own-filters/#html-filtering-rules--pseudo-classes}
|
|
410
|
+
*/
|
|
411
|
+
const HTML_RULES_PSEUDO_CLASS_MARKERS = [
|
|
412
|
+
// AdGuard-specific pseudo-classes
|
|
413
|
+
':contains(',
|
|
414
|
+
// aliases
|
|
415
|
+
':-abp-contains(',
|
|
416
|
+
':has-text(',
|
|
417
|
+
];
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Checks if the rule should be kept as is,
|
|
421
|
+
* i.e. no conversion and no validation,
|
|
422
|
+
* since it is not supported by the extension yet.
|
|
423
|
+
*
|
|
424
|
+
* Conditions for keeping the rule as is:
|
|
425
|
+
* - rule is HTML filtering rule
|
|
426
|
+
* - rule is AdGuard syntax
|
|
427
|
+
* - rule contains pseudo-class marker.
|
|
428
|
+
*
|
|
429
|
+
* Planned to be fixed:
|
|
430
|
+
* https://github.com/AdguardTeam/tsurlfilter/issues/96.
|
|
431
|
+
*
|
|
432
|
+
* @param {import('@adguard/agtree').AnyRule} ruleNode Rule node to check.
|
|
433
|
+
*
|
|
434
|
+
* @returns {boolean} True if the rule should be kept as is, otherwise false.
|
|
435
|
+
*/
|
|
436
|
+
const shouldKeepAdgHtmlFilteringRuleAsIs = (ruleNode) => {
|
|
437
|
+
return ruleNode.type === agtree.CosmeticRuleType.HtmlFilteringRule
|
|
438
|
+
&& ruleNode.syntax === agtree.AdblockSyntax.Adg
|
|
439
|
+
&& HTML_RULES_PSEUDO_CLASS_MARKERS.some((marker) => ruleNode.body.value.includes(marker));
|
|
440
|
+
};
|
|
441
|
+
|
|
206
442
|
/**
|
|
207
443
|
* Excludes rule
|
|
208
444
|
* @param {string} rule
|
|
@@ -218,29 +454,50 @@ const excludeRule$1 = (rule, excluded, message) => {
|
|
|
218
454
|
|
|
219
455
|
/**
|
|
220
456
|
* Converts rules to AdGuard syntax
|
|
221
|
-
*
|
|
222
|
-
* @param {
|
|
223
|
-
* @
|
|
457
|
+
*
|
|
458
|
+
* @param {string[]} rulesList Input rules.
|
|
459
|
+
* @param {string[]} [excluded=[]] Rules to exclude.
|
|
460
|
+
* @param {string[]} [invalidRules=[]] Collected invalid rules for the report file.
|
|
461
|
+
*
|
|
462
|
+
* @return {string[]} Rules converted to AdGuard syntax.
|
|
224
463
|
*/
|
|
225
|
-
const convertRulesToAdgSyntax = (rulesList, excluded) => {
|
|
464
|
+
const convertRulesToAdgSyntax = (rulesList, excluded = [], invalidRules = []) => {
|
|
226
465
|
const result = [];
|
|
227
466
|
|
|
228
467
|
for (let i = 0; i < rulesList.length; i += 1) {
|
|
229
468
|
const rule = rulesList[i];
|
|
230
469
|
try {
|
|
231
470
|
const ruleNode = agtree.RuleParser.parse(rule);
|
|
471
|
+
|
|
472
|
+
// temporary workaround for AdGuard's HTML filtering rules with pseudo-classes.
|
|
473
|
+
// TODO: remove during AG-24662 resolving
|
|
474
|
+
if (shouldKeepAdgHtmlFilteringRuleAsIs(ruleNode)) {
|
|
475
|
+
const message = `Keeping HTML filtering rule with pseudo-classes as is: "${rule}"`;
|
|
476
|
+
logger.warn(message);
|
|
477
|
+
result.push(rule);
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
|
|
232
481
|
const conversionResult = agtree.RuleConverter.convertToAdg(ruleNode);
|
|
233
482
|
const convertedRules = conversionResult.result.map((r) => agtree.RuleGenerator.generate(r));
|
|
234
483
|
result.push(...convertedRules);
|
|
235
484
|
|
|
236
485
|
if (conversionResult.isConverted) {
|
|
237
|
-
const
|
|
486
|
+
const messagePostfix = convertedRules.length > 1
|
|
487
|
+
// eslint-disable-next-line quotes
|
|
488
|
+
? `to multiple rules: "${convertedRules.join(", ")}"`
|
|
489
|
+
: `to: "${convertedRules[0]}"`;
|
|
490
|
+
const message = `Rule "${rule}" converted ${messagePostfix}`;
|
|
238
491
|
excludeRule$1(rule, excluded, message);
|
|
239
492
|
}
|
|
240
493
|
} catch (e) {
|
|
241
|
-
|
|
494
|
+
// eslint-disable-next-line max-len
|
|
495
|
+
const message = `Error: Unable to convert rule to AdGuard syntax: "${rule}" due to error: ${logger$1.getErrorMessage(e)}`;
|
|
242
496
|
logger.error(message);
|
|
243
497
|
excludeRule$1(rule, excluded, message);
|
|
498
|
+
|
|
499
|
+
// collect the invalid rule for the report file
|
|
500
|
+
invalidRules.push(message);
|
|
244
501
|
}
|
|
245
502
|
}
|
|
246
503
|
|
|
@@ -614,6 +871,13 @@ const validateAndFilterRules = (list, excluded, invalid = [], filterName) => {
|
|
|
614
871
|
parseAbpSpecificRules: false,
|
|
615
872
|
parseUboSpecificRules: false,
|
|
616
873
|
});
|
|
874
|
+
|
|
875
|
+
// temporary workaround for AdGuard's HTML filtering rules with pseudo-classes.
|
|
876
|
+
// TODO: remove during AG-24662 resolving
|
|
877
|
+
if (shouldKeepAdgHtmlFilteringRuleAsIs(ruleNode)) {
|
|
878
|
+
return true;
|
|
879
|
+
}
|
|
880
|
+
|
|
617
881
|
const conversionResult = agtree.RuleConverter.convertToAdg(ruleNode);
|
|
618
882
|
convertedRuleNodes = conversionResult.result;
|
|
619
883
|
// eslint-disable-next-line no-restricted-syntax
|
|
@@ -642,9 +906,10 @@ const validateAndFilterRules = (list, excluded, invalid = [], filterName) => {
|
|
|
642
906
|
}
|
|
643
907
|
}
|
|
644
908
|
} catch (e) {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
909
|
+
const message = `Error: Invalid rule in ${filterName}: "${ruleText}" due to error: ${logger$1.getErrorMessage(e)}`;
|
|
910
|
+
logger.error(message);
|
|
911
|
+
excludeRule(excluded, message, ruleText);
|
|
912
|
+
invalid.push(message);
|
|
648
913
|
return false;
|
|
649
914
|
}
|
|
650
915
|
|
|
@@ -729,203 +994,6 @@ const checkAffinityDirectives = (lines) => {
|
|
|
729
994
|
return !stack.length;
|
|
730
995
|
};
|
|
731
996
|
|
|
732
|
-
// TODO: consider refactoring this file
|
|
733
|
-
// because agtree provides modern approach to solve such problems
|
|
734
|
-
|
|
735
|
-
// Based on: https://github.com/github/linguist/pull/5968/commits/f7c5c39139945576a5f9ff0b41c990e6b6019232
|
|
736
|
-
// eslint-disable-next-line max-len
|
|
737
|
-
const ADBLOCK_AGENT_PATTERN = /^(?:!|#)?\s*\[(?<AdblockInfo>\s*(?:[Aa]d[Bb]lock(?:\s+[Pp]lus)?|u[Bb]lock(?:\s+[Oo]rigin)?|[Aa]d[Gg]uard)(?:\s+(?:\d\.?)+)?\s*)(?:;\g<AdblockInfo>)*\]\s*$/;
|
|
738
|
-
|
|
739
|
-
/**
|
|
740
|
-
* CSS rules with width and height attributes break SVG rendering
|
|
741
|
-
* https://github.com/AdguardTeam/AdguardBrowserExtension/issues/683
|
|
742
|
-
*
|
|
743
|
-
* @param ruleText Rule text
|
|
744
|
-
*/
|
|
745
|
-
const fixCssRuleAttributesForEdge = function (ruleText) {
|
|
746
|
-
if (ruleText.includes(RuleMasks.MASK_CSS)
|
|
747
|
-
|| ruleText.includes(RuleMasks.MASK_CSS_EXCEPTION)
|
|
748
|
-
|| ruleText.includes(RuleMasks.MASK_ELEMENT_HIDING)
|
|
749
|
-
|| ruleText.includes(RuleMasks.MASK_ELEMENT_HIDING_EXCEPTION)) {
|
|
750
|
-
ruleText = ruleText.replace(/\[width=/gi, '[Width=');
|
|
751
|
-
ruleText = ruleText.replace('/[height=/gi', '[Height=');
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
return ruleText;
|
|
755
|
-
};
|
|
756
|
-
|
|
757
|
-
/**
|
|
758
|
-
* Updates rule text
|
|
759
|
-
*/
|
|
760
|
-
const overrideRule = function (ruleText, platform) {
|
|
761
|
-
if (platform === 'ext_edge') {
|
|
762
|
-
ruleText = fixCssRuleAttributesForEdge(ruleText);
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
return ruleText;
|
|
766
|
-
};
|
|
767
|
-
|
|
768
|
-
/**
|
|
769
|
-
* Modifies header for AdGuard Base filter
|
|
770
|
-
* https://github.com/AdguardTeam/FiltersCompiler/issues/78
|
|
771
|
-
* @param {array} header
|
|
772
|
-
* @param {boolean} optimized
|
|
773
|
-
*/
|
|
774
|
-
const modifyBaseFilterHeader = (header, optimized) => {
|
|
775
|
-
header[0] = `! Title: AdGuard Base filter + EasyList${optimized ? ' (Optimized)' : ''}`;
|
|
776
|
-
};
|
|
777
|
-
|
|
778
|
-
/**
|
|
779
|
-
* Rewrites title and description
|
|
780
|
-
* https://github.com/AdguardTeam/AdguardFilters/issues/5138#issuecomment-328847738
|
|
781
|
-
*/
|
|
782
|
-
const rewriteHeader = function (header) {
|
|
783
|
-
const result = [];
|
|
784
|
-
header.forEach((line) => {
|
|
785
|
-
if (line.startsWith('! Title: ')) {
|
|
786
|
-
line = '! Title: AdGuard Base filter';
|
|
787
|
-
} else if (line.startsWith('! Description: ')) {
|
|
788
|
-
line = '! Description: This filter is necessary for quality ad blocking.';
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
result.push(line);
|
|
792
|
-
});
|
|
793
|
-
|
|
794
|
-
return result;
|
|
795
|
-
};
|
|
796
|
-
|
|
797
|
-
/**
|
|
798
|
-
* Filters easylist block from list of rules
|
|
799
|
-
* https://github.com/AdguardTeam/AdguardFilters/issues/5138#issuecomment-328847738
|
|
800
|
-
*/
|
|
801
|
-
const rewriteRules = function (rules) {
|
|
802
|
-
const filtered = [];
|
|
803
|
-
let flag = -1;
|
|
804
|
-
for (let i = 0; i < rules.length; i += 1) {
|
|
805
|
-
const rule = rules[i];
|
|
806
|
-
|
|
807
|
-
if (flag >= 0 && rule.startsWith('!------------------')) {
|
|
808
|
-
if (flag !== i - 1) {
|
|
809
|
-
// we skip next line after block header
|
|
810
|
-
// looking for the end of easylist block
|
|
811
|
-
flag = -1;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
continue;
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
if (rule.startsWith('!------------------ EasyList rules')) {
|
|
818
|
-
flag = i;
|
|
819
|
-
continue;
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
if (flag < 0) {
|
|
823
|
-
filtered.push(rule);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
return filtered;
|
|
828
|
-
};
|
|
829
|
-
|
|
830
|
-
/**
|
|
831
|
-
* Replaces Version: with OriginalVersion: comments in case of some client cannot afford it.
|
|
832
|
-
*
|
|
833
|
-
* @param rules
|
|
834
|
-
*/
|
|
835
|
-
const fixVersionComments = (rules) => {
|
|
836
|
-
return rules.map((x) => {
|
|
837
|
-
if (x.startsWith('! Version:')) {
|
|
838
|
-
return x.replace('! Version:', '! OriginalVersion:');
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
return x;
|
|
842
|
-
});
|
|
843
|
-
};
|
|
844
|
-
|
|
845
|
-
// TODO: use agtree to remove adblock agent strings
|
|
846
|
-
/**
|
|
847
|
-
* Removes adblock agent strings, for example:
|
|
848
|
-
* - [AdBlock],
|
|
849
|
-
* - [Adblock Plus],
|
|
850
|
-
* - [Adblock Plus 2.0],
|
|
851
|
-
* - [AdGuard],
|
|
852
|
-
* - [uBlock] / [uBlock Origin], etc.
|
|
853
|
-
*
|
|
854
|
-
* @param lines
|
|
855
|
-
*/
|
|
856
|
-
const removeAdblockVersion = (lines) => {
|
|
857
|
-
return lines.filter((line) => !line.trim().match(ADBLOCK_AGENT_PATTERN));
|
|
858
|
-
};
|
|
859
|
-
|
|
860
|
-
/**
|
|
861
|
-
* Removes scriptlet rules
|
|
862
|
-
* @param {array} rules
|
|
863
|
-
* @return {array} rules
|
|
864
|
-
*/
|
|
865
|
-
const removeScriptletRules = (rules) => rules.filter((rule) => !rule.script.startsWith(agtree.ADG_SCRIPTLET_MASK));
|
|
866
|
-
|
|
867
|
-
/**
|
|
868
|
-
* Removes `groupDescription` field from `groups`.
|
|
869
|
-
*
|
|
870
|
-
* @param rawGroups
|
|
871
|
-
* @returns Corrected groups
|
|
872
|
-
*/
|
|
873
|
-
const removeGroupDescriptions$1 = function (rawGroups) {
|
|
874
|
-
const groups = [];
|
|
875
|
-
|
|
876
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
877
|
-
for (const g of rawGroups) {
|
|
878
|
-
const copy = { ...g };
|
|
879
|
-
|
|
880
|
-
delete copy.groupDescription;
|
|
881
|
-
|
|
882
|
-
groups.push(copy);
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
return groups;
|
|
886
|
-
};
|
|
887
|
-
|
|
888
|
-
/**
|
|
889
|
-
* Corrects metadata for backward compatibility with old clients on MAC (v1) platform
|
|
890
|
-
* Hides tag fields
|
|
891
|
-
*
|
|
892
|
-
* @param metadata
|
|
893
|
-
* @returns Corrected metadata
|
|
894
|
-
*/
|
|
895
|
-
const rewriteMetadataForOldMacV1 = function (metadata) {
|
|
896
|
-
const result = {
|
|
897
|
-
groups: removeGroupDescriptions$1(metadata.groups.slice(0)),
|
|
898
|
-
filters: [],
|
|
899
|
-
};
|
|
900
|
-
|
|
901
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
902
|
-
for (const f of metadata.filters) {
|
|
903
|
-
const copy = { ...f };
|
|
904
|
-
delete copy.tags;
|
|
905
|
-
delete copy.timeAdded;
|
|
906
|
-
delete copy.trustLevel;
|
|
907
|
-
delete copy.downloadUrl;
|
|
908
|
-
delete copy.deprecated;
|
|
909
|
-
|
|
910
|
-
result.filters.push(copy);
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
return result;
|
|
914
|
-
};
|
|
915
|
-
|
|
916
|
-
/**
|
|
917
|
-
* Corrects metadata for backward compatibility with old clients on MAC_V2 platform —
|
|
918
|
-
* removed `groupDescription` field from `groups`.
|
|
919
|
-
*
|
|
920
|
-
* @param metadata
|
|
921
|
-
* @returns Corrected metadata
|
|
922
|
-
*/
|
|
923
|
-
const rewriteMetadataForOldMacV2 = function (metadata) {
|
|
924
|
-
const result = { ...metadata };
|
|
925
|
-
result.groups = removeGroupDescriptions$1(result.groups.slice(0));
|
|
926
|
-
return result;
|
|
927
|
-
};
|
|
928
|
-
|
|
929
997
|
/* eslint-disable global-require */
|
|
930
998
|
|
|
931
999
|
const require$1 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
@@ -3155,13 +3223,20 @@ const getResolvedPreprocessorIncludes = async function (filterDir, filterName, l
|
|
|
3155
3223
|
* @param {string} filterDir Filter directory.
|
|
3156
3224
|
* @param {string} filterName Filter name.
|
|
3157
3225
|
* @param {string[]} lines Array of raw rules and possibly preprocessor `!#include` directives.
|
|
3158
|
-
* @param {string} excluded Array of rules to exclude.
|
|
3226
|
+
* @param {string[]} excluded Array of rules to exclude.
|
|
3227
|
+
* @param {string[]} invalidRules Invalid rules for the report file.
|
|
3159
3228
|
*
|
|
3160
3229
|
* @returns {Promise<string[]>} Promise which resolves to array of AdGuard syntax rules.
|
|
3161
3230
|
*/
|
|
3162
|
-
const prepareAdgRules = async function (
|
|
3231
|
+
const prepareAdgRules = async function (
|
|
3232
|
+
filterDir,
|
|
3233
|
+
filterName,
|
|
3234
|
+
lines,
|
|
3235
|
+
excluded,
|
|
3236
|
+
invalidRules,
|
|
3237
|
+
) {
|
|
3163
3238
|
const resolvedIncludes = await getResolvedPreprocessorIncludes(filterDir, filterName, lines);
|
|
3164
|
-
return convertRulesToAdgSyntax(resolvedIncludes, excluded);
|
|
3239
|
+
return convertRulesToAdgSyntax(resolvedIncludes, excluded, invalidRules);
|
|
3165
3240
|
};
|
|
3166
3241
|
|
|
3167
3242
|
/**
|
|
@@ -3227,7 +3302,13 @@ const compile$1 = async function (filterDir, filterName, templateContent, trustL
|
|
|
3227
3302
|
includedLines.forEach((line) => includedRules.push(line.trim()));
|
|
3228
3303
|
|
|
3229
3304
|
// eslint-disable-next-line no-await-in-loop
|
|
3230
|
-
includedRules = await prepareAdgRules(
|
|
3305
|
+
includedRules = await prepareAdgRules(
|
|
3306
|
+
filterDir,
|
|
3307
|
+
filterName,
|
|
3308
|
+
includedRules,
|
|
3309
|
+
excluded,
|
|
3310
|
+
invalid,
|
|
3311
|
+
);
|
|
3231
3312
|
|
|
3232
3313
|
if (shouldIgnoreTrustLevel) {
|
|
3233
3314
|
logger.info(`Ignoring trust level for ${filterName} due to @include directive: ${line}`);
|
|
@@ -3245,7 +3326,13 @@ const compile$1 = async function (filterDir, filterName, templateContent, trustL
|
|
|
3245
3326
|
let inlineRules = [line.trim()];
|
|
3246
3327
|
|
|
3247
3328
|
// eslint-disable-next-line no-await-in-loop
|
|
3248
|
-
inlineRules = await prepareAdgRules(
|
|
3329
|
+
inlineRules = await prepareAdgRules(
|
|
3330
|
+
filterDir,
|
|
3331
|
+
filterName,
|
|
3332
|
+
inlineRules,
|
|
3333
|
+
excluded,
|
|
3334
|
+
invalid,
|
|
3335
|
+
);
|
|
3249
3336
|
|
|
3250
3337
|
logger.info('Applying trust-level exclusions to inline template.txt rules...');
|
|
3251
3338
|
inlineRules = exclude(inlineRules, trustLevelSettings, excluded);
|
package/dist/index.js
CHANGED
|
@@ -3,11 +3,11 @@ import { RuleFactory, CosmeticRule, NetworkRule, setConfiguration, Compatibility
|
|
|
3
3
|
import fs$1 from 'fs';
|
|
4
4
|
import md5 from 'md5';
|
|
5
5
|
import { FiltersDownloader } from '@adguard/filters-downloader';
|
|
6
|
-
import { RuleParser, RuleConverter, RuleGenerator,
|
|
6
|
+
import { ADG_SCRIPTLET_MASK, CosmeticRuleType, AdblockSyntax, RuleParser, RuleConverter, RuleGenerator, CommentParser, RuleCategory, RegExpUtils } from '@adguard/agtree';
|
|
7
|
+
import { Logger, getErrorMessage } from '@adguard/logger';
|
|
7
8
|
import fs from 'node:fs';
|
|
8
9
|
import os from 'node:os';
|
|
9
10
|
import path from 'node:path';
|
|
10
|
-
import { Logger } from '@adguard/logger';
|
|
11
11
|
import { defaultParserOptions } from '@adguard/agtree/parser';
|
|
12
12
|
import { parse } from '@adguard/ecss-tree';
|
|
13
13
|
import { TextEncoder, TextDecoder } from 'util';
|
|
@@ -200,6 +200,242 @@ const RuleMasks = {
|
|
|
200
200
|
MASK_TRUSTED_SCRIPTLET: 'trusted-',
|
|
201
201
|
};
|
|
202
202
|
|
|
203
|
+
// TODO: consider refactoring this file
|
|
204
|
+
// because agtree provides modern approach to solve such problems
|
|
205
|
+
|
|
206
|
+
// Based on: https://github.com/github/linguist/pull/5968/commits/f7c5c39139945576a5f9ff0b41c990e6b6019232
|
|
207
|
+
// eslint-disable-next-line max-len
|
|
208
|
+
const ADBLOCK_AGENT_PATTERN = /^(?:!|#)?\s*\[(?<AdblockInfo>\s*(?:[Aa]d[Bb]lock(?:\s+[Pp]lus)?|u[Bb]lock(?:\s+[Oo]rigin)?|[Aa]d[Gg]uard)(?:\s+(?:\d\.?)+)?\s*)(?:;\g<AdblockInfo>)*\]\s*$/;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* CSS rules with width and height attributes break SVG rendering
|
|
212
|
+
* https://github.com/AdguardTeam/AdguardBrowserExtension/issues/683
|
|
213
|
+
*
|
|
214
|
+
* @param ruleText Rule text
|
|
215
|
+
*/
|
|
216
|
+
const fixCssRuleAttributesForEdge = function (ruleText) {
|
|
217
|
+
if (ruleText.includes(RuleMasks.MASK_CSS)
|
|
218
|
+
|| ruleText.includes(RuleMasks.MASK_CSS_EXCEPTION)
|
|
219
|
+
|| ruleText.includes(RuleMasks.MASK_ELEMENT_HIDING)
|
|
220
|
+
|| ruleText.includes(RuleMasks.MASK_ELEMENT_HIDING_EXCEPTION)) {
|
|
221
|
+
ruleText = ruleText.replace(/\[width=/gi, '[Width=');
|
|
222
|
+
ruleText = ruleText.replace('/[height=/gi', '[Height=');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return ruleText;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Updates rule text
|
|
230
|
+
*/
|
|
231
|
+
const overrideRule = function (ruleText, platform) {
|
|
232
|
+
if (platform === 'ext_edge') {
|
|
233
|
+
ruleText = fixCssRuleAttributesForEdge(ruleText);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return ruleText;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Modifies header for AdGuard Base filter
|
|
241
|
+
* https://github.com/AdguardTeam/FiltersCompiler/issues/78
|
|
242
|
+
* @param {array} header
|
|
243
|
+
* @param {boolean} optimized
|
|
244
|
+
*/
|
|
245
|
+
const modifyBaseFilterHeader = (header, optimized) => {
|
|
246
|
+
header[0] = `! Title: AdGuard Base filter + EasyList${optimized ? ' (Optimized)' : ''}`;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Rewrites title and description
|
|
251
|
+
* https://github.com/AdguardTeam/AdguardFilters/issues/5138#issuecomment-328847738
|
|
252
|
+
*/
|
|
253
|
+
const rewriteHeader = function (header) {
|
|
254
|
+
const result = [];
|
|
255
|
+
header.forEach((line) => {
|
|
256
|
+
if (line.startsWith('! Title: ')) {
|
|
257
|
+
line = '! Title: AdGuard Base filter';
|
|
258
|
+
} else if (line.startsWith('! Description: ')) {
|
|
259
|
+
line = '! Description: This filter is necessary for quality ad blocking.';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
result.push(line);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
return result;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Filters easylist block from list of rules
|
|
270
|
+
* https://github.com/AdguardTeam/AdguardFilters/issues/5138#issuecomment-328847738
|
|
271
|
+
*/
|
|
272
|
+
const rewriteRules = function (rules) {
|
|
273
|
+
const filtered = [];
|
|
274
|
+
let flag = -1;
|
|
275
|
+
for (let i = 0; i < rules.length; i += 1) {
|
|
276
|
+
const rule = rules[i];
|
|
277
|
+
|
|
278
|
+
if (flag >= 0 && rule.startsWith('!------------------')) {
|
|
279
|
+
if (flag !== i - 1) {
|
|
280
|
+
// we skip next line after block header
|
|
281
|
+
// looking for the end of easylist block
|
|
282
|
+
flag = -1;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (rule.startsWith('!------------------ EasyList rules')) {
|
|
289
|
+
flag = i;
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (flag < 0) {
|
|
294
|
+
filtered.push(rule);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return filtered;
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Replaces Version: with OriginalVersion: comments in case of some client cannot afford it.
|
|
303
|
+
*
|
|
304
|
+
* @param rules
|
|
305
|
+
*/
|
|
306
|
+
const fixVersionComments = (rules) => {
|
|
307
|
+
return rules.map((x) => {
|
|
308
|
+
if (x.startsWith('! Version:')) {
|
|
309
|
+
return x.replace('! Version:', '! OriginalVersion:');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return x;
|
|
313
|
+
});
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// TODO: use agtree to remove adblock agent strings
|
|
317
|
+
/**
|
|
318
|
+
* Removes adblock agent strings, for example:
|
|
319
|
+
* - [AdBlock],
|
|
320
|
+
* - [Adblock Plus],
|
|
321
|
+
* - [Adblock Plus 2.0],
|
|
322
|
+
* - [AdGuard],
|
|
323
|
+
* - [uBlock] / [uBlock Origin], etc.
|
|
324
|
+
*
|
|
325
|
+
* @param lines
|
|
326
|
+
*/
|
|
327
|
+
const removeAdblockVersion = (lines) => {
|
|
328
|
+
return lines.filter((line) => !line.trim().match(ADBLOCK_AGENT_PATTERN));
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Removes scriptlet rules
|
|
333
|
+
* @param {array} rules
|
|
334
|
+
* @return {array} rules
|
|
335
|
+
*/
|
|
336
|
+
const removeScriptletRules = (rules) => rules.filter((rule) => !rule.script.startsWith(ADG_SCRIPTLET_MASK));
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Removes `groupDescription` field from `groups`.
|
|
340
|
+
*
|
|
341
|
+
* @param rawGroups
|
|
342
|
+
* @returns Corrected groups
|
|
343
|
+
*/
|
|
344
|
+
const removeGroupDescriptions$1 = function (rawGroups) {
|
|
345
|
+
const groups = [];
|
|
346
|
+
|
|
347
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
348
|
+
for (const g of rawGroups) {
|
|
349
|
+
const copy = { ...g };
|
|
350
|
+
|
|
351
|
+
delete copy.groupDescription;
|
|
352
|
+
|
|
353
|
+
groups.push(copy);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return groups;
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Corrects metadata for backward compatibility with old clients on MAC (v1) platform
|
|
361
|
+
* Hides tag fields
|
|
362
|
+
*
|
|
363
|
+
* @param metadata
|
|
364
|
+
* @returns Corrected metadata
|
|
365
|
+
*/
|
|
366
|
+
const rewriteMetadataForOldMacV1 = function (metadata) {
|
|
367
|
+
const result = {
|
|
368
|
+
groups: removeGroupDescriptions$1(metadata.groups.slice(0)),
|
|
369
|
+
filters: [],
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
373
|
+
for (const f of metadata.filters) {
|
|
374
|
+
const copy = { ...f };
|
|
375
|
+
delete copy.tags;
|
|
376
|
+
delete copy.timeAdded;
|
|
377
|
+
delete copy.trustLevel;
|
|
378
|
+
delete copy.downloadUrl;
|
|
379
|
+
delete copy.deprecated;
|
|
380
|
+
|
|
381
|
+
result.filters.push(copy);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return result;
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Corrects metadata for backward compatibility with old clients on MAC_V2 platform —
|
|
389
|
+
* removed `groupDescription` field from `groups`.
|
|
390
|
+
*
|
|
391
|
+
* @param metadata
|
|
392
|
+
* @returns Corrected metadata
|
|
393
|
+
*/
|
|
394
|
+
const rewriteMetadataForOldMacV2 = function (metadata) {
|
|
395
|
+
const result = { ...metadata };
|
|
396
|
+
result.groups = removeGroupDescriptions$1(result.groups.slice(0));
|
|
397
|
+
return result;
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Markers of pseudo-classes for HTML filtering rules.
|
|
402
|
+
*
|
|
403
|
+
* Already supported by the AdGuard apps, but not implemented for the extension yet.
|
|
404
|
+
* TODO: AG-24662.
|
|
405
|
+
*
|
|
406
|
+
* @see {@link https://adgkb.service.agrd.dev/kb/general/ad-filtering/create-own-filters/#html-filtering-rules--pseudo-classes}
|
|
407
|
+
*/
|
|
408
|
+
const HTML_RULES_PSEUDO_CLASS_MARKERS = [
|
|
409
|
+
// AdGuard-specific pseudo-classes
|
|
410
|
+
':contains(',
|
|
411
|
+
// aliases
|
|
412
|
+
':-abp-contains(',
|
|
413
|
+
':has-text(',
|
|
414
|
+
];
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Checks if the rule should be kept as is,
|
|
418
|
+
* i.e. no conversion and no validation,
|
|
419
|
+
* since it is not supported by the extension yet.
|
|
420
|
+
*
|
|
421
|
+
* Conditions for keeping the rule as is:
|
|
422
|
+
* - rule is HTML filtering rule
|
|
423
|
+
* - rule is AdGuard syntax
|
|
424
|
+
* - rule contains pseudo-class marker.
|
|
425
|
+
*
|
|
426
|
+
* Planned to be fixed:
|
|
427
|
+
* https://github.com/AdguardTeam/tsurlfilter/issues/96.
|
|
428
|
+
*
|
|
429
|
+
* @param {import('@adguard/agtree').AnyRule} ruleNode Rule node to check.
|
|
430
|
+
*
|
|
431
|
+
* @returns {boolean} True if the rule should be kept as is, otherwise false.
|
|
432
|
+
*/
|
|
433
|
+
const shouldKeepAdgHtmlFilteringRuleAsIs = (ruleNode) => {
|
|
434
|
+
return ruleNode.type === CosmeticRuleType.HtmlFilteringRule
|
|
435
|
+
&& ruleNode.syntax === AdblockSyntax.Adg
|
|
436
|
+
&& HTML_RULES_PSEUDO_CLASS_MARKERS.some((marker) => ruleNode.body.value.includes(marker));
|
|
437
|
+
};
|
|
438
|
+
|
|
203
439
|
/**
|
|
204
440
|
* Excludes rule
|
|
205
441
|
* @param {string} rule
|
|
@@ -215,29 +451,50 @@ const excludeRule$1 = (rule, excluded, message) => {
|
|
|
215
451
|
|
|
216
452
|
/**
|
|
217
453
|
* Converts rules to AdGuard syntax
|
|
218
|
-
*
|
|
219
|
-
* @param {
|
|
220
|
-
* @
|
|
454
|
+
*
|
|
455
|
+
* @param {string[]} rulesList Input rules.
|
|
456
|
+
* @param {string[]} [excluded=[]] Rules to exclude.
|
|
457
|
+
* @param {string[]} [invalidRules=[]] Collected invalid rules for the report file.
|
|
458
|
+
*
|
|
459
|
+
* @return {string[]} Rules converted to AdGuard syntax.
|
|
221
460
|
*/
|
|
222
|
-
const convertRulesToAdgSyntax = (rulesList, excluded) => {
|
|
461
|
+
const convertRulesToAdgSyntax = (rulesList, excluded = [], invalidRules = []) => {
|
|
223
462
|
const result = [];
|
|
224
463
|
|
|
225
464
|
for (let i = 0; i < rulesList.length; i += 1) {
|
|
226
465
|
const rule = rulesList[i];
|
|
227
466
|
try {
|
|
228
467
|
const ruleNode = RuleParser.parse(rule);
|
|
468
|
+
|
|
469
|
+
// temporary workaround for AdGuard's HTML filtering rules with pseudo-classes.
|
|
470
|
+
// TODO: remove during AG-24662 resolving
|
|
471
|
+
if (shouldKeepAdgHtmlFilteringRuleAsIs(ruleNode)) {
|
|
472
|
+
const message = `Keeping HTML filtering rule with pseudo-classes as is: "${rule}"`;
|
|
473
|
+
logger.warn(message);
|
|
474
|
+
result.push(rule);
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
|
|
229
478
|
const conversionResult = RuleConverter.convertToAdg(ruleNode);
|
|
230
479
|
const convertedRules = conversionResult.result.map((r) => RuleGenerator.generate(r));
|
|
231
480
|
result.push(...convertedRules);
|
|
232
481
|
|
|
233
482
|
if (conversionResult.isConverted) {
|
|
234
|
-
const
|
|
483
|
+
const messagePostfix = convertedRules.length > 1
|
|
484
|
+
// eslint-disable-next-line quotes
|
|
485
|
+
? `to multiple rules: "${convertedRules.join(", ")}"`
|
|
486
|
+
: `to: "${convertedRules[0]}"`;
|
|
487
|
+
const message = `Rule "${rule}" converted ${messagePostfix}`;
|
|
235
488
|
excludeRule$1(rule, excluded, message);
|
|
236
489
|
}
|
|
237
490
|
} catch (e) {
|
|
238
|
-
|
|
491
|
+
// eslint-disable-next-line max-len
|
|
492
|
+
const message = `Error: Unable to convert rule to AdGuard syntax: "${rule}" due to error: ${getErrorMessage(e)}`;
|
|
239
493
|
logger.error(message);
|
|
240
494
|
excludeRule$1(rule, excluded, message);
|
|
495
|
+
|
|
496
|
+
// collect the invalid rule for the report file
|
|
497
|
+
invalidRules.push(message);
|
|
241
498
|
}
|
|
242
499
|
}
|
|
243
500
|
|
|
@@ -611,6 +868,13 @@ const validateAndFilterRules = (list, excluded, invalid = [], filterName) => {
|
|
|
611
868
|
parseAbpSpecificRules: false,
|
|
612
869
|
parseUboSpecificRules: false,
|
|
613
870
|
});
|
|
871
|
+
|
|
872
|
+
// temporary workaround for AdGuard's HTML filtering rules with pseudo-classes.
|
|
873
|
+
// TODO: remove during AG-24662 resolving
|
|
874
|
+
if (shouldKeepAdgHtmlFilteringRuleAsIs(ruleNode)) {
|
|
875
|
+
return true;
|
|
876
|
+
}
|
|
877
|
+
|
|
614
878
|
const conversionResult = RuleConverter.convertToAdg(ruleNode);
|
|
615
879
|
convertedRuleNodes = conversionResult.result;
|
|
616
880
|
// eslint-disable-next-line no-restricted-syntax
|
|
@@ -639,9 +903,10 @@ const validateAndFilterRules = (list, excluded, invalid = [], filterName) => {
|
|
|
639
903
|
}
|
|
640
904
|
}
|
|
641
905
|
} catch (e) {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
906
|
+
const message = `Error: Invalid rule in ${filterName}: "${ruleText}" due to error: ${getErrorMessage(e)}`;
|
|
907
|
+
logger.error(message);
|
|
908
|
+
excludeRule(excluded, message, ruleText);
|
|
909
|
+
invalid.push(message);
|
|
645
910
|
return false;
|
|
646
911
|
}
|
|
647
912
|
|
|
@@ -726,203 +991,6 @@ const checkAffinityDirectives = (lines) => {
|
|
|
726
991
|
return !stack.length;
|
|
727
992
|
};
|
|
728
993
|
|
|
729
|
-
// TODO: consider refactoring this file
|
|
730
|
-
// because agtree provides modern approach to solve such problems
|
|
731
|
-
|
|
732
|
-
// Based on: https://github.com/github/linguist/pull/5968/commits/f7c5c39139945576a5f9ff0b41c990e6b6019232
|
|
733
|
-
// eslint-disable-next-line max-len
|
|
734
|
-
const ADBLOCK_AGENT_PATTERN = /^(?:!|#)?\s*\[(?<AdblockInfo>\s*(?:[Aa]d[Bb]lock(?:\s+[Pp]lus)?|u[Bb]lock(?:\s+[Oo]rigin)?|[Aa]d[Gg]uard)(?:\s+(?:\d\.?)+)?\s*)(?:;\g<AdblockInfo>)*\]\s*$/;
|
|
735
|
-
|
|
736
|
-
/**
|
|
737
|
-
* CSS rules with width and height attributes break SVG rendering
|
|
738
|
-
* https://github.com/AdguardTeam/AdguardBrowserExtension/issues/683
|
|
739
|
-
*
|
|
740
|
-
* @param ruleText Rule text
|
|
741
|
-
*/
|
|
742
|
-
const fixCssRuleAttributesForEdge = function (ruleText) {
|
|
743
|
-
if (ruleText.includes(RuleMasks.MASK_CSS)
|
|
744
|
-
|| ruleText.includes(RuleMasks.MASK_CSS_EXCEPTION)
|
|
745
|
-
|| ruleText.includes(RuleMasks.MASK_ELEMENT_HIDING)
|
|
746
|
-
|| ruleText.includes(RuleMasks.MASK_ELEMENT_HIDING_EXCEPTION)) {
|
|
747
|
-
ruleText = ruleText.replace(/\[width=/gi, '[Width=');
|
|
748
|
-
ruleText = ruleText.replace('/[height=/gi', '[Height=');
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
return ruleText;
|
|
752
|
-
};
|
|
753
|
-
|
|
754
|
-
/**
|
|
755
|
-
* Updates rule text
|
|
756
|
-
*/
|
|
757
|
-
const overrideRule = function (ruleText, platform) {
|
|
758
|
-
if (platform === 'ext_edge') {
|
|
759
|
-
ruleText = fixCssRuleAttributesForEdge(ruleText);
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
return ruleText;
|
|
763
|
-
};
|
|
764
|
-
|
|
765
|
-
/**
|
|
766
|
-
* Modifies header for AdGuard Base filter
|
|
767
|
-
* https://github.com/AdguardTeam/FiltersCompiler/issues/78
|
|
768
|
-
* @param {array} header
|
|
769
|
-
* @param {boolean} optimized
|
|
770
|
-
*/
|
|
771
|
-
const modifyBaseFilterHeader = (header, optimized) => {
|
|
772
|
-
header[0] = `! Title: AdGuard Base filter + EasyList${optimized ? ' (Optimized)' : ''}`;
|
|
773
|
-
};
|
|
774
|
-
|
|
775
|
-
/**
|
|
776
|
-
* Rewrites title and description
|
|
777
|
-
* https://github.com/AdguardTeam/AdguardFilters/issues/5138#issuecomment-328847738
|
|
778
|
-
*/
|
|
779
|
-
const rewriteHeader = function (header) {
|
|
780
|
-
const result = [];
|
|
781
|
-
header.forEach((line) => {
|
|
782
|
-
if (line.startsWith('! Title: ')) {
|
|
783
|
-
line = '! Title: AdGuard Base filter';
|
|
784
|
-
} else if (line.startsWith('! Description: ')) {
|
|
785
|
-
line = '! Description: This filter is necessary for quality ad blocking.';
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
result.push(line);
|
|
789
|
-
});
|
|
790
|
-
|
|
791
|
-
return result;
|
|
792
|
-
};
|
|
793
|
-
|
|
794
|
-
/**
|
|
795
|
-
* Filters easylist block from list of rules
|
|
796
|
-
* https://github.com/AdguardTeam/AdguardFilters/issues/5138#issuecomment-328847738
|
|
797
|
-
*/
|
|
798
|
-
const rewriteRules = function (rules) {
|
|
799
|
-
const filtered = [];
|
|
800
|
-
let flag = -1;
|
|
801
|
-
for (let i = 0; i < rules.length; i += 1) {
|
|
802
|
-
const rule = rules[i];
|
|
803
|
-
|
|
804
|
-
if (flag >= 0 && rule.startsWith('!------------------')) {
|
|
805
|
-
if (flag !== i - 1) {
|
|
806
|
-
// we skip next line after block header
|
|
807
|
-
// looking for the end of easylist block
|
|
808
|
-
flag = -1;
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
continue;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
if (rule.startsWith('!------------------ EasyList rules')) {
|
|
815
|
-
flag = i;
|
|
816
|
-
continue;
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
if (flag < 0) {
|
|
820
|
-
filtered.push(rule);
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
return filtered;
|
|
825
|
-
};
|
|
826
|
-
|
|
827
|
-
/**
|
|
828
|
-
* Replaces Version: with OriginalVersion: comments in case of some client cannot afford it.
|
|
829
|
-
*
|
|
830
|
-
* @param rules
|
|
831
|
-
*/
|
|
832
|
-
const fixVersionComments = (rules) => {
|
|
833
|
-
return rules.map((x) => {
|
|
834
|
-
if (x.startsWith('! Version:')) {
|
|
835
|
-
return x.replace('! Version:', '! OriginalVersion:');
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
return x;
|
|
839
|
-
});
|
|
840
|
-
};
|
|
841
|
-
|
|
842
|
-
// TODO: use agtree to remove adblock agent strings
|
|
843
|
-
/**
|
|
844
|
-
* Removes adblock agent strings, for example:
|
|
845
|
-
* - [AdBlock],
|
|
846
|
-
* - [Adblock Plus],
|
|
847
|
-
* - [Adblock Plus 2.0],
|
|
848
|
-
* - [AdGuard],
|
|
849
|
-
* - [uBlock] / [uBlock Origin], etc.
|
|
850
|
-
*
|
|
851
|
-
* @param lines
|
|
852
|
-
*/
|
|
853
|
-
const removeAdblockVersion = (lines) => {
|
|
854
|
-
return lines.filter((line) => !line.trim().match(ADBLOCK_AGENT_PATTERN));
|
|
855
|
-
};
|
|
856
|
-
|
|
857
|
-
/**
|
|
858
|
-
* Removes scriptlet rules
|
|
859
|
-
* @param {array} rules
|
|
860
|
-
* @return {array} rules
|
|
861
|
-
*/
|
|
862
|
-
const removeScriptletRules = (rules) => rules.filter((rule) => !rule.script.startsWith(ADG_SCRIPTLET_MASK));
|
|
863
|
-
|
|
864
|
-
/**
|
|
865
|
-
* Removes `groupDescription` field from `groups`.
|
|
866
|
-
*
|
|
867
|
-
* @param rawGroups
|
|
868
|
-
* @returns Corrected groups
|
|
869
|
-
*/
|
|
870
|
-
const removeGroupDescriptions$1 = function (rawGroups) {
|
|
871
|
-
const groups = [];
|
|
872
|
-
|
|
873
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
874
|
-
for (const g of rawGroups) {
|
|
875
|
-
const copy = { ...g };
|
|
876
|
-
|
|
877
|
-
delete copy.groupDescription;
|
|
878
|
-
|
|
879
|
-
groups.push(copy);
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
return groups;
|
|
883
|
-
};
|
|
884
|
-
|
|
885
|
-
/**
|
|
886
|
-
* Corrects metadata for backward compatibility with old clients on MAC (v1) platform
|
|
887
|
-
* Hides tag fields
|
|
888
|
-
*
|
|
889
|
-
* @param metadata
|
|
890
|
-
* @returns Corrected metadata
|
|
891
|
-
*/
|
|
892
|
-
const rewriteMetadataForOldMacV1 = function (metadata) {
|
|
893
|
-
const result = {
|
|
894
|
-
groups: removeGroupDescriptions$1(metadata.groups.slice(0)),
|
|
895
|
-
filters: [],
|
|
896
|
-
};
|
|
897
|
-
|
|
898
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
899
|
-
for (const f of metadata.filters) {
|
|
900
|
-
const copy = { ...f };
|
|
901
|
-
delete copy.tags;
|
|
902
|
-
delete copy.timeAdded;
|
|
903
|
-
delete copy.trustLevel;
|
|
904
|
-
delete copy.downloadUrl;
|
|
905
|
-
delete copy.deprecated;
|
|
906
|
-
|
|
907
|
-
result.filters.push(copy);
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
return result;
|
|
911
|
-
};
|
|
912
|
-
|
|
913
|
-
/**
|
|
914
|
-
* Corrects metadata for backward compatibility with old clients on MAC_V2 platform —
|
|
915
|
-
* removed `groupDescription` field from `groups`.
|
|
916
|
-
*
|
|
917
|
-
* @param metadata
|
|
918
|
-
* @returns Corrected metadata
|
|
919
|
-
*/
|
|
920
|
-
const rewriteMetadataForOldMacV2 = function (metadata) {
|
|
921
|
-
const result = { ...metadata };
|
|
922
|
-
result.groups = removeGroupDescriptions$1(result.groups.slice(0));
|
|
923
|
-
return result;
|
|
924
|
-
};
|
|
925
|
-
|
|
926
994
|
/* eslint-disable global-require */
|
|
927
995
|
|
|
928
996
|
const require = createRequire(import.meta.url);
|
|
@@ -3152,13 +3220,20 @@ const getResolvedPreprocessorIncludes = async function (filterDir, filterName, l
|
|
|
3152
3220
|
* @param {string} filterDir Filter directory.
|
|
3153
3221
|
* @param {string} filterName Filter name.
|
|
3154
3222
|
* @param {string[]} lines Array of raw rules and possibly preprocessor `!#include` directives.
|
|
3155
|
-
* @param {string} excluded Array of rules to exclude.
|
|
3223
|
+
* @param {string[]} excluded Array of rules to exclude.
|
|
3224
|
+
* @param {string[]} invalidRules Invalid rules for the report file.
|
|
3156
3225
|
*
|
|
3157
3226
|
* @returns {Promise<string[]>} Promise which resolves to array of AdGuard syntax rules.
|
|
3158
3227
|
*/
|
|
3159
|
-
const prepareAdgRules = async function (
|
|
3228
|
+
const prepareAdgRules = async function (
|
|
3229
|
+
filterDir,
|
|
3230
|
+
filterName,
|
|
3231
|
+
lines,
|
|
3232
|
+
excluded,
|
|
3233
|
+
invalidRules,
|
|
3234
|
+
) {
|
|
3160
3235
|
const resolvedIncludes = await getResolvedPreprocessorIncludes(filterDir, filterName, lines);
|
|
3161
|
-
return convertRulesToAdgSyntax(resolvedIncludes, excluded);
|
|
3236
|
+
return convertRulesToAdgSyntax(resolvedIncludes, excluded, invalidRules);
|
|
3162
3237
|
};
|
|
3163
3238
|
|
|
3164
3239
|
/**
|
|
@@ -3224,7 +3299,13 @@ const compile$1 = async function (filterDir, filterName, templateContent, trustL
|
|
|
3224
3299
|
includedLines.forEach((line) => includedRules.push(line.trim()));
|
|
3225
3300
|
|
|
3226
3301
|
// eslint-disable-next-line no-await-in-loop
|
|
3227
|
-
includedRules = await prepareAdgRules(
|
|
3302
|
+
includedRules = await prepareAdgRules(
|
|
3303
|
+
filterDir,
|
|
3304
|
+
filterName,
|
|
3305
|
+
includedRules,
|
|
3306
|
+
excluded,
|
|
3307
|
+
invalid,
|
|
3308
|
+
);
|
|
3228
3309
|
|
|
3229
3310
|
if (shouldIgnoreTrustLevel) {
|
|
3230
3311
|
logger.info(`Ignoring trust level for ${filterName} due to @include directive: ${line}`);
|
|
@@ -3242,7 +3323,13 @@ const compile$1 = async function (filterDir, filterName, templateContent, trustL
|
|
|
3242
3323
|
let inlineRules = [line.trim()];
|
|
3243
3324
|
|
|
3244
3325
|
// eslint-disable-next-line no-await-in-loop
|
|
3245
|
-
inlineRules = await prepareAdgRules(
|
|
3326
|
+
inlineRules = await prepareAdgRules(
|
|
3327
|
+
filterDir,
|
|
3328
|
+
filterName,
|
|
3329
|
+
inlineRules,
|
|
3330
|
+
excluded,
|
|
3331
|
+
invalid,
|
|
3332
|
+
);
|
|
3246
3333
|
|
|
3247
3334
|
logger.info('Applying trust-level exclusions to inline template.txt rules...');
|
|
3248
3335
|
inlineRules = exclude(inlineRules, trustLevelSettings, excluded);
|