@adguard/filters-compiler 3.1.2 → 3.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build.txt +1 -1
- package/dist/index.cjs +302 -212
- package/dist/index.js +303 -213
- package/package.json +4 -4
package/dist/build.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
version=3.1.
|
|
1
|
+
version=3.1.4
|
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,245 @@ 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
|
+
// Explicitly keep only the required fields for backward compatibility
|
|
353
|
+
const copy = {
|
|
354
|
+
groupId: g.groupId,
|
|
355
|
+
groupName: g.groupName,
|
|
356
|
+
displayNumber: g.displayNumber,
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
groups.push(copy);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return groups;
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Corrects metadata for backward compatibility with old clients on MAC (v1) platform
|
|
367
|
+
* Hides tag fields
|
|
368
|
+
*
|
|
369
|
+
* @param metadata
|
|
370
|
+
* @returns Corrected metadata
|
|
371
|
+
*/
|
|
372
|
+
const rewriteMetadataForOldMacV1 = function (metadata) {
|
|
373
|
+
const result = {
|
|
374
|
+
groups: removeGroupDescriptions$1(metadata.groups.slice(0)),
|
|
375
|
+
filters: [],
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
379
|
+
for (const f of metadata.filters) {
|
|
380
|
+
const copy = { ...f };
|
|
381
|
+
delete copy.tags;
|
|
382
|
+
delete copy.timeAdded;
|
|
383
|
+
delete copy.trustLevel;
|
|
384
|
+
delete copy.downloadUrl;
|
|
385
|
+
delete copy.deprecated;
|
|
386
|
+
|
|
387
|
+
result.filters.push(copy);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return result;
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Corrects metadata for backward compatibility with old clients on MAC_V2 platform —
|
|
395
|
+
* removed `groupDescription` field from `groups`.
|
|
396
|
+
*
|
|
397
|
+
* @param metadata
|
|
398
|
+
* @returns Corrected metadata
|
|
399
|
+
*/
|
|
400
|
+
const rewriteMetadataForOldMacV2 = function (metadata) {
|
|
401
|
+
const result = { ...metadata };
|
|
402
|
+
result.groups = removeGroupDescriptions$1(result.groups.slice(0));
|
|
403
|
+
return result;
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Markers of pseudo-classes for HTML filtering rules.
|
|
408
|
+
*
|
|
409
|
+
* Already supported by the AdGuard apps, but not implemented for the extension yet.
|
|
410
|
+
* TODO: AG-24662.
|
|
411
|
+
*
|
|
412
|
+
* @see {@link https://adgkb.service.agrd.dev/kb/general/ad-filtering/create-own-filters/#html-filtering-rules--pseudo-classes}
|
|
413
|
+
*/
|
|
414
|
+
const HTML_RULES_PSEUDO_CLASS_MARKERS = [
|
|
415
|
+
// AdGuard-specific pseudo-classes
|
|
416
|
+
':contains(',
|
|
417
|
+
// aliases
|
|
418
|
+
':-abp-contains(',
|
|
419
|
+
':has-text(',
|
|
420
|
+
];
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Checks if the rule should be kept as is,
|
|
424
|
+
* i.e. no conversion and no validation,
|
|
425
|
+
* since it is not supported by the extension yet.
|
|
426
|
+
*
|
|
427
|
+
* Conditions for keeping the rule as is:
|
|
428
|
+
* - rule is HTML filtering rule
|
|
429
|
+
* - rule is AdGuard syntax
|
|
430
|
+
* - rule contains pseudo-class marker.
|
|
431
|
+
*
|
|
432
|
+
* Planned to be fixed:
|
|
433
|
+
* https://github.com/AdguardTeam/tsurlfilter/issues/96.
|
|
434
|
+
*
|
|
435
|
+
* @param {import('@adguard/agtree').AnyRule} ruleNode Rule node to check.
|
|
436
|
+
*
|
|
437
|
+
* @returns {boolean} True if the rule should be kept as is, otherwise false.
|
|
438
|
+
*/
|
|
439
|
+
const shouldKeepAdgHtmlFilteringRuleAsIs = (ruleNode) => {
|
|
440
|
+
return ruleNode.type === agtree.CosmeticRuleType.HtmlFilteringRule
|
|
441
|
+
&& ruleNode.syntax === agtree.AdblockSyntax.Adg
|
|
442
|
+
&& HTML_RULES_PSEUDO_CLASS_MARKERS.some((marker) => ruleNode.body.value.includes(marker));
|
|
443
|
+
};
|
|
444
|
+
|
|
206
445
|
/**
|
|
207
446
|
* Excludes rule
|
|
208
447
|
* @param {string} rule
|
|
@@ -218,29 +457,50 @@ const excludeRule$1 = (rule, excluded, message) => {
|
|
|
218
457
|
|
|
219
458
|
/**
|
|
220
459
|
* Converts rules to AdGuard syntax
|
|
221
|
-
*
|
|
222
|
-
* @param {
|
|
223
|
-
* @
|
|
460
|
+
*
|
|
461
|
+
* @param {string[]} rulesList Input rules.
|
|
462
|
+
* @param {string[]} [excluded=[]] Rules to exclude.
|
|
463
|
+
* @param {string[]} [invalidRules=[]] Collected invalid rules for the report file.
|
|
464
|
+
*
|
|
465
|
+
* @return {string[]} Rules converted to AdGuard syntax.
|
|
224
466
|
*/
|
|
225
|
-
const convertRulesToAdgSyntax = (rulesList, excluded) => {
|
|
467
|
+
const convertRulesToAdgSyntax = (rulesList, excluded = [], invalidRules = []) => {
|
|
226
468
|
const result = [];
|
|
227
469
|
|
|
228
470
|
for (let i = 0; i < rulesList.length; i += 1) {
|
|
229
471
|
const rule = rulesList[i];
|
|
230
472
|
try {
|
|
231
473
|
const ruleNode = agtree.RuleParser.parse(rule);
|
|
474
|
+
|
|
475
|
+
// temporary workaround for AdGuard's HTML filtering rules with pseudo-classes.
|
|
476
|
+
// TODO: remove during AG-24662 resolving
|
|
477
|
+
if (shouldKeepAdgHtmlFilteringRuleAsIs(ruleNode)) {
|
|
478
|
+
const message = `Keeping HTML filtering rule with pseudo-classes as is: "${rule}"`;
|
|
479
|
+
logger.warn(message);
|
|
480
|
+
result.push(rule);
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
|
|
232
484
|
const conversionResult = agtree.RuleConverter.convertToAdg(ruleNode);
|
|
233
485
|
const convertedRules = conversionResult.result.map((r) => agtree.RuleGenerator.generate(r));
|
|
234
486
|
result.push(...convertedRules);
|
|
235
487
|
|
|
236
488
|
if (conversionResult.isConverted) {
|
|
237
|
-
const
|
|
489
|
+
const messagePostfix = convertedRules.length > 1
|
|
490
|
+
// eslint-disable-next-line quotes
|
|
491
|
+
? `to multiple rules: "${convertedRules.join(", ")}"`
|
|
492
|
+
: `to: "${convertedRules[0]}"`;
|
|
493
|
+
const message = `Rule "${rule}" converted ${messagePostfix}`;
|
|
238
494
|
excludeRule$1(rule, excluded, message);
|
|
239
495
|
}
|
|
240
496
|
} catch (e) {
|
|
241
|
-
|
|
497
|
+
// eslint-disable-next-line max-len
|
|
498
|
+
const message = `Error: Unable to convert rule to AdGuard syntax: "${rule}" due to error: ${logger$1.getErrorMessage(e)}`;
|
|
242
499
|
logger.error(message);
|
|
243
500
|
excludeRule$1(rule, excluded, message);
|
|
501
|
+
|
|
502
|
+
// collect the invalid rule for the report file
|
|
503
|
+
invalidRules.push(message);
|
|
244
504
|
}
|
|
245
505
|
}
|
|
246
506
|
|
|
@@ -614,6 +874,13 @@ const validateAndFilterRules = (list, excluded, invalid = [], filterName) => {
|
|
|
614
874
|
parseAbpSpecificRules: false,
|
|
615
875
|
parseUboSpecificRules: false,
|
|
616
876
|
});
|
|
877
|
+
|
|
878
|
+
// temporary workaround for AdGuard's HTML filtering rules with pseudo-classes.
|
|
879
|
+
// TODO: remove during AG-24662 resolving
|
|
880
|
+
if (shouldKeepAdgHtmlFilteringRuleAsIs(ruleNode)) {
|
|
881
|
+
return true;
|
|
882
|
+
}
|
|
883
|
+
|
|
617
884
|
const conversionResult = agtree.RuleConverter.convertToAdg(ruleNode);
|
|
618
885
|
convertedRuleNodes = conversionResult.result;
|
|
619
886
|
// eslint-disable-next-line no-restricted-syntax
|
|
@@ -642,9 +909,10 @@ const validateAndFilterRules = (list, excluded, invalid = [], filterName) => {
|
|
|
642
909
|
}
|
|
643
910
|
}
|
|
644
911
|
} catch (e) {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
912
|
+
const message = `Error: Invalid rule in ${filterName}: "${ruleText}" due to error: ${logger$1.getErrorMessage(e)}`;
|
|
913
|
+
logger.error(message);
|
|
914
|
+
excludeRule(excluded, message, ruleText);
|
|
915
|
+
invalid.push(message);
|
|
648
916
|
return false;
|
|
649
917
|
}
|
|
650
918
|
|
|
@@ -729,203 +997,6 @@ const checkAffinityDirectives = (lines) => {
|
|
|
729
997
|
return !stack.length;
|
|
730
998
|
};
|
|
731
999
|
|
|
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
1000
|
/* eslint-disable global-require */
|
|
930
1001
|
|
|
931
1002
|
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 +3226,20 @@ const getResolvedPreprocessorIncludes = async function (filterDir, filterName, l
|
|
|
3155
3226
|
* @param {string} filterDir Filter directory.
|
|
3156
3227
|
* @param {string} filterName Filter name.
|
|
3157
3228
|
* @param {string[]} lines Array of raw rules and possibly preprocessor `!#include` directives.
|
|
3158
|
-
* @param {string} excluded Array of rules to exclude.
|
|
3229
|
+
* @param {string[]} excluded Array of rules to exclude.
|
|
3230
|
+
* @param {string[]} invalidRules Invalid rules for the report file.
|
|
3159
3231
|
*
|
|
3160
3232
|
* @returns {Promise<string[]>} Promise which resolves to array of AdGuard syntax rules.
|
|
3161
3233
|
*/
|
|
3162
|
-
const prepareAdgRules = async function (
|
|
3234
|
+
const prepareAdgRules = async function (
|
|
3235
|
+
filterDir,
|
|
3236
|
+
filterName,
|
|
3237
|
+
lines,
|
|
3238
|
+
excluded,
|
|
3239
|
+
invalidRules,
|
|
3240
|
+
) {
|
|
3163
3241
|
const resolvedIncludes = await getResolvedPreprocessorIncludes(filterDir, filterName, lines);
|
|
3164
|
-
return convertRulesToAdgSyntax(resolvedIncludes, excluded);
|
|
3242
|
+
return convertRulesToAdgSyntax(resolvedIncludes, excluded, invalidRules);
|
|
3165
3243
|
};
|
|
3166
3244
|
|
|
3167
3245
|
/**
|
|
@@ -3227,7 +3305,13 @@ const compile$1 = async function (filterDir, filterName, templateContent, trustL
|
|
|
3227
3305
|
includedLines.forEach((line) => includedRules.push(line.trim()));
|
|
3228
3306
|
|
|
3229
3307
|
// eslint-disable-next-line no-await-in-loop
|
|
3230
|
-
includedRules = await prepareAdgRules(
|
|
3308
|
+
includedRules = await prepareAdgRules(
|
|
3309
|
+
filterDir,
|
|
3310
|
+
filterName,
|
|
3311
|
+
includedRules,
|
|
3312
|
+
excluded,
|
|
3313
|
+
invalid,
|
|
3314
|
+
);
|
|
3231
3315
|
|
|
3232
3316
|
if (shouldIgnoreTrustLevel) {
|
|
3233
3317
|
logger.info(`Ignoring trust level for ${filterName} due to @include directive: ${line}`);
|
|
@@ -3245,7 +3329,13 @@ const compile$1 = async function (filterDir, filterName, templateContent, trustL
|
|
|
3245
3329
|
let inlineRules = [line.trim()];
|
|
3246
3330
|
|
|
3247
3331
|
// eslint-disable-next-line no-await-in-loop
|
|
3248
|
-
inlineRules = await prepareAdgRules(
|
|
3332
|
+
inlineRules = await prepareAdgRules(
|
|
3333
|
+
filterDir,
|
|
3334
|
+
filterName,
|
|
3335
|
+
inlineRules,
|
|
3336
|
+
excluded,
|
|
3337
|
+
invalid,
|
|
3338
|
+
);
|
|
3249
3339
|
|
|
3250
3340
|
logger.info('Applying trust-level exclusions to inline template.txt rules...');
|
|
3251
3341
|
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,245 @@ 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
|
+
// Explicitly keep only the required fields for backward compatibility
|
|
350
|
+
const copy = {
|
|
351
|
+
groupId: g.groupId,
|
|
352
|
+
groupName: g.groupName,
|
|
353
|
+
displayNumber: g.displayNumber,
|
|
354
|
+
};
|
|
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 === CosmeticRuleType.HtmlFilteringRule
|
|
438
|
+
&& ruleNode.syntax === AdblockSyntax.Adg
|
|
439
|
+
&& HTML_RULES_PSEUDO_CLASS_MARKERS.some((marker) => ruleNode.body.value.includes(marker));
|
|
440
|
+
};
|
|
441
|
+
|
|
203
442
|
/**
|
|
204
443
|
* Excludes rule
|
|
205
444
|
* @param {string} rule
|
|
@@ -215,29 +454,50 @@ const excludeRule$1 = (rule, excluded, message) => {
|
|
|
215
454
|
|
|
216
455
|
/**
|
|
217
456
|
* Converts rules to AdGuard syntax
|
|
218
|
-
*
|
|
219
|
-
* @param {
|
|
220
|
-
* @
|
|
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.
|
|
221
463
|
*/
|
|
222
|
-
const convertRulesToAdgSyntax = (rulesList, excluded) => {
|
|
464
|
+
const convertRulesToAdgSyntax = (rulesList, excluded = [], invalidRules = []) => {
|
|
223
465
|
const result = [];
|
|
224
466
|
|
|
225
467
|
for (let i = 0; i < rulesList.length; i += 1) {
|
|
226
468
|
const rule = rulesList[i];
|
|
227
469
|
try {
|
|
228
470
|
const ruleNode = 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
|
+
|
|
229
481
|
const conversionResult = RuleConverter.convertToAdg(ruleNode);
|
|
230
482
|
const convertedRules = conversionResult.result.map((r) => RuleGenerator.generate(r));
|
|
231
483
|
result.push(...convertedRules);
|
|
232
484
|
|
|
233
485
|
if (conversionResult.isConverted) {
|
|
234
|
-
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}`;
|
|
235
491
|
excludeRule$1(rule, excluded, message);
|
|
236
492
|
}
|
|
237
493
|
} catch (e) {
|
|
238
|
-
|
|
494
|
+
// eslint-disable-next-line max-len
|
|
495
|
+
const message = `Error: Unable to convert rule to AdGuard syntax: "${rule}" due to error: ${getErrorMessage(e)}`;
|
|
239
496
|
logger.error(message);
|
|
240
497
|
excludeRule$1(rule, excluded, message);
|
|
498
|
+
|
|
499
|
+
// collect the invalid rule for the report file
|
|
500
|
+
invalidRules.push(message);
|
|
241
501
|
}
|
|
242
502
|
}
|
|
243
503
|
|
|
@@ -611,6 +871,13 @@ const validateAndFilterRules = (list, excluded, invalid = [], filterName) => {
|
|
|
611
871
|
parseAbpSpecificRules: false,
|
|
612
872
|
parseUboSpecificRules: false,
|
|
613
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
|
+
|
|
614
881
|
const conversionResult = RuleConverter.convertToAdg(ruleNode);
|
|
615
882
|
convertedRuleNodes = conversionResult.result;
|
|
616
883
|
// eslint-disable-next-line no-restricted-syntax
|
|
@@ -639,9 +906,10 @@ const validateAndFilterRules = (list, excluded, invalid = [], filterName) => {
|
|
|
639
906
|
}
|
|
640
907
|
}
|
|
641
908
|
} catch (e) {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
909
|
+
const message = `Error: Invalid rule in ${filterName}: "${ruleText}" due to error: ${getErrorMessage(e)}`;
|
|
910
|
+
logger.error(message);
|
|
911
|
+
excludeRule(excluded, message, ruleText);
|
|
912
|
+
invalid.push(message);
|
|
645
913
|
return false;
|
|
646
914
|
}
|
|
647
915
|
|
|
@@ -726,203 +994,6 @@ const checkAffinityDirectives = (lines) => {
|
|
|
726
994
|
return !stack.length;
|
|
727
995
|
};
|
|
728
996
|
|
|
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
997
|
/* eslint-disable global-require */
|
|
927
998
|
|
|
928
999
|
const require = createRequire(import.meta.url);
|
|
@@ -3152,13 +3223,20 @@ const getResolvedPreprocessorIncludes = async function (filterDir, filterName, l
|
|
|
3152
3223
|
* @param {string} filterDir Filter directory.
|
|
3153
3224
|
* @param {string} filterName Filter name.
|
|
3154
3225
|
* @param {string[]} lines Array of raw rules and possibly preprocessor `!#include` directives.
|
|
3155
|
-
* @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.
|
|
3156
3228
|
*
|
|
3157
3229
|
* @returns {Promise<string[]>} Promise which resolves to array of AdGuard syntax rules.
|
|
3158
3230
|
*/
|
|
3159
|
-
const prepareAdgRules = async function (
|
|
3231
|
+
const prepareAdgRules = async function (
|
|
3232
|
+
filterDir,
|
|
3233
|
+
filterName,
|
|
3234
|
+
lines,
|
|
3235
|
+
excluded,
|
|
3236
|
+
invalidRules,
|
|
3237
|
+
) {
|
|
3160
3238
|
const resolvedIncludes = await getResolvedPreprocessorIncludes(filterDir, filterName, lines);
|
|
3161
|
-
return convertRulesToAdgSyntax(resolvedIncludes, excluded);
|
|
3239
|
+
return convertRulesToAdgSyntax(resolvedIncludes, excluded, invalidRules);
|
|
3162
3240
|
};
|
|
3163
3241
|
|
|
3164
3242
|
/**
|
|
@@ -3224,7 +3302,13 @@ const compile$1 = async function (filterDir, filterName, templateContent, trustL
|
|
|
3224
3302
|
includedLines.forEach((line) => includedRules.push(line.trim()));
|
|
3225
3303
|
|
|
3226
3304
|
// eslint-disable-next-line no-await-in-loop
|
|
3227
|
-
includedRules = await prepareAdgRules(
|
|
3305
|
+
includedRules = await prepareAdgRules(
|
|
3306
|
+
filterDir,
|
|
3307
|
+
filterName,
|
|
3308
|
+
includedRules,
|
|
3309
|
+
excluded,
|
|
3310
|
+
invalid,
|
|
3311
|
+
);
|
|
3228
3312
|
|
|
3229
3313
|
if (shouldIgnoreTrustLevel) {
|
|
3230
3314
|
logger.info(`Ignoring trust level for ${filterName} due to @include directive: ${line}`);
|
|
@@ -3242,7 +3326,13 @@ const compile$1 = async function (filterDir, filterName, templateContent, trustL
|
|
|
3242
3326
|
let inlineRules = [line.trim()];
|
|
3243
3327
|
|
|
3244
3328
|
// eslint-disable-next-line no-await-in-loop
|
|
3245
|
-
inlineRules = await prepareAdgRules(
|
|
3329
|
+
inlineRules = await prepareAdgRules(
|
|
3330
|
+
filterDir,
|
|
3331
|
+
filterName,
|
|
3332
|
+
inlineRules,
|
|
3333
|
+
excluded,
|
|
3334
|
+
invalid,
|
|
3335
|
+
);
|
|
3246
3336
|
|
|
3247
3337
|
logger.info('Applying trust-level exclusions to inline template.txt rules...');
|
|
3248
3338
|
inlineRules = exclude(inlineRules, trustLevelSettings, excluded);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adguard/filters-compiler",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.4",
|
|
4
4
|
"description": "AdGuard filters compiler",
|
|
5
5
|
"homepage": "http://adguard.com",
|
|
6
6
|
"type": "module",
|
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
"node": ">=22"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@adguard/agtree": "^3.2.
|
|
21
|
+
"@adguard/agtree": "^3.2.5",
|
|
22
22
|
"@adguard/ecss-tree": "^1.1.0",
|
|
23
23
|
"@adguard/extended-css": "^2.1.1",
|
|
24
24
|
"@adguard/filters-downloader": "^2.4.0",
|
|
25
25
|
"@adguard/logger": "^2.0.0",
|
|
26
|
-
"@adguard/scriptlets": "^2.2.
|
|
27
|
-
"@adguard/tsurlfilter": "^3.2.
|
|
26
|
+
"@adguard/scriptlets": "^2.2.12",
|
|
27
|
+
"@adguard/tsurlfilter": "^3.2.8",
|
|
28
28
|
"ajv": "^8.17.1",
|
|
29
29
|
"child_process": ">=1.0.2",
|
|
30
30
|
"jsdom": "^21.1.1",
|