@duckduckgo/autoconsent 14.75.0 → 14.76.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 (35) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/addon-firefox/background.bundle.js +5 -3
  3. package/dist/addon-firefox/compact-rules.json +1 -1
  4. package/dist/addon-firefox/content.bundle.js +25 -12
  5. package/dist/addon-firefox/manifest.json +1 -1
  6. package/dist/addon-firefox/rules.json +1 -1
  7. package/dist/addon-mv3/background.bundle.js +5 -3
  8. package/dist/addon-mv3/compact-rules.json +1 -1
  9. package/dist/addon-mv3/content.bundle.js +25 -12
  10. package/dist/addon-mv3/manifest.json +1 -1
  11. package/dist/addon-mv3/rules.json +1 -1
  12. package/dist/autoconsent.cjs.js +25 -12
  13. package/dist/autoconsent.esm.js +25 -12
  14. package/dist/autoconsent.extra.cjs.js +167 -160
  15. package/dist/autoconsent.extra.esm.js +167 -160
  16. package/dist/autoconsent.playwright.js +25 -12
  17. package/lib/cmps/onetrust.ts +25 -4
  18. package/lib/cmps/trustarc-frame.ts +1 -6
  19. package/lib/eval-snippets.ts +8 -3
  20. package/lib/filterlist-engine.ts +2 -2
  21. package/lib/heuristic-patterns.ts +2 -2
  22. package/package.json +1 -1
  23. package/rules/autoconsent/cookieconsent2.json +2 -2
  24. package/rules/autoconsent/cookieconsent3.json +14 -1
  25. package/rules/autoconsent/didomi.json +2 -2
  26. package/rules/compact-rules.json +1 -1
  27. package/rules/filterlist.txt +198 -180
  28. package/rules/rules.json +1 -1
  29. package/tests/cookieconsent3.spec.ts +1 -1
  30. package/tests/didomi.spec.ts +11 -0
  31. package/tests/onetrust.spec.ts +21 -0
  32. package/tests/trustarc.spec.ts +1 -1
  33. package/tests-wtr/heuristics/heuristics-utils.test.ts +9 -0
  34. package/tests-wtr/lifecycle/find-cmp.html +1 -0
  35. package/tests-wtr/lifecycle/find-cmp.ts +7 -0
@@ -467,10 +467,12 @@
467
467
  return false;
468
468
  },
469
469
  EVAL_DIDOMI_TEST: () => {
470
- if (window.Didomi) {
471
- return window.Didomi.getUserConsentStatusForAll().purposes.disabled.length > 0;
470
+ const purposes = window.Didomi?.getCurrentUserStatus?.()?.purposes;
471
+ if (purposes) {
472
+ return Object.values(purposes).some((p) => !p.enabled);
472
473
  }
473
- return false;
474
+ const disabled = window.Didomi?.getUserConsentStatusForAll?.()?.purposes?.disabled;
475
+ return Array.isArray(disabled) && disabled.length > 0;
474
476
  },
475
477
  EVAL_CONSENTMANAGER_1: () => window.__cmp && typeof __cmp("getCMPData") === "object",
476
478
  EVAL_CONSENTMANAGER_2: () => !__cmp("consentStatus").userChoiceExists,
@@ -860,9 +862,9 @@
860
862
  /wijs alles af/gi
861
863
  ];
862
864
  var REJECT_PATTERNS_ENGLISH = [
863
- // e.g. "i reject cookies", "reject all", "reject all cookies", "reject cookies", "deny all", "deny all cookies", "refuse", "refuse all", "refuse cookies", "refuse all cookies", "deny", "reject all and close", "deny all and close", "reject non-essential cookies", "reject all non-essential cookies and continue", "reject optional cookies", "reject additional cookies", "reject targeting cookies", "reject marketing cookies", "reject analytics cookies", "reject tracking cookies", "reject advertising cookies", "reject all and close", "deny all and close"
865
+ // e.g. "i reject cookies", "reject all", "reject all cookies", "reject cookies", "deny all", "deny all cookies", "refuse", "refuse all", "refuse cookies", "refuse all cookies", "deny", "reject all and close", "deny all and close", "reject non-essential cookies", "reject all non-essential cookies and continue", "reject optional cookies", "reject additional cookies", "reject targeting cookies", "reject marketing cookies", "reject analytics cookies", "reject tracking cookies", "reject advertising cookies", "reject all and close", "deny all and close", "i reject all (except strictly necessary)"
864
866
  // note that "reject and subscribe" and "reject and pay" are excluded
865
- /^\s*(i)?\s*(reject|deny|refuse|decline|disable)\s*(all)?\s*(non-essential|optional|additional|targeting|analytics|marketing|unrequired|non-necessary|extra|tracking|advertising)?\s*(cookies)?\s*$/is,
867
+ /^\s*(i)?\s*(reject|deny|refuse|decline|disable)\s*(all)?\s*(non-essential|optional|additional|targeting|analytics|marketing|unrequired|non-necessary|extra|tracking|advertising)?\s*(cookies)?\s*(\(?\s*except\s+(strictly\s+)?(necessary|essential)\s*\)?)?\s*$/is,
866
868
  // e.g. "i do not accept", "i do not accept cookies", "do not accept", "do not accept cookies"
867
869
  /^\s*(i)?\s*do\s+not\s+accept\s*(cookies)?\s*$/is,
868
870
  // e.g. "continue without accepting", "continue without agreeing", "continue without agreeing →"
@@ -2210,16 +2212,13 @@
2210
2212
  return await waitFor(() => this.elementVisible(".switch span:first-child", "any"), 5, 1e3);
2211
2213
  }
