@duckduckgo/autoconsent 15.0.0 → 16.0.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 (90) hide show
  1. package/.github/dependabot.yml +0 -3
  2. package/.github/workflows/release.yml +0 -4
  3. package/AGENTS.md +3 -3
  4. package/CHANGELOG.md +32 -0
  5. package/build.sh +3 -2
  6. package/dist/addon-firefox/background.bundle.js +8 -24
  7. package/dist/addon-firefox/compact-rules.json +1 -1
  8. package/dist/addon-firefox/content.bundle.js +184 -686
  9. package/dist/addon-firefox/manifest.json +1 -1
  10. package/dist/addon-firefox/popup.bundle.js +196 -25
  11. package/dist/addon-firefox/popup.html +98 -5
  12. package/dist/addon-firefox/rule-index.json +1 -0
  13. package/dist/addon-firefox/rules.json +1 -1
  14. package/dist/addon-mv3/background.bundle.js +8 -24
  15. package/dist/addon-mv3/compact-rules.json +1 -1
  16. package/dist/addon-mv3/content.bundle.js +184 -686
  17. package/dist/addon-mv3/manifest.json +1 -1
  18. package/dist/addon-mv3/popup.bundle.js +196 -25
  19. package/dist/addon-mv3/popup.html +98 -5
  20. package/dist/addon-mv3/rule-index.json +1 -0
  21. package/dist/addon-mv3/rules.json +1 -1
  22. package/dist/autoconsent.cjs.js +184 -685
  23. package/dist/autoconsent.esm.js +184 -685
  24. package/dist/autoconsent.playwright.js +185 -689
  25. package/dist/autoconsent.standalone.js +3625 -0
  26. package/dist/types/cmps/base.d.ts +3 -3
  27. package/dist/types/cmps/trustarc-top.d.ts +1 -1
  28. package/dist/types/eval-handler.d.ts +3 -3
  29. package/dist/types/heuristics.d.ts +2 -2
  30. package/dist/types/types.d.ts +10 -23
  31. package/dist/types/web.d.ts +0 -9
  32. package/docs/api.md +1 -8
  33. package/docs/puppeteer.md +1 -3
  34. package/docs/rule-syntax.md +0 -17
  35. package/eslint.config.mjs +1 -1
  36. package/lib/cmps/base.ts +5 -6
  37. package/lib/cmps/conversant.ts +7 -4
  38. package/lib/cmps/trustarc-top.ts +1 -1
  39. package/lib/cmps/uniconsent.ts +4 -4
  40. package/lib/eval-handler.ts +6 -3
  41. package/lib/heuristics.ts +15 -15
  42. package/lib/types.ts +9 -21
  43. package/lib/utils.ts +13 -9
  44. package/lib/web.ts +3 -53
  45. package/package.json +4 -17
  46. package/playwright/content.ts +1 -2
  47. package/readme.md +3 -6
  48. package/rules/autoconsent/dreamlab-cmp.json +13 -0
  49. package/rules/build.ts +4 -23
  50. package/rules/compact-rules.json +1 -1
  51. package/rules/rule-index-builder.ts +58 -0
  52. package/rules/rule-index.json +1 -0
  53. package/rules/rules.json +1 -1
  54. package/standalone/content.ts +101 -0
  55. package/tests/dreamlab-cmp.spec.ts +3 -0
  56. package/tests/quantcast.spec.ts +0 -6
  57. package/tests-wtr/heuristics/get-actionable-popups.ts +13 -13
  58. package/tests-wtr/heuristics/heuristic-cmp.ts +32 -33
  59. package/tests-wtr/lifecycle/find-cmp.ts +5 -8
  60. package/tests-wtr/web/do-opt-in.test.ts +0 -1
  61. package/tests-wtr/web/do-opt-out.test.ts +0 -1
  62. package/tests-wtr/web/do-self-test.test.ts +0 -1
  63. package/tests-wtr/web/filter-cmps.test.ts +0 -1
  64. package/tests-wtr/web/receive-message-callback.test.ts +1 -2
  65. package/tests-wtr/web/update-state.test.ts +1 -3
  66. package/tsconfig.build.json +1 -1
  67. package/.github/workflows/update-filterlist.yml +0 -42
  68. package/dist/autoconsent.extra.cjs.js +0 -15709
  69. package/dist/autoconsent.extra.esm.js +0 -15641
  70. package/dist/types/cmps/consentomatic.d.ts +0 -34
  71. package/dist/types/consentomatic/index.d.ts +0 -2
  72. package/dist/types/consentomatic/tools.d.ts +0 -10
  73. package/dist/types/filterlist-engine.d.ts +0 -2
  74. package/dist/types/filterlist-utils.d.ts +0 -4
  75. package/dist/types/web-extra.d.ts +0 -11
  76. package/lib/cmps/consentomatic.ts +0 -97
  77. package/lib/consentomatic/index.ts +0 -252
  78. package/lib/consentomatic/tools.ts +0 -199
  79. package/lib/filterlist-engine.ts +0 -5
  80. package/lib/filterlist-utils.ts +0 -45
  81. package/lib/web-extra.ts +0 -116
  82. package/rules/consentomatic.json +0 -1
  83. package/rules/filterlist.txt +0 -18440
  84. package/rules/filterlists/overrides.txt +0 -4
  85. package/scripts/compile-filterlist.mjs +0 -63
  86. package/scripts/fetch-easylist.sh +0 -25
  87. package/scripts/rebuild-filterlist.mjs +0 -216
  88. package/tests/oil.spec.ts +0 -7
  89. package/tests/springer.spec.ts +0 -7
  90. package/tests/wordpressgdpr.spec.ts +0 -6
