@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 CHANGED
@@ -1 +1 @@
1
- version=3.1.2
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
- * @param {array} rulesList
222
- * @param {array} [excluded]
223
- * @return {array} result
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 message = `Rule "${rule}" converted to: "${[...convertedRules]}"`;
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
- const message = `Unable to convert rule to AdGuard syntax: "${rule}" due to error: ${e.message}`;
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
- logger.error(`Invalid rule in ${filterName}: ${ruleText}`);
646
- excludeRule(excluded, e.message, ruleText);
647
- invalid.push(e.message);
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 (filterDir, filterName, lines, excluded) {
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(filterDir, filterName, includedRules, excluded);
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(filterDir, filterName, inlineRules, excluded);
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, CosmeticRuleType, CommentParser, RuleCategory, RegExpUtils, ADG_SCRIPTLET_MASK } from '@adguard/agtree';
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
- * @param {array} rulesList
219
- * @param {array} [excluded]
220
- * @return {array} result
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 message = `Rule "${rule}" converted to: "${[...convertedRules]}"`;
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
- const message = `Unable to convert rule to AdGuard syntax: "${rule}" due to error: ${e.message}`;
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
- logger.error(`Invalid rule in ${filterName}: ${ruleText}`);
643
- excludeRule(excluded, e.message, ruleText);
644
- invalid.push(e.message);
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 (filterDir, filterName, lines, excluded) {
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(filterDir, filterName, includedRules, excluded);
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(filterDir, filterName, inlineRules, excluded);
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.2",
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.4",
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.11",
27
- "@adguard/tsurlfilter": "^3.2.7",
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",