@duckduckgo/autoconsent 14.65.0 → 14.66.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.
- package/AGENTS.md +332 -0
- package/CHANGELOG.md +22 -0
- package/dist/addon-firefox/content.bundle.js +8 -3
- package/dist/addon-firefox/manifest.json +1 -1
- package/dist/addon-mv3/content.bundle.js +8 -3
- package/dist/addon-mv3/manifest.json +1 -1
- package/dist/autoconsent.cjs.js +8 -3
- package/dist/autoconsent.esm.js +8 -3
- package/dist/autoconsent.extra.cjs.js +171 -60
- package/dist/autoconsent.extra.esm.js +171 -60
- package/dist/autoconsent.playwright.js +8 -3
- package/docs/rule-syntax.md +242 -0
- package/lib/filterlist-engine.ts +2 -2
- package/lib/heuristic-patterns.ts +8 -2
- package/package.json +1 -1
- package/readme.md +2 -245
- package/rules/filterlist.txt +177 -106
- /package/{api.md → docs/api.md} +0 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# Autoconsent Agent Guide
|
|
2
|
+
|
|
3
|
+
A library for automatically handling cookie consent popups on websites. Used in DuckDuckGo browser apps. Rules detect Consent Management Providers (CMPs) and automate opt-out/opt-in flows.
|
|
4
|
+
|
|
5
|
+
## Project Layout
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
lib/ TypeScript source for the autoconsent engine
|
|
9
|
+
lib/cmps/ Code-based CMP rule classes (sourcepoint, onetrust, etc.)
|
|
10
|
+
lib/rules.ts Type definitions for AutoConsentCMPRule and rule steps
|
|
11
|
+
lib/eval-snippets.ts Eval snippets for main-world JS execution
|
|
12
|
+
rules/autoconsent/ Hand-maintained JSON rules
|
|
13
|
+
rules/generated/ Auto-generated JSON rules (auto_XX_domain_hash.json)
|
|
14
|
+
rules/build.ts Merges all rules into rules.json, consentomatic.json, compact-rules.json
|
|
15
|
+
tests/ Playwright E2E test specs (one per CMP)
|
|
16
|
+
tests-wtr/ Web Test Runner unit tests for DOM actions and rule logic
|
|
17
|
+
playwright/runner.ts Test harness: generateCMPTests(name, urls, options)
|
|
18
|
+
addon/ Browser extension (MV3 + Firefox)
|
|
19
|
+
scripts/ Build and validation scripts
|
|
20
|
+
docs/ Reference documentation (rule syntax, internal API)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm ci
|
|
27
|
+
npm run prepublish # full build: compile filterlist, build rules, bundle
|
|
28
|
+
npm run watch # auto-rebuild on changes to lib/, addon/, rules/
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| Command | Purpose |
|
|
32
|
+
|---------|---------|
|
|
33
|
+
| `npm run watch` | Auto-rebuild on changes to `lib/`, `addon/`, `rules/` (runs `prepublish` on each change) |
|
|
34
|
+
| `npm run lint` | ESLint + Prettier + JSON rule schema validation |
|
|
35
|
+
| `npm run lint-fix` | Auto-fix lint and formatting issues |
|
|
36
|
+
| `npm run rule-syntax-check` | Validate `rules/autoconsent/*.json` against the TypeScript schema |
|
|
37
|
+
| `npm run test:lib` | Unit tests (Web Test Runner) |
|
|
38
|
+
| `npm run test` | All Playwright E2E tests |
|
|
39
|
+
| `npm run test:webkit` | Playwright tests in WebKit only |
|
|
40
|
+
| `npm run test:chrome` | Playwright tests in Chrome only |
|
|
41
|
+
| `npm run build-rules` | Rebuild `rules.json`, `consentomatic.json`, `compact-rules.json` |
|
|
42
|
+
| `npm run create-rule` | Scaffold a new JSON rule + test spec |
|
|
43
|
+
|
|
44
|
+
## Working with Rules
|
|
45
|
+
|
|
46
|
+
### JSON Rules
|
|
47
|
+
|
|
48
|
+
JSON rules live in `rules/autoconsent/` (hand-maintained) and `rules/generated/` (auto-generated). Each file defines one CMP rule following the `AutoConsentCMPRule` type in `lib/rules.ts`.
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"name": "example-cmp",
|
|
53
|
+
"prehideSelectors": ["#cookie-banner"],
|
|
54
|
+
"detectCmp": [{ "exists": "#cookie-banner" }],
|
|
55
|
+
"detectPopup": [{ "visible": "#cookie-banner" }],
|
|
56
|
+
"optIn": [{ "waitForThenClick": "#accept-all" }],
|
|
57
|
+
"optOut": [{ "waitForThenClick": "#reject-all" }],
|
|
58
|
+
"test": [{ "cookieContains": "consent=rejected" }]
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
For the complete rule syntax reference (all step types, element selectors, conditionals, etc.), see [docs/rule-syntax.md](docs/rule-syntax.md).
|
|
63
|
+
|
|
64
|
+
### prehideSelectors
|
|
65
|
+
|
|
66
|
+
`prehideSelectors` inject CSS early (before the CMP is even detected) to prevent the cookie popup from flickering on screen. They use `opacity: 0` (not `display: none`) so the popup still occupies layout space and detection via `visible` checks still works. If opt-out doesn't start within 2 seconds, the elements are automatically unhidden to avoid permanently hiding page content.
|
|
67
|
+
|
|
68
|
+
Keep prehideSelectors **narrow** — they are applied across all matching rules simultaneously, so an overly broad selector (e.g. `body`) could hide the entire page during the 2-second window.
|
|
69
|
+
|
|
70
|
+
### Code-Based Rules
|
|
71
|
+
|
|
72
|
+
For CMPs requiring complex non-linear logic, CMP API interaction, or complex multi-path flows, use a TypeScript class extending `AutoConsentCMPBase` in `lib/cmps/`. Examples: `sourcepoint-frame.ts`, `onetrust.ts`, `cookiebot.ts`, `consentmanager.ts`.
|
|
73
|
+
|
|
74
|
+
Code-based rules implement the `AutoCMP` interface: `detectCmp()`, `detectPopup()`, `optOut()`, `optIn()`, and optionally `test()`. They have access to DOM helpers like `this.click()`, `this.waitForElement()`, `this.waitForVisible()`, and `this.elementExists()`.
|
|
75
|
+
|
|
76
|
+
### When to Use Code vs JSON
|
|
77
|
+
|
|
78
|
+
- **JSON:** Linear consent flows, DOM-based detection, single-path opt-out. JSON rules are preferable because they can be shipped in DuckDuckGo apps without a full app release.
|
|
79
|
+
- **Code:** Multi-path branching, CMP JavaScript API calls, `Promise.race()` for competing UI states, complex state machines (e.g., Sourcepoint serving GDPR/CCPA/US National variants on different URL paths).
|
|
80
|
+
|
|
81
|
+
### Selector Strategy
|
|
82
|
+
|
|
83
|
+
Prefer selectors in this order (most stable first):
|
|
84
|
+
|
|
85
|
+
1. **Stable data attributes:** `[data-testid="cookie-reject"]`, `[data-action="sp-cc"]`, `[data-qa="allow-all-cookies"]`
|
|
86
|
+
2. **Stable IDs:** `#sp-cc-accept`, `#cookie-banner` — but avoid dynamic IDs from React Aria (`#react-aria*`), Radix (`#radix-\:*\:`), or CSS Modules (`.sd-cmp-3cRQ2`), which change between builds/sessions.
|
|
87
|
+
3. **Semantic class substrings:** `[class*="cookie-banner"]`, `[class*="reject"]` — avoid full body class lists (`body.home.wp-singular.page-template...`) which break across pages.
|
|
88
|
+
4. **Structural CSS:** `#banner button.secondary` — avoid deep `nth-child` chains from generated rules.
|
|
89
|
+
5. **XPath text matching (fallback):** `xpath///button[contains(., 'Reject')]` — use as a last resort since button text is language-specific and breaks across locales. Same caution applies to `aria-label` attributes, which are often localized.
|
|
90
|
+
6. **Array selectors** for shadow DOM / iframe piercing: `["host-element", "button"]` finds `button` inside the shadow root of `host-element`. Each string in the array narrows the search scope — if an intermediate element has an open `shadowRoot`, the next selector runs inside it; if it's a same-origin iframe, the next selector runs inside its `contentDocument`. Use when a CMP renders inside shadow DOM or a same-origin iframe.
|
|
91
|
+
|
|
92
|
+
When writing or reviewing selectors, also watch out for:
|
|
93
|
+
- **Hardcoded attribute values** that are site-specific — use generic selectors in code-based rules.
|
|
94
|
+
- **Over-qualified selectors** from generated rules — e.g. `div[id][name][role][aria-modal][tabindex][lang]` requiring every attribute to exist, or redundant `:nth-child(2)#some-id` where the ID alone suffices.
|
|
95
|
+
|
|
96
|
+
## Debugging and Fixing Rules
|
|
97
|
+
|
|
98
|
+
### Identifying Broken Rules
|
|
99
|
+
|
|
100
|
+
1. **Playwright test failures** are the primary signal. Run the specific test:
|
|
101
|
+
```bash
|
|
102
|
+
npx playwright test tests/sirdata.spec.ts --project webkit
|
|
103
|
+
```
|
|
104
|
+
2. **Check test output** for which stage failed: `cmpDetected`, `popupFound`, `autoconsentDone`, `optOutResult`, `selfTestResult`.
|
|
105
|
+
3. **Use the test extension** (`dist/addon-mv3/`) for manual debugging. Load it in Chrome, visit the site, and check the devtools panel for step-by-step logs.
|
|
106
|
+
|
|
107
|
+
### Common Failure Modes
|
|
108
|
+
|
|
109
|
+
**Race conditions:** Consent popups load asynchronously. Use `waitFor` / `waitForThenClick` / `waitForVisible` instead of bare `exists` / `click`. Add `{ "wait": 500 }` before critical actions when the CMP has known async initialization. In code-based rules, use `Promise.race()` for multiple possible UI states.
|
|
110
|
+
|
|
111
|
+
**Incorrect consent action selectors:** Generated rules sometimes target a privacy policy link instead of the reject button. Ensure `optOut` steps target an actual reject/decline button.
|
|
112
|
+
|
|
113
|
+
**Region-dependent behavior:** Many CMPs show different dialogs by region (GDPR in EU, CCPA in US). See [Regional Differences](#regional-differences) below.
|
|
114
|
+
|
|
115
|
+
### Fixing JSON Rules
|
|
116
|
+
|
|
117
|
+
1. Read the existing rule to understand its current selectors and flow.
|
|
118
|
+
2. Identify the broken step from test output or by inspecting the site.
|
|
119
|
+
3. Edit the JSON file — apply the fix to every occurrence of the selector within the file (`detectCmp`, `detectPopup`, `optOut`, and `test` often use similar selectors).
|
|
120
|
+
4. For generated rules, the same CMP may appear across multiple region files (`auto_CH_*.json`, `auto_DE_*.json`, etc.). Apply the fix to all affected files.
|
|
121
|
+
5. Run `npm run lint` to validate.
|
|
122
|
+
|
|
123
|
+
### Fixing Code-Based Rules
|
|
124
|
+
|
|
125
|
+
1. Read the CMP class in `lib/cmps/` and trace the failing code path.
|
|
126
|
+
2. Avoid hardcoded attribute values that are site-specific.
|
|
127
|
+
3. Add path/state detection for new CMP variants. Check `location.pathname`, button presence, or URL parameters.
|
|
128
|
+
4. Add fallback paths when variants may not have the expected buttons.
|
|
129
|
+
|
|
130
|
+
### Adding Fallback Paths
|
|
131
|
+
|
|
132
|
+
Use `if`/`then`/`else` for region-dependent or variant-dependent flows:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"if": { "exists": "#reject-button" },
|
|
137
|
+
"then": [{ "waitForThenClick": "#reject-button" }],
|
|
138
|
+
"else": [{
|
|
139
|
+
"if": { "exists": "#manage-cookies" },
|
|
140
|
+
"then": [
|
|
141
|
+
{ "waitForThenClick": "#manage-cookies" },
|
|
142
|
+
{ "waitForThenClick": "#reject-all" }
|
|
143
|
+
],
|
|
144
|
+
"else": [
|
|
145
|
+
{ "waitForThenClick": "[role='button'][title='Close']" }
|
|
146
|
+
]
|
|
147
|
+
}]
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Adding New Rules
|
|
152
|
+
|
|
153
|
+
1. Run `npm run create-rule` to scaffold the JSON + test spec.
|
|
154
|
+
2. Fill in `detectCmp`, `detectPopup`, `optOut`, `optIn` with stable selectors.
|
|
155
|
+
3. Add a `test` array — prefer `cookieContains` when the CMP stores consent in cookies.
|
|
156
|
+
- JSON rules can also use `{ "eval": "SNIPPET_NAME" }` steps to execute predefined JavaScript snippets from `lib/eval-snippets.ts`. Useful for calling CMP APIs (e.g., `window.Cookiebot`, `__cmp('getCMPData')`) in detection, opt-out, or test phases. Each snippet is a named function that returns a boolean. New snippets must be added to `lib/eval-snippets.ts` and referenced by name in the rule JSON.
|
|
157
|
+
4. Run `npm run lint` and `npm run test:lib`.
|
|
158
|
+
5. Test with Playwright: `npx playwright test tests/my-cmp.spec.ts --project webkit`.
|
|
159
|
+
|
|
160
|
+
### When Generated Rules Need Fixes
|
|
161
|
+
|
|
162
|
+
Generated rules (`rules/generated/auto_XX_domain_hash.json`) are created by a crawler and often have:
|
|
163
|
+
- Deep `nth-child` chains that break on layout changes
|
|
164
|
+
- Dynamic IDs from UI frameworks
|
|
165
|
+
- Long body class lists
|
|
166
|
+
- Over-qualified selectors requiring many attributes simultaneously
|
|
167
|
+
|
|
168
|
+
Fixes typically need to be applied across all region variants of the same domain (e.g., `auto_CH_kitbag.com_*.json`, `auto_DE_kitbag.com_*.json`). Search for the domain to find all related files.
|
|
169
|
+
|
|
170
|
+
## Cosmetic Rules
|
|
171
|
+
|
|
172
|
+
Cosmetic rules hide the cookie popup via CSS rather than clicking a reject button. They are marked with `"cosmetic": true` and use `hide` steps in their `optOut` array. Use cosmetic rules when a popup has no reject/decline button — only an "Accept" or "Close" option.
|
|
173
|
+
|
|
174
|
+
### When to Use Cosmetic vs Click-Based Rules
|
|
175
|
+
|
|
176
|
+
A popup should use a **click-based rule** (the default) if it has a reject/decline button. This includes buttons with text like "Reject all", "Only necessary cookies", "Decline", and equivalents in other languages. If the popup only has "Accept" / "OK" / "Close" / "Got it" and no way to reject, use a **cosmetic rule** to hide it.
|
|
177
|
+
|
|
178
|
+
### Common Breakage Patterns
|
|
179
|
+
|
|
180
|
+
Hiding a popup can break the page if the CMP also locks scrolling or adds overlays. Watch for:
|
|
181
|
+
|
|
182
|
+
**Scroll lock via CSS class:** `body` or `html` gets a class like `no-scroll`, `modal-open`, `overflow-hidden`. Fix with:
|
|
183
|
+
```json
|
|
184
|
+
{ "removeClass": "no-scroll", "selector": "body" }
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Scroll lock via inline style:** `body.style.overflow = "hidden"`. Fix with:
|
|
188
|
+
```json
|
|
189
|
+
{ "addStyle": "overflow: auto !important", "selector": "body" }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Overlay preventing clicks:** A `position: fixed` div with high z-index covers the page. Fix by hiding it:
|
|
193
|
+
```json
|
|
194
|
+
{ "hide": "#overlay-selector" }
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Body position lock:** `body.style.position = "fixed"` with `top: -XXpx`. Fix with:
|
|
198
|
+
```json
|
|
199
|
+
{ "setStyle": "", "selector": "body" }
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Cosmetic Rule Structure
|
|
203
|
+
|
|
204
|
+
```json
|
|
205
|
+
{
|
|
206
|
+
"name": "example-cosmetic",
|
|
207
|
+
"cosmetic": true,
|
|
208
|
+
"prehideSelectors": ["#cookie-banner"],
|
|
209
|
+
"detectCmp": [{ "exists": "#cookie-banner" }],
|
|
210
|
+
"detectPopup": [{ "visible": "#cookie-banner" }],
|
|
211
|
+
"optOut": [
|
|
212
|
+
{ "hide": "#cookie-banner" },
|
|
213
|
+
{ "removeClass": "no-scroll", "selector": "body", "optional": true }
|
|
214
|
+
],
|
|
215
|
+
"optIn": [{ "waitForThenClick": "#accept-button" }]
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Add breakage fix steps AFTER the `hide` step in `optOut`. Mark breakage fixes as `"optional": true` since they may not always apply.
|
|
220
|
+
|
|
221
|
+
## Triaging Broken Sites
|
|
222
|
+
|
|
223
|
+
When investigating a site where cookie popup handling is broken or missing:
|
|
224
|
+
|
|
225
|
+
### Step 1: Check Current State
|
|
226
|
+
|
|
227
|
+
Load the bundled extension in Chrome (`dist/addon-mv3/` after `npm run prepublish`), visit the site, and check the devtools panel for autoconsent logs. Determine whether:
|
|
228
|
+
- An existing rule matched but failed (which stage? `detectCmp`, `detectPopup`, `optOut`?)
|
|
229
|
+
- No rule matched at all
|
|
230
|
+
|
|
231
|
+
### Step 2: Diagnose
|
|
232
|
+
|
|
233
|
+
If an **existing rule matched but failed**: identify the broken step from the logs, inspect the site to understand what changed (new selectors, different layout, region variant), and fix the rule.
|
|
234
|
+
|
|
235
|
+
If **no rule matched**: determine the CMP type. Check if the popup is from a known CMP (OneTrust, Sourcepoint, Cookiebot, etc.) by inspecting the banner's HTML, class names, and script sources. If it's a known CMP, the existing rule may need updated detection selectors. If it's unknown, create a new rule.
|
|
236
|
+
|
|
237
|
+
### Step 3: Determine Rule Type
|
|
238
|
+
|
|
239
|
+
- If the popup has a **reject/decline button** → create or fix a click-based rule
|
|
240
|
+
- If the popup has **no reject option** (only accept/close) → create a cosmetic rule
|
|
241
|
+
- If the CMP requires **complex logic** (API calls, multiple UI states, iframe communication) → use a code-based CMP class
|
|
242
|
+
|
|
243
|
+
### Step 4: Implement and Test
|
|
244
|
+
|
|
245
|
+
1. Create or edit the rule file
|
|
246
|
+
2. Add or update the test spec in `tests/`
|
|
247
|
+
3. Run `npm run lint` to validate
|
|
248
|
+
4. Test locally: `npx playwright test tests/<cmp>.spec.ts --project webkit`
|
|
249
|
+
5. Test in multiple regions if the CMP is region-dependent (requires both `REGION` and `PROXY_SERVER` — see [Testing Across Regions](#testing-across-regions))
|
|
250
|
+
6. For cosmetic rules, verify no breakage (scrolling works, page is interactable)
|
|
251
|
+
|
|
252
|
+
## Regional Differences
|
|
253
|
+
|
|
254
|
+
CMPs behave differently depending on the user's region due to different privacy regulations:
|
|
255
|
+
|
|
256
|
+
- **EU/EEA (GDPR):** Full consent dialog with explicit reject/accept options. Most rules target this variant.
|
|
257
|
+
- **US (CCPA/state laws):** Often a simpler notice with just a "Close" button, or a "Do Not Sell" link. Some CMPs show nothing at all in the US.
|
|
258
|
+
- **Other regions:** May show GDPR-like dialogs, simplified notices, or nothing.
|
|
259
|
+
|
|
260
|
+
### Handling Regional Variants in Rules
|
|
261
|
+
|
|
262
|
+
Use `if`/`then`/`else` conditionals to handle different UIs within a single rule. For code-based rules, add path detection (e.g., Sourcepoint's `/privacy-manager/index.html` vs `/us_pm/index.html`).
|
|
263
|
+
|
|
264
|
+
### Testing Across Regions
|
|
265
|
+
|
|
266
|
+
Two things are needed to test from a specific region:
|
|
267
|
+
|
|
268
|
+
1. **`REGION` env var** — filters which test URLs to run (from `data/coverage.json`). This only controls test selection, it does **not** change where requests come from.
|
|
269
|
+
2. **`PROXY_SERVER` env var** — routes browser traffic through a geographic proxy so sites see the correct region. Without a proxy, the site sees your real location regardless of `REGION`.
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
# Local: only filters tests, requests come from your real location
|
|
273
|
+
REGION=DE npx playwright test tests/sirdata.spec.ts --project webkit
|
|
274
|
+
|
|
275
|
+
# With proxy: tests are filtered AND requests are routed through the proxy
|
|
276
|
+
REGION=DE PROXY_SERVER=socks5://proxy.example:1080 npx playwright test tests/sirdata.spec.ts --project webkit
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
In CI, Jenkins loads region-specific `.env` files that set both `REGION` and `PROXY_SERVER` together.
|
|
280
|
+
|
|
281
|
+
Test specs support `skipRegions` and `onlyRegions` to control when tests run:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
generateCMPTests('Sirdata', ['https://gizmodo.com/'], {
|
|
285
|
+
skipRegions: ['US'], // skip test in these regions
|
|
286
|
+
onlyRegions: [], // only run in these regions
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## PR Review Checklist
|
|
291
|
+
|
|
292
|
+
### CI Pipeline
|
|
293
|
+
|
|
294
|
+
Two CI systems run on PRs:
|
|
295
|
+
|
|
296
|
+
1. **GitHub Actions** (`.github/workflows/checks.yml`): runs `npm run lint` and `npm run test:lib` on every push/PR. These must pass.
|
|
297
|
+
2. **Jenkins**: runs Playwright E2E tests in 9 regions (US, GB, AU, CA, DE, FR, NL, CH, NO). Only tests for modified rule files and their corresponding test specs are run. Jenkins posts a PR comment with an artifact ZIP link and a link to the [review tool](https://zok.pw/autoconsent-review-tool/) for inspecting screenshots.
|
|
298
|
+
|
|
299
|
+
### Reviewing New Rule PRs
|
|
300
|
+
|
|
301
|
+
- [ ] `npm run lint` passes (ESLint + Prettier + schema validation)
|
|
302
|
+
- [ ] JSON rule validates against schema (`npm run rule-syntax-check`)
|
|
303
|
+
- [ ] A corresponding test spec exists in `tests/`
|
|
304
|
+
- [ ] Test URLs are reachable and relevant
|
|
305
|
+
- [ ] Selectors are stable (no dynamic IDs, no full body class lists, no CSS module hashes)
|
|
306
|
+
- [ ] `optOut` targets an actual reject/decline button, not a privacy policy link
|
|
307
|
+
- [ ] For generated rule fixes: all region variants are updated consistently
|
|
308
|
+
|
|
309
|
+
### Reviewing Code-Based Rule PRs
|
|
310
|
+
|
|
311
|
+
- [ ] Lint and unit tests pass
|
|
312
|
+
- [ ] No hardcoded site-specific attribute values
|
|
313
|
+
- [ ] Fallback paths exist for regional variants
|
|
314
|
+
- [ ] Uses existing DOM helpers (`this.click()`, `this.waitForElement()`, etc.)
|
|
315
|
+
|
|
316
|
+
### Handling Flaky E2E Tests
|
|
317
|
+
|
|
318
|
+
E2E tests hit live sites and are inherently flaky due to site changes, regional differences, and network conditions. Before concluding a test is broken:
|
|
319
|
+
|
|
320
|
+
- Check Jenkins screenshots in the review tool
|
|
321
|
+
- If a test fails only in certain regions, consider adding `skipRegions`
|
|
322
|
+
- Playwright is configured with retries (2 retries in CI)
|
|
323
|
+
- Verify the site still has the same cookie consent popup by visiting it manually
|
|
324
|
+
|
|
325
|
+
## Verification
|
|
326
|
+
|
|
327
|
+
| Step | Command |
|
|
328
|
+
|------|---------|
|
|
329
|
+
| Schema + formatting | `npm run lint` |
|
|
330
|
+
| Unit tests | `npm run test:lib` |
|
|
331
|
+
| Single CMP E2E test | `npx playwright test tests/<cmp>.spec.ts --project webkit` |
|
|
332
|
+
| Full E2E suite | `npm run test` |
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
# v14.66.0 (Sat Mar 28 2026)
|
|
2
|
+
|
|
3
|
+
#### 🚀 Enhancement
|
|
4
|
+
|
|
5
|
+
- Bump the adblocker group with 2 updates [#1099](https://github.com/duckduckgo/autoconsent/pull/1099) ([@dependabot[bot]](https://github.com/dependabot[bot]) [@muodov](https://github.com/muodov))
|
|
6
|
+
- Update EasyList Cookie to 3d7c4396813e269c757e5e9851d80b7b5db622a0 [#1216](https://github.com/duckduckgo/autoconsent/pull/1216) ([@sammacbeth](https://github.com/sammacbeth) [@github-actions[bot]](https://github.com/github-actions[bot]))
|
|
7
|
+
- Add more "reject" patterns [#1218](https://github.com/duckduckgo/autoconsent/pull/1218) ([@muodov](https://github.com/muodov))
|
|
8
|
+
|
|
9
|
+
#### 📝 Documentation
|
|
10
|
+
|
|
11
|
+
- Reorganize docs, add AGENTS.md [#1217](https://github.com/duckduckgo/autoconsent/pull/1217) ([@cursoragent](https://github.com/cursoragent) [@muodov](https://github.com/muodov))
|
|
12
|
+
|
|
13
|
+
#### Authors: 5
|
|
14
|
+
|
|
15
|
+
- [@dependabot[bot]](https://github.com/dependabot[bot])
|
|
16
|
+
- [@github-actions[bot]](https://github.com/github-actions[bot])
|
|
17
|
+
- Cursor Agent ([@cursoragent](https://github.com/cursoragent))
|
|
18
|
+
- Maxim Tsoy ([@muodov](https://github.com/muodov))
|
|
19
|
+
- Sam Macbeth ([@sammacbeth](https://github.com/sammacbeth))
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
1
23
|
# v14.65.0 (Fri Mar 27 2026)
|
|
2
24
|
|
|
3
25
|
#### 🚀 Enhancement
|
|
@@ -860,10 +860,10 @@
|
|
|
860
860
|
/^\s*(use|accept|allow|continue\s+with)?\s*only\s*(strictly)?\s*(necessary|essentials?|required)?\s*(cookies)?\s*$/is,
|
|
861
861
|
// e.g. "do not sell or share my personal information", "do not sell my personal information"
|
|
862
862
|
// often used in CCPA
|
|
863
|
-
/^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*information\s*$/is
|
|
863
|
+
/^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*information\s*$/is,
|
|
864
|
+
"allow selection",
|
|
865
|
+
"disagree and close"
|
|
864
866
|
// These are impactful, but look error-prone
|
|
865
|
-
// // e.g. "disagree"
|
|
866
|
-
// /^\s*(i)?\s*disagree\s*(and\s+close)?\s*$/i,
|
|
867
867
|
// // e.g. "i do not agree"
|
|
868
868
|
// /^\s*(i\s+)?do\s+not\s+agree\s*$/i,
|
|
869
869
|
];
|
|
@@ -877,6 +877,7 @@
|
|
|
877
877
|
"alleen noodzakelijk",
|
|
878
878
|
"weiger",
|
|
879
879
|
"weiger cookies",
|
|
880
|
+
"selectie toestaan",
|
|
880
881
|
"doorgaan zonder te accepteren",
|
|
881
882
|
"alleen functionele cookies",
|
|
882
883
|
"alleen functioneel",
|
|
@@ -979,6 +980,8 @@
|
|
|
979
980
|
"tout refuser",
|
|
980
981
|
"refuser",
|
|
981
982
|
"refuser tous les cookies",
|
|
983
|
+
"non merci",
|
|
984
|
+
"interdire tous les cookies",
|
|
982
985
|
"je refuse",
|
|
983
986
|
"refuser tout",
|
|
984
987
|
"tout rejeter",
|
|
@@ -1066,6 +1069,8 @@
|
|
|
1066
1069
|
"nur notwendige cookies",
|
|
1067
1070
|
"nur essenzielle cookies akzeptieren",
|
|
1068
1071
|
"nur notwendige cookies verwenden",
|
|
1072
|
+
"nur technisch notwendige",
|
|
1073
|
+
"nur essentielle cookies akzeptieren",
|
|
1069
1074
|
"alles ablehnen",
|
|
1070
1075
|
"nur notwendige",
|
|
1071
1076
|
"alle cookies ablehnen",
|
|
@@ -860,10 +860,10 @@
|
|
|
860
860
|
/^\s*(use|accept|allow|continue\s+with)?\s*only\s*(strictly)?\s*(necessary|essentials?|required)?\s*(cookies)?\s*$/is,
|
|
861
861
|
// e.g. "do not sell or share my personal information", "do not sell my personal information"
|
|
862
862
|
// often used in CCPA
|
|
863
|
-
/^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*information\s*$/is
|
|
863
|
+
/^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*information\s*$/is,
|
|
864
|
+
"allow selection",
|
|
865
|
+
"disagree and close"
|
|
864
866
|
// These are impactful, but look error-prone
|
|
865
|
-
// // e.g. "disagree"
|
|
866
|
-
// /^\s*(i)?\s*disagree\s*(and\s+close)?\s*$/i,
|
|
867
867
|
// // e.g. "i do not agree"
|
|
868
868
|
// /^\s*(i\s+)?do\s+not\s+agree\s*$/i,
|
|
869
869
|
];
|
|
@@ -877,6 +877,7 @@
|
|
|
877
877
|
"alleen noodzakelijk",
|
|
878
878
|
"weiger",
|
|
879
879
|
"weiger cookies",
|
|
880
|
+
"selectie toestaan",
|
|
880
881
|
"doorgaan zonder te accepteren",
|
|
881
882
|
"alleen functionele cookies",
|
|
882
883
|
"alleen functioneel",
|
|
@@ -979,6 +980,8 @@
|
|
|
979
980
|
"tout refuser",
|
|
980
981
|
"refuser",
|
|
981
982
|
"refuser tous les cookies",
|
|
983
|
+
"non merci",
|
|
984
|
+
"interdire tous les cookies",
|
|
982
985
|
"je refuse",
|
|
983
986
|
"refuser tout",
|
|
984
987
|
"tout rejeter",
|
|
@@ -1066,6 +1069,8 @@
|
|
|
1066
1069
|
"nur notwendige cookies",
|
|
1067
1070
|
"nur essenzielle cookies akzeptieren",
|
|
1068
1071
|
"nur notwendige cookies verwenden",
|
|
1072
|
+
"nur technisch notwendige",
|
|
1073
|
+
"nur essentielle cookies akzeptieren",
|
|
1069
1074
|
"alles ablehnen",
|
|
1070
1075
|
"nur notwendige",
|
|
1071
1076
|
"alle cookies ablehnen",
|
package/dist/autoconsent.cjs.js
CHANGED
|
@@ -895,10 +895,10 @@ var REJECT_PATTERNS_ENGLISH = [
|
|
|
895
895
|
/^\s*(use|accept|allow|continue\s+with)?\s*only\s*(strictly)?\s*(necessary|essentials?|required)?\s*(cookies)?\s*$/is,
|
|
896
896
|
// e.g. "do not sell or share my personal information", "do not sell my personal information"
|
|
897
897
|
// often used in CCPA
|
|
898
|
-
/^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*information\s*$/is
|
|
898
|
+
/^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*information\s*$/is,
|
|
899
|
+
"allow selection",
|
|
900
|
+
"disagree and close"
|
|
899
901
|
// These are impactful, but look error-prone
|
|
900
|
-
// // e.g. "disagree"
|
|
901
|
-
// /^\s*(i)?\s*disagree\s*(and\s+close)?\s*$/i,
|
|
902
902
|
// // e.g. "i do not agree"
|
|
903
903
|
// /^\s*(i\s+)?do\s+not\s+agree\s*$/i,
|
|
904
904
|
];
|
|
@@ -912,6 +912,7 @@ var REJECT_PATTERNS_DUTCH = [
|
|
|
912
912
|
"alleen noodzakelijk",
|
|
913
913
|
"weiger",
|
|
914
914
|
"weiger cookies",
|
|
915
|
+
"selectie toestaan",
|
|
915
916
|
"doorgaan zonder te accepteren",
|
|
916
917
|
"alleen functionele cookies",
|
|
917
918
|
"alleen functioneel",
|
|
@@ -1014,6 +1015,8 @@ var REJECT_PATTERNS_FRENCH = [
|
|
|
1014
1015
|
"tout refuser",
|
|
1015
1016
|
"refuser",
|
|
1016
1017
|
"refuser tous les cookies",
|
|
1018
|
+
"non merci",
|
|
1019
|
+
"interdire tous les cookies",
|
|
1017
1020
|
"je refuse",
|
|
1018
1021
|
"refuser tout",
|
|
1019
1022
|
"tout rejeter",
|
|
@@ -1101,6 +1104,8 @@ var REJECT_PATTERNS_GERMAN = [
|
|
|
1101
1104
|
"nur notwendige cookies",
|
|
1102
1105
|
"nur essenzielle cookies akzeptieren",
|
|
1103
1106
|
"nur notwendige cookies verwenden",
|
|
1107
|
+
"nur technisch notwendige",
|
|
1108
|
+
"nur essentielle cookies akzeptieren",
|
|
1104
1109
|
"alles ablehnen",
|
|
1105
1110
|
"nur notwendige",
|
|
1106
1111
|
"alle cookies ablehnen",
|
package/dist/autoconsent.esm.js
CHANGED
|
@@ -865,10 +865,10 @@ var REJECT_PATTERNS_ENGLISH = [
|
|
|
865
865
|
/^\s*(use|accept|allow|continue\s+with)?\s*only\s*(strictly)?\s*(necessary|essentials?|required)?\s*(cookies)?\s*$/is,
|
|
866
866
|
// e.g. "do not sell or share my personal information", "do not sell my personal information"
|
|
867
867
|
// often used in CCPA
|
|
868
|
-
/^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*information\s*$/is
|
|
868
|
+
/^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*information\s*$/is,
|
|
869
|
+
"allow selection",
|
|
870
|
+
"disagree and close"
|
|
869
871
|
// These are impactful, but look error-prone
|
|
870
|
-
// // e.g. "disagree"
|
|
871
|
-
// /^\s*(i)?\s*disagree\s*(and\s+close)?\s*$/i,
|
|
872
872
|
// // e.g. "i do not agree"
|
|
873
873
|
// /^\s*(i\s+)?do\s+not\s+agree\s*$/i,
|
|
874
874
|
];
|
|
@@ -882,6 +882,7 @@ var REJECT_PATTERNS_DUTCH = [
|
|
|
882
882
|
"alleen noodzakelijk",
|
|
883
883
|
"weiger",
|
|
884
884
|
"weiger cookies",
|
|
885
|
+
"selectie toestaan",
|
|
885
886
|
"doorgaan zonder te accepteren",
|
|
886
887
|
"alleen functionele cookies",
|
|
887
888
|
"alleen functioneel",
|
|
@@ -984,6 +985,8 @@ var REJECT_PATTERNS_FRENCH = [
|
|
|
984
985
|
"tout refuser",
|
|
985
986
|
"refuser",
|
|
986
987
|
"refuser tous les cookies",
|
|
988
|
+
"non merci",
|
|
989
|
+
"interdire tous les cookies",
|
|
987
990
|
"je refuse",
|
|
988
991
|
"refuser tout",
|
|
989
992
|
"tout rejeter",
|
|
@@ -1071,6 +1074,8 @@ var REJECT_PATTERNS_GERMAN = [
|
|
|
1071
1074
|
"nur notwendige cookies",
|
|
1072
1075
|
"nur essenzielle cookies akzeptieren",
|
|
1073
1076
|
"nur notwendige cookies verwenden",
|
|
1077
|
+
"nur technisch notwendige",
|
|
1078
|
+
"nur essentielle cookies akzeptieren",
|
|
1074
1079
|
"alles ablehnen",
|
|
1075
1080
|
"nur notwendige",
|
|
1076
1081
|
"alle cookies ablehnen",
|