@duckduckgo/autoconsent 16.30.0 → 16.32.0

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.
Files changed (40) hide show
  1. package/.github/workflows/asana-sync.yml +1 -1
  2. package/CHANGELOG.md +39 -0
  3. package/dist/addon-firefox/compact-rules.json +1 -1
  4. package/dist/addon-firefox/content.bundle.js +47 -10
  5. package/dist/addon-firefox/manifest.json +1 -1
  6. package/dist/addon-firefox/rule-index.json +1 -1
  7. package/dist/addon-firefox/rules.json +1 -1
  8. package/dist/addon-mv3/compact-rules.json +1 -1
  9. package/dist/addon-mv3/content.bundle.js +47 -10
  10. package/dist/addon-mv3/manifest.json +1 -1
  11. package/dist/addon-mv3/rule-index.json +1 -1
  12. package/dist/addon-mv3/rules.json +1 -1
  13. package/dist/autoconsent.cjs.js +47 -10
  14. package/dist/autoconsent.esm.js +47 -10
  15. package/dist/autoconsent.playwright.js +47 -10
  16. package/dist/autoconsent.standalone.js +48 -11
  17. package/lib/cmps/cookiebot.ts +4 -0
  18. package/lib/heuristic-patterns.ts +45 -8
  19. package/lib/heuristics.ts +2 -1
  20. package/package.json +1 -1
  21. package/rules/autoconsent/doordash-storefront.json +34 -0
  22. package/rules/autoconsent/walmart-ca.json +44 -0
  23. package/rules/autoconsent/xnxx-com.json +1 -1
  24. package/rules/autoconsent/xvideos.json +1 -1
  25. package/rules/compact-rules.json +1 -1
  26. package/rules/rule-index.json +1 -1
  27. package/rules/rules.json +1 -1
  28. package/tests/cookiebot.spec.ts +4 -0
  29. package/tests/doordash-storefront.spec.ts +6 -0
  30. package/tests/walmart-ca.spec.ts +3 -0
  31. package/tests/xnxx.spec.ts +8 -2
  32. package/tests/xvideos.spec.ts +3 -3
  33. package/tests-wtr/heuristics/get-actionable-popups.html +15 -0
  34. package/tests-wtr/heuristics/get-actionable-popups.ts +20 -0
  35. package/tests-wtr/heuristics/heuristic-cmp.html +17 -0
  36. package/tests-wtr/heuristics/heuristic-cmp.ts +34 -0
  37. package/tests-wtr/heuristics/heuristics-utils.test.ts +135 -0
  38. package/tests-wtr/rules/cmp-test-urls.json +0 -1
  39. package/rules/generated/auto_CA_algonquincollege.com_o8v.json +0 -40
  40. package/tests/generated/auto_CA_algonquincollege.com_o8v.spec.ts +0 -6
@@ -34,6 +34,10 @@ export default class Cookiebot extends AutoConsentCMPBase {
34
34
  let res = await this.mainWorldEval('EVAL_COOKIEBOT_3'); // withdraw
35
35
  await this.wait(1000); // prevent race conditions
36
36
  res = res && (await this.mainWorldEval('EVAL_COOKIEBOT_4')); // hide
37
+ // Custom dialog templates reuse Cookiebot button ids but aren't closed by Cookiebot.hide().
38
+ if (this.elementVisible('#CybotCookiebotDialogBodyButtonDecline')) {
39
+ return await this.click('#CybotCookiebotDialogBodyButtonDecline');
40
+ }
37
41
  return res;
38
42
  }
39
43
 