2212
2214
  async optOut() {
2213
- if (await this.mainWorldEval("EVAL_TRUSTARC_FRAME_TEST")) {
2214
- return true;
2215
- }
2216
2215
  let timeout = 3e3;
2217
2216
  if (await this.mainWorldEval("EVAL_TRUSTARC_FRAME_GTM")) {
2218
2217
  timeout = 1500;
2219
2218
  }
2220
2219
  await waitFor(() => document.readyState === "complete", 20, 100);
2221
2220
  await this.waitForElement(".mainContent[aria-hidden=false]", timeout);
2222
- if (await this.click(".rejectAll")) {
2221
+ if (await this.click(".rejectAll,.declineAllButtonLower", true)) {
2223
2222
  return true;
2224
2223
  }
2225
2224
  if (this.elementExists(".prefPanel")) {
@@ -2565,14 +2564,21 @@
2565
2564
  return false;
2566
2565
  }
2567
2566
  async detectCmp() {
2568
- return this.elementExists("#onetrust-banner-sdk,#onetrust-pc-sdk");
2567
+ return this.elementExists("#onetrust-banner-sdk") || this.elementVisible("#onetrust-pc-sdk", "any");
2569
2568
  }
2570
2569
  async detectPopup() {
2571
2570
  return this.elementVisible("#onetrust-banner-sdk,#onetrust-pc-sdk", "any");
2572
2571
  }
2573
2572
  async optOut() {
2574
- if (this.elementVisible("#onetrust-reject-all-handler,.ot-pc-refuse-all-handler,.js-reject-cookies", "any")) {
2575
- return await this.click("#onetrust-reject-all-handler,.ot-pc-refuse-all-handler,.js-reject-cookies");
2573
+ await this.wait(500);
2574
+ if (this.elementVisible("#onetrust-reject-all-handler", "any")) {
2575
+ return await this.click("#onetrust-reject-all-handler");
2576
+ }
2577
+ if (this.elementVisible(".ot-pc-refuse-all-handler", "any")) {
2578
+ return await this.click(".ot-pc-refuse-all-handler");
2579
+ }
2580
+ if (this.elementVisible(".js-reject-cookies", "any")) {
2581
+ return await this.click(".js-reject-cookies");
2576
2582
  }
2577
2583
  if (this.elementVisible(".onetrust-close-btn-handler", "any")) {
2578
2584
  const closeBtn = document.querySelector(".onetrust-close-btn-handler");
@@ -2581,6 +2587,13 @@
2581
2587
  if (rejectPatterns.some((pattern) => btnText.includes(pattern))) {
2582
2588
  return await this.click(".onetrust-close-btn-handler");
2583
2589
  }
2590
+ const banner = document.getElementById("onetrust-banner-sdk");
2591
+ const isCloseOnlyNotice = banner?.classList.contains("ot-close-btn-link") && !this.elementExists(
2592
+ "#onetrust-accept-btn-handler,#onetrust-reject-all-handler,#onetrust-pc-btn-handler,.ot-sdk-show-settings,button.js-cookie-settings"
2593
+ );
2594
+ if (isCloseOnlyNotice) {
2595
+ return await this.click(".onetrust-close-btn-handler");
2596
+ }
2584
2597
  }
2585
2598
  if (this.elementExists("#onetrust-pc-btn-handler")) {
2586
2599
  await this.click("#onetrust-pc-btn-handler");
@@ -18,7 +18,7 @@ export default class Onetrust extends AutoConsentCMPBase {
18
18
  }
19
19
 
20
20
  async detectCmp() {
21
- return this.elementExists('#onetrust-banner-sdk,#onetrust-pc-sdk');
21
+ return this.elementExists('#onetrust-banner-sdk') || this.elementVisible('#onetrust-pc-sdk', 'any');
22
22
  }
23
23
 
24
24
  async detectPopup() {
@@ -26,9 +26,16 @@ export default class Onetrust extends AutoConsentCMPBase {
26
26
  }
27
27
 
28
28
  async optOut() {
29
- if (this.elementVisible('#onetrust-reject-all-handler,.ot-pc-refuse-all-handler,.js-reject-cookies', 'any')) {
30
- // 'reject all' shortcut
31
- return await this.click('#onetrust-reject-all-handler,.ot-pc-refuse-all-handler,.js-reject-cookies');
29
+ await this.wait(500);
30
+ // 'reject all' shortcuts
31
+ if (this.elementVisible('#onetrust-reject-all-handler', 'any')) {
32
+ return await this.click('#onetrust-reject-all-handler');
33
+ }
34
+ if (this.elementVisible('.ot-pc-refuse-all-handler', 'any')) {
35
+ return await this.click('.ot-pc-refuse-all-handler');
36
+ }
37
+ if (this.elementVisible('.js-reject-cookies', 'any')) {
38
+ return await this.click('.js-reject-cookies');
32
39
  }
33
40
 
34
41
  if (this.elementVisible('.onetrust-close-btn-handler', 'any')) {
@@ -41,6 +48,20 @@ export default class Onetrust extends AutoConsentCMPBase {
41
48
  if (rejectPatterns.some((pattern) => btnText.includes(pattern))) {
42
49
  return await this.click('.onetrust-close-btn-handler');
43
50
  }
51
+
52
+ // CCPA notice-only variant: the banner has the `ot-close-btn-link` class and
53
+ // contains only a Close button (no Accept, Reject, or Settings). There's no
54
+ // real opt-out path in the DOM, so we click Close to dismiss.
55
+ // See e.g. columbia.com (US/CCPA region).
56
+ const banner = document.getElementById('onetrust-banner-sdk');
57
+ const isCloseOnlyNotice =
58
+ banner?.classList.contains('ot-close-btn-link') &&
59
+ !this.elementExists(
60
+ '#onetrust-accept-btn-handler,#onetrust-reject-all-handler,#onetrust-pc-btn-handler,.ot-sdk-show-settings,button.js-cookie-settings',
61
+ );
62
+ if (isCloseOnlyNotice) {
63
+ return await this.click('.onetrust-close-btn-handler');
64
+ }
44
65
  }
45
66
 
46
67
  if (this.elementExists('#onetrust-pc-btn-handler')) {
@@ -60,11 +60,6 @@ export default class TrustArcFrame extends AutoConsentCMPBase {
60
60
  }
61
61
 
62
62
  async optOut() {
63
- // if the user has already opted out, let's not close the window
64
- if (await this.mainWorldEval('EVAL_TRUSTARC_FRAME_TEST')) {
65
- return true;
66
- }
67
-
68
63
  // When Tags are being controlled through a tag managment system, the window will not call the vendors' opt-out
69
64
  let timeout = 3000;
70
65
  if (await this.mainWorldEval('EVAL_TRUSTARC_FRAME_GTM')) {
@@ -74,7 +69,7 @@ export default class TrustArcFrame extends AutoConsentCMPBase {
74
69
  await waitFor(() => document.readyState === 'complete', 20, 100);
75
70
  await this.waitForElement('.mainContent[aria-hidden=false]', timeout);
76
71
 
77
- if (await this.click('.rejectAll')) {
72
+ if (await this.click('.rejectAll,.declineAllButtonLower', true)) {
78
73
  return true;
79
74
  }
80
75
 
@@ -12,10 +12,15 @@ export const snippets = {
12
12
  return false;
13
13
  },
14
14
  EVAL_DIDOMI_TEST: () => {
15
- if (window.Didomi) {
16
- return window.Didomi.getUserConsentStatusForAll().purposes.disabled.length > 0;
15
+ // getCurrentUserStatus() works under both GDPR and CCPA/CPRA, unlike the legacy
16
+ // getUserConsentStatusForAll() which returns empty arrays in CCPA/CPRA mode.
17
+ // Falls back to the legacy API for older Didomi SDK versions.
18
+ const purposes = window.Didomi?.getCurrentUserStatus?.()?.purposes;
19
+ if (purposes) {
20
+ return Object.values(purposes).some((p) => !p.enabled);
17
21
  }
18
- return false;
22
+ const disabled = window.Didomi?.getUserConsentStatusForAll?.()?.purposes?.disabled;
23
+ return Array.isArray(disabled) && disabled.length > 0;
19
24
  },
20
25
  EVAL_CONSENTMANAGER_1: () => window.__cmp && typeof __cmp('getCMPData') === 'object',
21
26
  EVAL_CONSENTMANAGER_2: () => !__cmp('consentStatus').userChoiceExists,