@@ -1,4 +1,4 @@
1
- import { AutoCMP, DomActionsProvider, PopupData, PopupHandlingMode } from '../types';
1
+ import { AutoCMP, DomActionsProvider, HeuristicLevel, PopupData } from '../types';
2
2
  import { AutoConsentCMPRule, AutoConsentRuleStep, ElementSelector, HideMethod, RunContext, VisibilityCheck } from '../rules';
3
3
  import AutoConsent from '../web';
4
4
  import { snippets } from '../eval-snippets';
@@ -62,8 +62,8 @@ export declare class AutoConsentCMP extends AutoConsentCMPBase {
62
62
  }
63
63
  export declare class AutoConsentHeuristicCMP extends AutoConsentCMPBase {
64
64
  popups: PopupData[];
65
- mode: PopupHandlingMode;
66
- constructor(autoconsentInstance: AutoConsent, mode?: PopupHandlingMode);
65
+ mode: HeuristicLevel;
66
+ constructor(autoconsentInstance: AutoConsent, mode?: HeuristicLevel);
67
67
  get hasSelfTest(): boolean;
68
68
  get isIntermediate(): boolean;
69
69
  get isCosmetic(): boolean;
@@ -5,7 +5,7 @@ export default class TrustArcTop extends AutoConsentCMPBase {
5
5
  name: string;
6
6
  prehideSelectors: string[];
7
7
  runContext: RunContext;
8
- _shortcutButton: HTMLElement;
8
+ _shortcutButton: HTMLElement | null;
9
9
  _optInDone: boolean;
10
10
  constructor(autoconsentInstance: AutoConsent);
11
11
  get hasSelfTest(): boolean;
@@ -3,14 +3,14 @@ import { ContentScriptMessage } from './messages';
3
3
  declare class Deferred<T> {
4
4
  id: string;
5
5
  promise: Promise<T>;
6
- resolve: (value?: T) => void;
7
- reject: (reason?: any) => void;
6
+ resolve: (value: T) => void;
7
+ reject: (reason?: unknown) => void;
8
8
  timer: number;
9
9
  constructor(id: string, timeout?: number);
10
10
  }
11
11
  type EvalState = {
12
12
  pending: Map<string, Deferred<boolean>>;
13
- sendContentMessage: (message: ContentScriptMessage) => void;
13
+ sendContentMessage: ((message: ContentScriptMessage) => void) | null;
14
14
  };
15
15
  export declare const evalState: EvalState;
16
16
  export declare function requestEval(code: string, snippetId?: keyof typeof snippets): Promise<boolean>;
@@ -1,9 +1,9 @@
1
- import { ButtonData, ButtonRegexClassification, PopupData, PopupHandlingMode } from './types';
1
+ import { ButtonData, ButtonRegexClassification, HeuristicLevel, PopupData } from './types';
2
2
  export declare function checkHeuristicPatterns(allText: string, detectPatterns?: RegExp[]): {
3
3
  patterns: string[];
4
4
  snippets: string[];
5
5
  };
6
- export declare function getActionablePopups(mode?: PopupHandlingMode, timeout?: number): PopupData[];
6
+ export declare function getActionablePopups(mode?: HeuristicLevel, timeout?: number): PopupData[];
7
7
  export declare function classifyButtons(buttons: ButtonData[]): void;
8
8
  export declare function cleanButtonText(buttonText: string): string;
9
9
  export declare function classifyButtonTextRegex(buttonText: string): ButtonRegexClassification;
@@ -1,5 +1,4 @@
1
1
  import { ContentScriptMessage } from './messages';
2
- import { ConsentOMaticConfig } from './cmps/consentomatic';
3
2
  import { AutoConsentCMPRule, ElementSelector, HideMethod, RunContext, VisibilityCheck } from './rules';
4
3
  import { CompactCMPRuleset } from './encoding';
5
4
  export type MessageSender = (message: ContentScriptMessage) => Promise<void>;
@@ -43,9 +42,6 @@ export interface DomActionsProvider {
43
42
  export type RuleBundle = {
44
43
  autoconsent: AutoConsentCMPRule[];
45
44
  compact?: CompactCMPRuleset;
46
- consentomatic?: {
47
- [name: string]: ConsentOMaticConfig;
48
- };
49
45
  };
50
46
  export type AutoAction = 'optOut' | 'optIn' | null;
51
47
  export type Config = {
@@ -58,9 +54,7 @@ export type Config = {
58
54
  detectRetries: number;
59
55
  isMainWorld: boolean;
60
56
  prehideTimeout: number;
61
- enableFilterList: boolean;
62
57
  enableHeuristicDetection: boolean;
63
- enableHeuristicAction: boolean;
64
58
  enablePopupMutationObserver: boolean;
65
59
  visualTest: boolean;
66
60
  logs: {
@@ -74,12 +68,10 @@ export type Config = {
74
68
  };
75
69
  performanceLoggingEnabled: boolean;
76
70
  heuristicPopupSearchTimeout: number;
77
- heuristicMode: PopupHandlingMode;
71
+ heuristicMode: HeuristicLevel;
78
72
  };
79
- export type LifecycleState = 'loading' | 'initialized' | 'waitingForInitResponse' | 'started' | 'nothingDetected' | 'cosmeticFiltersDetected' | 'cmpDetected' | 'openPopupDetected' | 'runningOptOut' | 'runningOptIn' | 'optOutSucceeded' | 'optOutFailed' | 'optInSucceeded' | 'optInFailed' | 'done';
73
+ export type LifecycleState = 'loading' | 'initialized' | 'waitingForInitResponse' | 'started' | 'nothingDetected' | 'cmpDetected' | 'openPopupDetected' | 'runningOptOut' | 'runningOptIn' | 'optOutSucceeded' | 'optOutFailed' | 'optInSucceeded' | 'optInFailed' | 'done';
80
74
  export type ConsentState = {
81
- cosmeticFiltersOn: boolean;
82
- filterListReported: boolean;
83
75
  lifecycle: LifecycleState;
84
76
  prehideOn: boolean;
85
77
  findCmpAttempts: number;
@@ -103,19 +95,14 @@ export interface PopupData {
103
95
  text: string;
104
96
  element: HTMLElement;
105
97
  buttons: ButtonData[];
106
- regexClassification?: PopupHandlingMode;
98
+ regexClassification?: PopupClassification;
107
99
  }
108
100
  /**
109
- * Controls the behavior of the heuristic popup detection.
110
- * - Reject: Will click the reject button on a popup if it exists.
111
- * - Tier1: Will also click the acknowledge button on a popup if it exists, and no Reject button exists.
112
- * - Tier2: Will also click the accept button on a popup if it exists, and no Reject or Acknowledge button exists.
113
- * - None: Disabled
101
+ * The user setting for autoconsent and heuristic detection.
102
+ * - off: Heuristic detection is disabled.
103
+ * - reject: Heuristic detection is enabled, and will click the reject button on a popup if it exists.
104
+ * - tier1: Heuristic detection is enabled, and will click the acknowledge button on a popup if it exists, and no Reject or Settings button exists.
105
+ * - tier2: Heuristic detection is enabled, and will click the accept button on a popup if it exists, and no Reject, Acknowledge, or Settings button exists.
114
106
  */
115
- export declare const PopupHandlingModes: {
116
- readonly None: -1;
117
- readonly Reject: 0;
118
- readonly Tier1: 1;
119
- readonly Tier2: 2;
120
- };
121
- export type PopupHandlingMode = (typeof PopupHandlingModes)[keyof typeof PopupHandlingModes];
107
+ export type HeuristicLevel = 'off' | 'reject' | 'tier1' | 'tier2';
108
+ export type PopupClassification = 'none' | 'reject' | 'tier1' | 'tier2';
@@ -1,9 +1,7 @@
1
1
  import { MessageSender, AutoCMP, RuleBundle, Config, ConsentState } from './types';
2
- import { ConsentOMaticConfig } from './cmps/consentomatic';
3
2
  import { AutoConsentCMPRule } from './rules';
4
3
  import { BackgroundMessage } from './messages';
5
4
  import { DomActions } from './dom-actions';
6
- import { FiltersEngine } from '@ghostery/adblocker';
7
5
  export { snippets as evalSnippets } from './eval-snippets';
8
6
  export { filterCompactRules, encodeRules, decodeRules } from './encoding';
9
7
  export type { CompactRuleStep, CompactCMPRuleStep, CompactCMPRule, CompactCMPRuleset, IndexedCMPRuleset } from './encoding';
@@ -18,21 +16,17 @@ export default class AutoConsent {
18
16
  foundCmp?: AutoCMP;
19
17
  state: ConsentState;
20
18
  domActions: DomActions;
21
- filtersEngine?: FiltersEngine;
22
19
  sendContentMessage: MessageSender;
23
- protected cosmeticStyleSheet?: CSSStyleSheet;
24
20
  protected focusedElement?: HTMLElement;
25
21
  constructor(sendContentMessage: MessageSender, config?: Partial<Config> | null, declarativeRules?: RuleBundle | null);
26
22
  get config(): Config;
27
23
  initialize(config: Partial<Config>, declarativeRules: RuleBundle | null): void;
28
- initializeFilterList(): void;
29
24
  get shouldPrehide(): boolean;
30
25
  saveFocus(): void;
31
26
  restoreFocus(): void;
32
27
  addDynamicRules(): void;
33
28
  parseDeclarativeRules(declarativeRules: RuleBundle): void;
34
29
  addDeclarativeCMP(ruleset: AutoConsentCMPRule): void;
35
- addConsentomaticCMP(name: string, config: ConsentOMaticConfig): void;
36
30
  start(): void;
37
31
  _start(): Promise<boolean>;
38
32
  findCmp(retries: number): Promise<AutoCMP[]>;
@@ -52,9 +46,6 @@ export default class AutoConsent {
52
46
  waitForPopup(cmp: AutoCMP, retries?: number, interval?: number): Promise<boolean>;
53
47
  prehideElements(): boolean;
54
48
  undoPrehide(): void;
55
- undoCosmetics(): void;
56
- reportFilterlist(): void;
57
- filterListFallback(): boolean;
58
49
  updateState(change: Partial<ConsentState>): void;
59
50
  receiveMessageCallback(message: BackgroundMessage): Promise<void>;
60
51
  measurePerformance(): {
package/docs/api.md CHANGED
@@ -12,7 +12,6 @@ const consent = new AutoConsent( // make sure not to leak anything to the page g
12
12
  disabledCmps: [],
13
13
  enablePrehide: true,
14
14
  enableCosmeticRules: true,
15
- enableFilterList: false,
16
15
  detectRetries: 20,
17
16
  isMainWorld: false,
18
17
  prehideTimeout: 2000,
@@ -27,7 +26,6 @@ const consent = new AutoConsent( // make sure not to leak anything to the page g
27
26
  },
28
27
  { // optionally, pass JSON rules
29
28
  autoconsent: [ ... ],
30
- consentomatic: [ ... ],
31
29
  }
32
30
  );
33
31
 
@@ -59,14 +57,9 @@ sequenceDiagram
59
57
 
60
58
  activate CS
61
59
  Note right of CS: Parse rules and initialize autoconsent code
62
- Note right of CS: apply prehideSelectors and filterlist rules (if enabled)
60
+ Note right of CS: apply prehideSelectors (if enabled)
63
61
  Note right of CS: wait for DOMContentLoaded
64
62
 
65
- opt if filterlist enabled and filterlist selectors matched
66
- CS -->> BG: cmpDetected (with cmp: "filterList")
67
- CS -->> BG: popupFound (with cmp: "filterList")
68
- end
69
-
70
63
  Note right of CS: detect a CMP presence (not necessarily visible)
71
64
 
72
65
  CS ->> BG: cmpDetected
package/docs/puppeteer.md CHANGED
@@ -40,7 +40,6 @@ const autoconsentConfig = {
40
40
  detectRetries: 20,
41
41
  isMainWorld: false,
42
42
  prehideTimeout: 2000,
43
- enableFilterList: false,
44
43
  enableHeuristicDetection: true,
45
44
  enableHeuristicAction: true,
46
45
  logs: {
@@ -144,13 +143,12 @@ Key options:
144
143
  | `enableCosmeticRules` | boolean | — | Enable rules that hide popups via CSS |
145
144
  | `enableGeneratedRules` | boolean | — | Include auto-generated rules |
146
145
  | `detectRetries` | number | — | How many times to retry CMP detection |
147
- | `enableFilterList` | boolean | — | Enable ABP/uBO cosmetic filter support (requires the `/extra` build) |
148
146
 
149
147
  ## Rules
150
148
 
151
149
  The `initResp` message **must** include a `rules` object. Without rules, no CMPs will be detected. The package ships pre-built rule bundles:
152
150
 
153
- - `@duckduckgo/autoconsent/rules/rules.json` — full rules (includes `autoconsent` and `consentomatic` arrays)
151
+ - `@duckduckgo/autoconsent/rules/rules.json` — full rules (includes `autoconsent` array)
154
152
  - `@duckduckgo/autoconsent/rules/compact-rules.json` — compact encoding for opt-out only (smaller payload)
155
153
 
156
154
  Load either file and pass it as the `rules` field in the `initResp` message.
@@ -225,23 +225,6 @@ Sometimes the opt-out process requires actions that span across multiple pages o
225
225
 
226
226
  Some rules do not interact with the page, and only hide the cookie pop-ups with CSS. These rules are marked with the `cosmetic: true` flag. They are useful for pop-ups that do not provide a Reject button. Cosmetic rules can be disabled with the `enableCosmeticRules` config option.
227
227
 
228
- ### Filterlist
229
- Autoconsent supports cosmetic filters in common ABP/uBO format. For performance reasons, it needs to be bundled at build time. At the moment we include cosmetic filters from [Easylist Cookie](https://github.com/easylist/easylist/tree/master/easylist_cookie).
230
- Note that by default filterlist rules are not included, as this significantly increases the resulting bundle size. To use filterlist rules, you need to explicitly import the "extra" version of the library (`@duckduckgo/autoconsent/extra`), and set the `enableFilterlist` config option to `true`.
231
-
232
- ```javascript
233
- // import the library version with bundled filterlist rules
234
- import AutoConsent from '@duckduckgo/autoconsent/extra'
235
-
236
- // ...
237
-
238
- new AutoConsent({
239
- enableFilterlist: true,
240
- // other options
241
- })
242
-
243
- ```
244
-
245
228
  ## Context filters
246
229
 
247
230
  By default, rules will be executed in all top-level documents. Some rules are designed for specific contexts (e.g. only nested iframes, or only specific URLs). This can be configured in `runContext` field (see [runContext](#rule-syntax-reference) above).
package/eslint.config.mjs CHANGED
@@ -8,7 +8,7 @@ const c = tseslint.config(
8
8
  ...ddgConfig,
9
9
 
10
10
  {
11
- ignores: ['lib/consentomatic/**/*', 'dist/**/*', '.agents/**/*'],
11
+ ignores: ['dist/**/*', '.agents/**/*'],
12
12
  },
13
13
 
14
14
  {
package/lib/cmps/base.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AutoCMP, DomActionsProvider, PopupData, PopupHandlingMode, PopupHandlingModes } from '../types';
1
+ import { AutoCMP, DomActionsProvider, HeuristicLevel, PopupData } from '../types';
2
2
  import { AutoConsentCMPRule, AutoConsentRuleStep, ElementSelector, HideMethod, RunContext, VisibilityCheck } from '../rules';
3
3
  import { requestEval } from '../eval-handler';
4
4
  import AutoConsent from '../web';
@@ -419,9 +419,9 @@ export class AutoConsentCMP extends AutoConsentCMPBase {
419
419
 
420
420
  export class AutoConsentHeuristicCMP extends AutoConsentCMPBase {
421
421
  popups: PopupData[] = [];
422
- mode: PopupHandlingMode;
422
+ mode: HeuristicLevel;
423
423
 
424
- constructor(autoconsentInstance: AutoConsent, mode: PopupHandlingMode = PopupHandlingModes.Reject) {
424
+ constructor(autoconsentInstance: AutoConsent, mode: HeuristicLevel = 'reject') {
425
425
  super(autoconsentInstance);
426
426
  this.name = 'HEURISTIC';
427
427
  this.runContext = {
@@ -453,7 +453,7 @@ export class AutoConsentHeuristicCMP extends AutoConsentCMPBase {
453
453
  performance.measure('heuristicDetector', 'heuristicDetectorStart', 'heuristicDetectorEnd');
454
454
 
455
455
  if (this.popups.length > 0) {
456
- this.name = `HEURISTIC-TIER${this.popups[0].regexClassification}`;
456
+ this.name = `HEURISTIC-${this.popups[0].regexClassification?.toUpperCase()}`;
457
457
  return Promise.resolve(true);
458
458
  }
459
459
  return Promise.resolve(false);
@@ -473,8 +473,7 @@ export class AutoConsentHeuristicCMP extends AutoConsentCMPBase {
473
473
  const popup = this.popups[0];
474
474
  const level = popup.regexClassification;
475
475
  const buttons = popup.buttons;
476
- const targetButtonType =
477
- level === PopupHandlingModes.Reject ? 'reject' : level === PopupHandlingModes.Tier1 ? 'acknowledge' : 'accept';
476
+ const targetButtonType = level === 'reject' ? 'reject' : level === 'tier1' ? 'acknowledge' : 'accept';
478
477
  return buttons.find((button) => button.regexClassification === targetButtonType);
479
478
  }
480
479
 
@@ -41,12 +41,15 @@ export default class Conversant extends AutoConsentCMPBase {
41
41
  (<HTMLElement>item.querySelector('.cmp-accordion-item-title')).click();
42
42
  await waitFor(() => !!item.querySelector('.cmp-accordion-item-content.cmp-active'), 10, 50);
43
43
  const content = item.querySelector('.cmp-accordion-item-content.cmp-active');
44
+ if (!content) {
45
+ return false;
46
+ }
44
47
  content
45
- .querySelectorAll('.cmp-toggle-actions .cmp-toggle-deny:not(.cmp-toggle-deny--active)')
46
- .forEach((e: HTMLElement) => e.click());
48
+ .querySelectorAll<HTMLElement>('.cmp-toggle-actions .cmp-toggle-deny:not(.cmp-toggle-deny--active)')
49
+ .forEach((e) => e.click());
47
50
  content
48
- .querySelectorAll('.cmp-toggle-actions .cmp-toggle-checkbox:not(.cmp-toggle-checkbox--active)')
49
- .forEach((e: HTMLElement) => e.click());
51
+ .querySelectorAll<HTMLElement>('.cmp-toggle-actions .cmp-toggle-checkbox:not(.cmp-toggle-checkbox--active)')
52
+ .forEach((e) => e.click());
50
53
  // await waitFor(() => !item.querySelector('.cmp-toggle-deny--active,.cmp-toggle-checkbox--active'), 5, 50); // this may take a long time
51
54
  }
52
55
  await this.click('.cmp-main-button:not(.cmp-main-button--primary)');
@@ -18,7 +18,7 @@ export default class TrustArcTop extends AutoConsentCMPBase {
18
18
  frame: false,
19
19
  };
20
20
 
21
- _shortcutButton: HTMLElement;
21
+ _shortcutButton: HTMLElement | null;
22
22
  _optInDone: boolean;
23
23
 
24
24
  constructor(autoconsentInstance: AutoConsent) {
@@ -29,8 +29,8 @@ export default class Uniconsent extends AutoConsentCMPBase {
29
29
 
30
30
  async optOut() {
31
31
  await this.waitForElement('.unic button', 1000);
32
- document.querySelectorAll('.unic button').forEach((button: HTMLButtonElement) => {
33
- const text = button.textContent;
32
+ document.querySelectorAll<HTMLButtonElement>('.unic button').forEach((button) => {
33
+ const text = button.textContent || '';
34
34
  if (text.includes('Manage Options') || text.includes('Optionen verwalten')) {
35
35
  button.click();
36
36
  }
@@ -39,14 +39,14 @@ export default class Uniconsent extends AutoConsentCMPBase {
39
39
  if (await this.waitForElement('.unic input[type=checkbox]', 1000)) {
40
40
  await this.waitForElement('.unic button', 1000);
41
41
 
42
- document.querySelectorAll('.unic input[type=checkbox]').forEach((c: HTMLInputElement) => {
42
+ document.querySelectorAll<HTMLInputElement>('.unic input[type=checkbox]').forEach((c) => {
43
43
  if (c.checked) {
44
44
  c.click();
45
45
  }
46
46
  });
47
47
 
48
48
  for (const b of <NodeListOf<HTMLButtonElement>>document.querySelectorAll('.unic button')) {
49
- const text = b.textContent;
49
+ const text = b.textContent || '';
50
50
  for (const pattern of ['Confirm Choices', 'Save Choices', 'Auswahl speichern']) {
51
51
  if (text.includes(pattern)) {
52
52
  b.click();
@@ -5,8 +5,8 @@ import { getRandomID } from './random';
5
5
  class Deferred<T> {
6
6
  id: string;
7
7
  promise: Promise<T>;
8
- resolve: (value?: T) => void;
9
- reject: (reason?: any) => void;
8
+ resolve!: (value: T) => void;
9
+ reject!: (reason?: unknown) => void;
10
10
  timer: number;
11
11
 
12
12
  constructor(id: string, timeout = 1000) {
@@ -23,7 +23,7 @@ class Deferred<T> {
23
23
 
24
24
  type EvalState = {
25
25
  pending: Map<string, Deferred<boolean>>;
26
- sendContentMessage: (message: ContentScriptMessage) => void;
26
+ sendContentMessage: ((message: ContentScriptMessage) => void) | null;
27
27
  };
28
28
 
29
29
  export const evalState: EvalState = {
@@ -32,6 +32,9 @@ export const evalState: EvalState = {
32
32
  };
33
33
 
34
34
  export function requestEval(code: string, snippetId?: keyof typeof snippets): Promise<boolean> {
35
+ if (!evalState.sendContentMessage) {
36
+ return Promise.reject(new Error('AutoConsent is not initialized yet'));
37
+ }
35
38
  const id = getRandomID();
36
39
  evalState.sendContentMessage({
37
40
  type: 'eval',
package/lib/heuristics.ts CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  REJECT_PATTERNS,
7
7
  SETTINGS_PATTERNS,
8
8
  } from './heuristic-patterns';
9
- import { ButtonData, ButtonRegexClassification, PopupData, PopupHandlingMode, PopupHandlingModes } from './types';
9
+ import { ButtonData, ButtonRegexClassification, HeuristicLevel, PopupClassification, PopupData } from './types';
10
10
  import { isElementVisible, isTopFrame } from './utils';
11
11
 
12
12
  const BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="submit"], a, [role="button"], [class*="button"]';
@@ -28,7 +28,12 @@ export function checkHeuristicPatterns(allText: string, detectPatterns = DETECT_
28
28
  return { patterns, snippets };
29
29
  }
30
30
 
31
- export function getActionablePopups(mode: PopupHandlingMode = PopupHandlingModes.Reject, timeout = POPUP_SEARCH_MAX_TIME): PopupData[] {
31
+ export function getActionablePopups(mode: HeuristicLevel = 'reject', timeout = POPUP_SEARCH_MAX_TIME): PopupData[] {
32
+ const acceptedLevels: PopupClassification[] =
33
+ mode === 'tier2' ? ['reject', 'tier1', 'tier2'] : mode === 'tier1' ? ['reject', 'tier1'] : ['reject'];
34
+ if (acceptedLevels.length === 0) {
35
+ return [];
36
+ }
32
37
  const popups = getPotentialPopups(timeout);
33
38
  const result = popups.reduce((acc, popup) => {
34
39
  const popupText = popup.text?.trim();
@@ -46,13 +51,8 @@ export function getActionablePopups(mode: PopupHandlingMode = PopupHandlingModes
46
51
  }, [] as PopupData[]);
47
52
  // popups filtered by mode and sorted so a popup with reject will always win.
48
53
  return result
49
- .filter(
50
- (popup) =>
51
- popup.regexClassification !== undefined &&
52
- popup.regexClassification !== PopupHandlingModes.None &&
53
- popup.regexClassification <= mode,
54
- )
55
- .sort((a, b) => (a.regexClassification ?? 0) - (b.regexClassification ?? 0));
54
+ .filter((popup) => popup.regexClassification !== undefined && acceptedLevels.includes(popup.regexClassification))
55
+ .sort((a, b) => ((a.regexClassification ?? '') > (b.regexClassification ?? '') ? 1 : -1));
56
56
  }
57
57
 
58
58
  export function classifyButtons(buttons: ButtonData[]) {
@@ -61,7 +61,7 @@ export function classifyButtons(buttons: ButtonData[]) {
61
61
  }
62
62
  }
63
63
 
64
- function classifyPopup(buttons: ButtonData[]): PopupHandlingMode {
64
+ function classifyPopup(buttons: ButtonData[]): PopupClassification {
65
65
  const { reject, settings, accept, acknowledge } = buttons.reduce(
66
66
  (acc, button) => {
67
67
  if (button.regexClassification && button.regexClassification !== 'other') {
@@ -72,18 +72,18 @@ function classifyPopup(buttons: ButtonData[]): PopupHandlingMode {
72
72
  { reject: 0, settings: 0, accept: 0, acknowledge: 0 },
73
73
  );
74
74
  if (reject > 0) {
75
- return PopupHandlingModes.Reject;
75
+ return 'reject';
76
76
  }
77
77
  if (settings > 0) {
78
- return PopupHandlingModes.None;
78
+ return 'none';
79
79
  }
80
80
  if (acknowledge > 0) {
81
- return PopupHandlingModes.Tier1;
81
+ return 'tier1';
82
82
  }
83
83
  if (accept > 0) {
84
- return accept === 1 ? PopupHandlingModes.Tier2 : PopupHandlingModes.None;
84
+ return accept === 1 ? 'tier2' : 'none';
85
85
  }
86
- return PopupHandlingModes.None;
86
+ return 'none';
87
87
  }
88
88
 
89
89
  /**
package/lib/types.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { ContentScriptMessage } from './messages';
2
- import { ConsentOMaticConfig } from './cmps/consentomatic';
3
2
  import { AutoConsentCMPRule, ElementSelector, HideMethod, RunContext, VisibilityCheck } from './rules';
4
3
  import { CompactCMPRuleset } from './encoding';
5
4
 
@@ -47,7 +46,6 @@ export interface DomActionsProvider {
47
46
  export type RuleBundle = {
48
47
  autoconsent: AutoConsentCMPRule[];
49
48
  compact?: CompactCMPRuleset;
50
- consentomatic?: { [name: string]: ConsentOMaticConfig };
51
49
  };
52
50
 
53
51
  export type AutoAction = 'optOut' | 'optIn' | null;
@@ -62,9 +60,7 @@ export type Config = {
62
60
  detectRetries: number;
63
61
  isMainWorld: boolean;
64
62
  prehideTimeout: number;
65
- enableFilterList: boolean;
66
63
  enableHeuristicDetection: boolean;
67
- enableHeuristicAction: boolean;
68
64
  enablePopupMutationObserver: boolean;
69
65
  visualTest: boolean; // If true, the script will delay before every click action
70
66
  logs: {
@@ -78,7 +74,7 @@ export type Config = {
78
74
  };
79
75
  performanceLoggingEnabled: boolean;
80
76
  heuristicPopupSearchTimeout: number;
81
- heuristicMode: PopupHandlingMode; // controls the behavior of the heuristic popup detection. Has no effect if enableHeuristicDetection is false.
77
+ heuristicMode: HeuristicLevel; // controls the behavior of the heuristic popup detection.
82
78
  };
83
79
 
84
80
  export type LifecycleState =
@@ -87,7 +83,6 @@ export type LifecycleState =
87
83
  | 'waitingForInitResponse'
88
84
  | 'started'
89
85
  | 'nothingDetected'
90
- | 'cosmeticFiltersDetected'
91
86
  | 'cmpDetected'
92
87
  | 'openPopupDetected'
93
88
  | 'runningOptOut'
@@ -99,8 +94,6 @@ export type LifecycleState =
99
94
  | 'done';
100
95
 
101
96
  export type ConsentState = {
102
- cosmeticFiltersOn: boolean; // true if cosmetic filter rules are currently applied.
103
- filterListReported: boolean; // true if the cosmetic filter list has been reported to the user.
104
97
  lifecycle: LifecycleState; // What point in the autoconsent lifecycle this script is at.
105
98
  prehideOn: boolean; // If the script is currently hiding preHide elements.
106
99
  findCmpAttempts: number; // Number of times we tried to find CMPs in this frame.
@@ -127,20 +120,15 @@ export interface PopupData {
127
120
  text: string;
128
121
  element: HTMLElement;
129
122
  buttons: ButtonData[];
130
- regexClassification?: PopupHandlingMode;
123
+ regexClassification?: PopupClassification;
131
124
  }
132
125
 
133
126
  /**
134
- * Controls the behavior of the heuristic popup detection.
135
- * - Reject: Will click the reject button on a popup if it exists.
136
- * - Tier1: Will also click the acknowledge button on a popup if it exists, and no Reject button exists.
137
- * - Tier2: Will also click the accept button on a popup if it exists, and no Reject or Acknowledge button exists.
138
- * - None: Disabled
127
+ * The user setting for autoconsent and heuristic detection.
128
+ * - off: Heuristic detection is disabled.
129
+ * - reject: Heuristic detection is enabled, and will click the reject button on a popup if it exists.
130
+ * - tier1: Heuristic detection is enabled, and will click the acknowledge button on a popup if it exists, and no Reject or Settings button exists.
131
+ * - tier2: Heuristic detection is enabled, and will click the accept button on a popup if it exists, and no Reject, Acknowledge, or Settings button exists.
139
132
  */
140
- export const PopupHandlingModes = {
141
- None: -1,
142
- Reject: 0,
143
- Tier1: 1,
144
- Tier2: 2,
145
- } as const;
146
- export type PopupHandlingMode = (typeof PopupHandlingModes)[keyof typeof PopupHandlingModes];
133
+ export type HeuristicLevel = 'off' | 'reject' | 'tier1' | 'tier2';
134
+ export type PopupClassification = 'none' | 'reject' | 'tier1' | 'tier2';
package/lib/utils.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { HideMethod } from './rules';
2
- import { Config, PopupHandlingModes } from './types';
2
+ import { Config } from './types';
3
3
 
4
4
  // get or create a style container for CSS overrides
5
5
  export function getStyleElement(styleOverrideElementId = 'autoconsent-css-rules'): HTMLStyleElement {
@@ -77,12 +77,10 @@ export function normalizeConfig(providedConfig: any): Config {
77
77
  enableCosmeticRules: true,
78
78
  enableGeneratedRules: true,
79
79
  enableHeuristicDetection: false,
80
- enableHeuristicAction: false,
81
80
  enablePopupMutationObserver: false,
82
81
  detectRetries: 20,
83
82
  isMainWorld: false,
84
83
  prehideTimeout: 2000,
85
- enableFilterList: false,
86
84
  visualTest: false,
87
85
  logs: {
88
86
  lifecycle: false,
@@ -95,7 +93,7 @@ export function normalizeConfig(providedConfig: any): Config {
95
93
  },
96
94
  performanceLoggingEnabled: false,
97
95
  heuristicPopupSearchTimeout: 100,
98
- heuristicMode: PopupHandlingModes.Reject,
96
+ heuristicMode: 'off', // heuristic disabled by default
99
97
  };
100
98
  const updatedConfig: Config = copyObject(defaultConfig);
101
99
  // filter out any unknown entries
@@ -117,13 +115,18 @@ export function scheduleWhenIdle(callback: () => void, timeout = 500) {
117
115
  }
118
116
  }
119
117
 
118
+ type HighlightedHTMLElement = HTMLElement & {
119
+ __oldStyles?: string;
120
+ };
121
+
120
122
  export function highlightNode(node: HTMLElement) {
123
+ const highlightedNode = node as HighlightedHTMLElement;
121
124
  if (!node.style) return;
122
- if (node.__oldStyles !== undefined) {
125
+ if (highlightedNode.__oldStyles !== undefined) {
123
126
  return; // already highlighted
124
127
  }
125
128
  if (node.hasAttribute('style')) {
126
- node.__oldStyles = node.style.cssText;
129
+ highlightedNode.__oldStyles = node.style.cssText;
127
130
  }
128
131
  node.style.animation = 'pulsate .5s infinite';
129
132
  node.style.outline = 'solid red';
@@ -154,10 +157,11 @@ export function highlightNode(node: HTMLElement) {
154
157
  }
155
158
 
156
159
  export function unhighlightNode(node: HTMLElement) {
160
+ const highlightedNode = node as HighlightedHTMLElement;
157
161
  if (!node.style || !node.hasAttribute('style')) return;
158
- if (node.__oldStyles !== undefined) {
159
- node.style.cssText = node.__oldStyles;
160
- delete node.__oldStyles;
162
+ if (highlightedNode.__oldStyles !== undefined) {
163
+ node.style.cssText = highlightedNode.__oldStyles;
164
+ delete highlightedNode.__oldStyles;
161
165
  } else {
162
166
  node.removeAttribute('style');
163
167
  }