@@ -10,6 +10,7 @@ export const DETECT_PATTERNS = [
10
10
  /we (?:use|serve)(?: optional)? cookies/gi,
11
11
  /we are using cookies/gi,
12
12
  /use of cookies/gi,
13
+ /website uses cookies to enhance your browsing experience/gi,
13
14
  /(?:this|our) (?:web)?site.{0,100}cookies/gi,
14
15
  /cookies (?:and|or) .{0,100} technologies/gi,
15
16
  /such as cookies/gi,
@@ -151,7 +152,16 @@ export const DETECT_PATTERNS = [
151
152
  /este portal emprega cookies propias ou de terceiros con fins analíticos/gi,
152
153
 
153
154
  // Russian (RU)
154
- /мы используем файлы cookie и аналогичные технологии/gi,
155
+ // e.g. "мы используем файлы cookie", "сайт использует куки", "используются технологии cookie"
156
+ // note: \w does not match Cyrillic, so these use explicit character ranges
157
+ /использу[а-яё]*.{0,40}(?:файл[а-яё]*[\s-]+)?(?:cookie|куки)/gi,
158
+ /(?:cookie|куки).{0,40}использу[а-яё]*/gi,
159
+ /(?:использовани[а-яё]*|обработк[а-яё]*|хранени[а-яё]*).{0,20}(?:файл(?:ов|ы)[\s-]+)?(?:cookie|куки)/gi,
160
+ /технологи[а-яё]*\s+(?:cookie|куки)/gi,
161
+ // e.g. "сайт собирает cookie", "мы применяем файлы cookie", "сохраняем куки на вашем устройстве"
162
+ /(?:собира[а-яё]*|сбор|применя[а-яё]*|сохраня[а-яё]*|размеща[а-яё]*|устанавлива[а-яё]*).{0,30}(?:файл[а-яё]*[\s-]+)?(?:cookie|куки)/gi,
163
+ // "файлы cookie" / "cookie-файлы" / "куки-файлы" in any phrasing
164
+ /(?:файл[а-яё]*[\s-]+(?:cookie|куки)|(?:cookie|куки)[\s-]+файл[а-яё]*)/gi,
155
165
 
156
166
  // Italian (IT)
157
167
  /usiamo.{0,20}cookie/gi,
@@ -171,8 +181,9 @@ const REJECT_PATTERNS_ENGLISH = [
171
181
 
172
182
  // essential/necessary/functional-only, e.g. "essential cookies only", "accept only essential cookies",
173
183
  // "allow necessary cookies continue", "use essential cookies only", "functional only", "i confirm necessary"
174
- // note that a necessary/essential/functional word is required
175
- /^\s*(i\s+)?(want\s+to\s+)?(only\s+)?(use|accept|allow|keep|enable|choose|continue\s+with|i\s+confirm)?\s*(only\s+)?(strictly\s+)?(necessary|essential|essentials|functional|required|minimal)\s*(only\s+)?(cookies)?\s*(continue|only)?\s*$/is,
184
+ /^\s*(i\s+)?(want\s+to\s+)?(only\s+)?(use|accept|allow|keep|enable|choose|continue\s+with|i\s+confirm)\s*(only\s+)?(strictly\s+)?(necessary|essential|essentials|functional|required|minimal)\s*(cookies)?\s*(continue|only)?\s*$/is,
185
+ /^\s*(i\s+)?(want\s+to\s+)?only\s+(strictly\s+)?(necessary|essential|essentials|functional|required|minimal)\s*(cookies)?\s*(continue|only)?\s*$/is,
186
+ /^\s*(strictly\s+)?(necessary|essential|essentials|functional|required|minimal)\s*(cookies)?\s+only\s*$/is,
176
187
 
177
188
  // e.g. "do not sell or share my personal information", "opt out of sale ..." (CCPA)
178
189
  /do\s+not\s+sell|opt\s+out\s+of\s+sale/is,
@@ -364,7 +375,16 @@ const REJECT_PATTERNS_POLISH = [
364
375
  /^funkcjonalne pliki cookie \(wymagane\)$/,
365
376
  ];
366
377
 
367
- const REJECT_PATTERNS_RUSSIAN = ['принимать только необходимые файлы cookie'];
378
+ const REJECT_PATTERNS_RUSSIAN = [
379
+ // отклонить / отказаться / запретить (reject verbs); \w does not match Cyrillic
380
+ /(^|\s)(отклон[а-яё]*|отказ[а-яё]*|откаж[а-яё]*|запрет[а-яё]*|запрещ[а-яё]*)/is,
381
+
382
+ // "только необходимые (файлы cookie)" / "принять только необходимые куки"
383
+ /^\s*(принять\s+|принима[а-яё]+\s+|разрешить\s+|использовать\s+|оставить\s+)?(только|лишь)\s+(строго\s+)?(необходим[а-яё]+|нужн[а-яё]+|обязательн[а-яё]+|техническ[а-яё]+|функциональн[а-яё]+)(\s+файл[а-яё]*)?([\s-]+(cookie|куки)([\s-]+файл[а-яё]*)?)?\s*$/is,
384
+
385
+ // refusals: "не принимаю", "не согласен", "не соглашаюсь", "нет, спасибо"
386
+ /^\s*(не\s+(принима[а-яё]+|соглас[а-яё]+|разреша[а-яё]+|хочу)|нет(,?\s+спасибо)?)\s*$/is,
387
+ ];
368
388
 
369
389
  const REJECT_PATTERNS_TURKISH = ['reddet', 'çerezleri reddet'];
370
390
 
@@ -415,6 +435,9 @@ export const BUTTON_NEVER_MATCH_PATTERNS = [
415
435
 
416
436
  // Polish (PL)
417
437
  /subskrybuj/,
438
+
439
+ // Russian (RU): paywall/subscription wording, e.g. "отказаться от подписки", "оплатить"
440
+ /подписатьс|подписк|оплатит|оплачива/is,
418
441
  ];
419
442
 
420
443
  // Popup body-text patterns that suppress heuristic detection. Currently targets age gates and adult-content disclaimers whose "reject" button leads to a dead end.
@@ -595,8 +618,12 @@ export const SETTINGS_PATTERNS = [
595
618
  'zobacz preferencje',
596
619
 
597
620
  // Russian (RU)
598
- 'настроить файлы cookie',
599
- 'настройки',
621
+ // e.g. "настроить", "настройки куки", "параметры конфиденциальности"; \w does not match Cyrillic
622
+ /^\s*((мои|моими|свои|своими)\s+)?(настро[а-яё]+|парамет[а-яё]+|предпочтени[а-яё]+)([\s-]+(мои|моими|свои|своими|файл[а-яё]*|cookie|куки[а-яё]*|конфиденциальност[а-яё]*|согласи[а-яё]*|приватност[а-яё]*))*\s*$/is,
623
+ // e.g. "управление файлами cookie", "изменить настройки", "выбрать категории"
624
+ // "подробн" is deliberately excluded: it would also match the "подробнее о cookie" policy link
625
+ /^\s*(управл[а-яё]+|измен[а-яё]+|выбрать|персонализ[а-яё]+|расширенн[а-яё]+|индивидуальн[а-яё]+)[\s-]+.{0,20}(настройк[а-яё]*|парамет[а-яё]*|предпочтени[а-яё]*|категори[а-яё]*|файл[а-яё]*|cookie|куки[а-яё]*|согласи[а-яё]*|конфиденциальност[а-яё]*)\s*$/is,
626
+ 'подробные настройки',
600
627
 
601
628
  // Italian (IT)
602
629
  'personalizza cookie',
@@ -748,7 +775,9 @@ export const ACCEPT_PATTERNS = [
748
775
  'zgoda na wybrane',
749
776
 
750
777
  // Russian (RU)
751
- 'принять все файлы cookie',
778
+ // e.g. "принять всё", "принять все файлы cookie", "разрешить куки", "я согласен"
779
+ // confirm/save wording (подтвердить, сохранить) is acknowledge, not accept
780
+ /^\s*(да,?\s+)?(я\s+)?(принять|принима[а-яё]+|соглас[а-яё]+|разрешить|разреша[а-яё]+)(\s+(вс[её]|(все\s+)?(файлы[\s-]+)?(cookie|куки)([\s-]+файл[а-яё]*)?|выбранные|выбор))?\s*$/is,
752
781
  'принять',
753
782
 
754
783
  // Turkish (TR)
@@ -820,5 +849,13 @@ export const ACKNOWLEDGE_PATTERNS = [
820
849
  'zapisz i zamknij',
821
850
 
822
851
  // Russian (RU)
823
- 'понятно',
852
+ // e.g. "понятно", "всё понятно", "хорошо", "ясно"; \w does not match Cyrillic
853
+ /^\s*(хорошо|ясно|(вс[её]\s+)?(понятно|понял[аи]?))[!.]*\s*$/is,
854
+ // e.g. "закрыть", "закрыть уведомление о cookie"
855
+ /^\s*закрыть([\s-]+(это|эту|баннер|уведомлени[а-яё]*|окно|сообщени[а-яё]*|плашк[а-яё]*|информаци[а-яё]*)){0,2}([\s-]+(о|об)[\s-]+(cookie|куки[а-яё]*|файл[а-яё]*[\s-]+cookie))?\s*$/is,
856
+ // e.g. "больше не показывать", "не показывать снова"
857
+ /^\s*(больше\s+не\s+показывать|не\s+показывать(\s+(снова|больше|это\s+сообщение))?)\s*$/is,
858
+ // e.g. "подтвердить", "подтверждаю выбор", "сохранить настройки", "сохранить и закрыть"
859
+ /^\s*(подтвер(дить|ждаю|ждени[а-яё]*)|сохран(ить|яю|ение))([\s-]+(мой|мои|моё|свой|свои|своё)?[\s-]*(выбор[а-яё]*|настройк[а-яё]*|парамет[а-яё]*|предпочтени[а-яё]*|согласи[а-яё]*))?([\s-]+и[\s-]+(закрыть|продолжить))?\s*$/is,
860
+ 'продолжить',
824
861
  ];
package/lib/heuristics.ts CHANGED
@@ -239,7 +239,8 @@ function getPopupLikeElements(timeout = POPUP_SEARCH_MAX_TIME): HTMLElement[] {
239
239
  for (let node = walker.nextNode(); node; node = walker.nextNode()) {
240
240
  found.push(node as HTMLElement);
241
241
  }
242
- return excludeContainers(found);
242
+ // Empty positioned children should not displace a contentful popup container.
243
+ return excludeContainers(found.filter((element) => element.innerText?.trim()));
243
244
  }
244
245
 
245
246
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duckduckgo/autoconsent",
3
- "version": "16.30.0",
3
+ "version": "16.32.0",
4
4
  "description": "",
5
5
  "types": "./dist/types/web.d.ts",
6
6
  "exports": {
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "doordash-storefront",
3
+ "vendorUrl": "https://www.doordash.com/",
4
+ "prehideSelectors": ["[data-testid=\"cookie-banner\"][class*=\"cookie-banner-styles__StyledCookieBanner\"]"],
5
+ "detectCmp": [
6
+ {
7
+ "exists": "[data-testid=\"cookie-banner\"][class*=\"cookie-banner-styles__StyledCookieBanner\"]"
8
+ }
9
+ ],
10
+ "detectPopup": [
11
+ {
12
+ "visible": "[data-testid=\"cookie-banner\"][class*=\"cookie-banner-styles__StyledCookieBanner\"]"
13
+ }
14
+ ],
15
+ "optIn": [
16
+ {
17
+ "waitForThenClick": "[data-testid=\"cookie-banner\"] button[kind=\"BUTTON/TERTIARY\"]",
18
+ "retry": 15,
19
+ "retryInterval": 300
20
+ }
21
+ ],
22
+ "optOut": [
23
+ {
24
+ "waitForThenClick": "[data-testid=\"cookie-banner\"] button[kind=\"BUTTON/FLAT_SECONDARY\"]",
25
+ "retry": 15,
26
+ "retryInterval": 300
27
+ }
28
+ ],
29
+ "test": [
30
+ {
31
+ "cookieContains": "cookie_tracking_enabled"
32
+ }
33
+ ]
34
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "walmart-ca",
3
+ "vendorUrl": "https://www.walmart.ca/",
4
+ "cosmetic": false,
5
+ "runContext": {
6
+ "urlPattern": "^https?://(www\\.)?walmart\\.ca/"
7
+ },
8
+ "prehideSelectors": [],
9
+ "detectCmp": [
10
+ {
11
+ "exists": "a[href=\"https://www.walmartcanada.ca/cookie-notice\"]"
12
+ }
13
+ ],
14
+ "detectPopup": [
15
+ {
16
+ "visible": "a[href=\"https://www.walmartcanada.ca/cookie-notice\"]"
17
+ }
18
+ ],
19
+ "optIn": [
20
+ {
21
+ "waitForThenClick": "button[data-dca-intent=\"open\"][role=\"link\"]"
22
+ },
23
+ {
24
+ "waitForThenClick": "button[aria-label=\"acceptAll button\"]"
25
+ }
26
+ ],
27
+ "optOut": [
28
+ {
29
+ "waitForThenClick": "button[data-dca-intent=\"open\"][role=\"link\"]",
30
+ "retry": 5,
31
+ "retryInterval": 500
32
+ },
33
+ {
34
+ "waitForThenClick": "xpath///button[contains(., 'Save settings') or contains(., 'Enregistrer les paramètres')]"
35
+ }
36
+ ],
37
+ "test": [
38
+ {
39
+ "waitForVisible": "a[href=\"https://www.walmartcanada.ca/cookie-notice\"]",
40
+ "check": "none",
41
+ "timeout": 5000
42
+ }
43
+ ]
44
+ }
@@ -4,7 +4,7 @@
4
4
  "prehideSelectors": ["#cookies-use-alert"],
5
5
  "detectCmp": [
6
6
  {
7
- "any": [{ "exists": "#cookies-use-alert" }, { "exists": ".disclaimer-opened #disclaimer-cookies" }]
7
+ "any": [{ "exists": "#cookies-use-alert" }, { "exists": ".disclaimer-opened #disclaimer-reject_cookies" }]
8
8
  }
9
9
  ],
10
10
  "detectPopup": [
@@ -7,7 +7,7 @@
7
7
  "prehideSelectors": [],
8
8
  "detectCmp": [
9
9
  {
10
- "exists": ".disclaimer-opened #disclaimer-cookies"
10
+ "exists": ".disclaimer-opened #disclaimer-reject_cookies"
11
11
  }
12
12
  ],
13
13
  "detectPopup": [