@duckduckgo/autoconsent 16.43.1 → 16.44.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/lib/regional-testing/harness.mjs +536 -0
- package/.agents/skills/oxylabs-testing/SKILL.md +99 -0
- package/.agents/skills/oxylabs-testing/scripts/oxylabs.mjs +300 -0
- package/.agents/skills/oxylabs-testing/scripts/oxylabs.test.mjs +37 -0
- package/.agents/skills/proxy-testing/SKILL.md +5 -2
- package/.agents/skills/proxy-testing/scripts/regional-proxy.mjs +22 -471
- package/.claude/skills/oxylabs-testing/SKILL.md +6 -0
- package/.claude/skills/proxy-testing/SKILL.md +1 -1
- package/.claude/user-settings.json.TEMPLATE +2 -0
- package/AGENTS.md +10 -2
- package/CHANGELOG.md +47 -0
- package/data/coverage.json +2005 -1512
- package/dist/addon-firefox/background.bundle.js +9 -3
- package/dist/addon-firefox/compact-rules.json +1 -1
- package/dist/addon-firefox/manifest.json +1 -1
- package/dist/addon-firefox/popup.bundle.js +9 -3
- package/dist/addon-firefox/rule-index.json +1 -1
- package/dist/addon-firefox/rules.json +1 -1
- package/dist/addon-mv3/background.bundle.js +9 -3
- package/dist/addon-mv3/compact-rules.json +1 -1
- package/dist/addon-mv3/manifest.json +1 -1
- package/dist/addon-mv3/popup.bundle.js +9 -3
- package/dist/addon-mv3/rule-index.json +1 -1
- package/dist/addon-mv3/rules.json +1 -1
- package/dist/autoconsent.standalone.js +1 -1
- package/docs/rule-syntax.md +4 -4
- package/package.json +2 -2
- package/readme.md +1 -1
- package/rules/autoconsent/bandcamp.json +42 -3
- package/rules/autoconsent/chatgpt.json +2 -2
- package/rules/autoconsent/elsevier.json +69 -0
- package/rules/autoconsent/fides.json +10 -2
- package/rules/autoconsent/lacoccinelle.json +97 -0
- package/rules/autoconsent/woo-commerce-com.json +28 -11
- package/rules/compact-rules.json +1 -1
- package/rules/rule-index.json +1 -1
- package/rules/rules.json +1 -1
- package/tests/bandcamp.spec.ts +7 -1
- package/tests/elsevier.spec.ts +3 -0
- package/tests/fides.spec.ts +7 -1
- package/tests/lacoccinelle.spec.ts +3 -0
- package/tests/woo-commerce-com.spec.ts +4 -1
package/docs/rule-syntax.md
CHANGED
|
@@ -66,7 +66,7 @@ Returns true if the given selector matches one or more elements.
|
|
|
66
66
|
"check": "any" | "all" | "none"
|
|
67
67
|
}
|
|
68
68
|
```
|
|
69
|
-
Returns true if elements matched by ElementSelector are currently visible on the page. If `check` is `all` (default), every element must be visible. If `check` is `none`, no element should be visible. Visibility check is a CSS-based heuristic.
|
|
69
|
+
Returns true if elements matched by ElementSelector are currently visible on the page. If `check` is `all` (default), every element must be visible. If `check` is `none`, no element should be visible. Visibility check is a CSS-based heuristic. Prehiding does not affect this check.
|
|
70
70
|
|
|
71
71
|
## Wait for element
|
|
72
72
|
|
|
@@ -87,7 +87,7 @@ Waits until `selector` exists in the page. After `timeout` ms the step fails.
|
|
|
87
87
|
"check": "any" | "all" | "none"
|
|
88
88
|
}
|
|
89
89
|
```
|
|
90
|
-
Waits until element is visible in the page. After `timeout` ms the step fails.
|
|
90
|
+
Waits until element is visible in the page. After `timeout` ms the step fails. To wait until an element is not visible, use `check: "none"`, not `negated`.
|
|
91
91
|
|
|
92
92
|
## Click an element
|
|
93
93
|
```javascript
|
|
@@ -183,7 +183,7 @@ The optional `stylesheetId` is a marker used to prevent duplicate inserts when t
|
|
|
183
183
|
"cookieContains": "substring"
|
|
184
184
|
}
|
|
185
185
|
```
|
|
186
|
-
Checks if the substring is present in the document.cookie string.
|
|
186
|
+
Checks if the substring is present in the document.cookie string. `HttpOnly` cookies are not in this string.
|
|
187
187
|
|
|
188
188
|
## Eval
|
|
189
189
|
|
|
@@ -234,7 +234,7 @@ Evaluates a list of steps in order. If any return true (success), then the step
|
|
|
234
234
|
}
|
|
235
235
|
```
|
|
236
236
|
|
|
237
|
-
If `negated` is true, the result will be inverted. For example, `{ "exists": ".my-class", "negated": true }` will return true if `.my-class` does not exist.
|
|
237
|
+
If `negated` is true, the result will be inverted. For example, `{ "exists": ".my-class", "negated": true }` will return true if `.my-class` does not exist. The result is inverted after the step is complete, so `negated` does not change what a waiting step waits for.
|
|
238
238
|
|
|
239
239
|
## Optional actions
|
|
240
240
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duckduckgo/autoconsent",
|
|
3
|
-
"version": "16.
|
|
3
|
+
"version": "16.44.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"types": "./dist/types/web.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@duckduckgo/eslint-config": "github:duckduckgo/eslint-config#v0.3.0",
|
|
46
46
|
"@esm-bundle/chai": "^4.3.4-fix.0",
|
|
47
47
|
"@octokit/rest": "^22.0.1",
|
|
48
|
-
"@playwright/test": "^1.
|
|
48
|
+
"@playwright/test": "^1.63.0",
|
|
49
49
|
"@types/chai": "^5.0.0",
|
|
50
50
|
"@types/chrome": "^0.1.0",
|
|
51
51
|
"@types/mocha": "^10.0.1",
|
package/readme.md
CHANGED
|
@@ -88,7 +88,7 @@ For more details on rule types, see [Rule Syntax Reference](docs/rule-syntax.md)
|
|
|
88
88
|
The agent skills in `.agents/skills/` need a few environment variables.
|
|
89
89
|
|
|
90
90
|
1. Copy the contents from [`.claude/user-settings.json.TEMPLATE`](.claude/user-settings.json.TEMPLATE) to your user settings (e.g. `~/.claude/settings.json`), in addition to your existing config. If your settings already have an `env` block, merge the entries into it: a second `env` key would silently override the first.
|
|
91
|
-
2. Replace the values in the template. Proxy values must be complete URLs with an `http://`, `https://`, or `socks5://` scheme, and without embedded credentials. `REGIONAL_PROXY_USERNAME` and `REGIONAL_PROXY_PASSWORD` are optional: they are only used for `https://` proxies, so remove them if your proxies don't need authentication.
|
|
91
|
+
2. Replace the values in the template. Proxy values must be complete URLs with an `http://`, `https://`, or `socks5://` scheme, and without embedded credentials. `REGIONAL_PROXY_USERNAME` and `REGIONAL_PROXY_PASSWORD` are optional: they are only used for `https://` proxies, so remove them if your proxies don't need authentication. `OXYLABS_USER` and `OXYLABS_PASSWORD` are optional too: they enable the `oxylabs-testing` skill for sites that show our proxies a bot wall, so remove them if you don't have an Oxylabs account.
|
|
92
92
|
3. Start a new Claude Code session: settings are only read at startup.
|
|
93
93
|
|
|
94
94
|
For other agents or plain shell use, `export` the same variables in your shell profile instead.
|
|
@@ -10,20 +10,59 @@
|
|
|
10
10
|
],
|
|
11
11
|
"detectPopup": [
|
|
12
12
|
{
|
|
13
|
-
"
|
|
13
|
+
"any": [
|
|
14
|
+
{
|
|
15
|
+
"visible": ".cookie-dialog-initial"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"visible": ["menu-bar", ".cookie-dialog-initial"]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"visible": ["page-footer", ".cookie-dialog-initial"]
|
|
22
|
+
}
|
|
23
|
+
]
|
|
14
24
|
}
|
|
15
25
|
],
|
|
16
26
|
"optIn": [
|
|
17
27
|
{
|
|
18
|
-
"
|
|
28
|
+
"any": [
|
|
29
|
+
{
|
|
30
|
+
"waitForThenClick": ".cookie-dialog-initial button.g-button[type=\"submit\"]",
|
|
31
|
+
"timeout": 500
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"waitForThenClick": ["menu-bar", ".cookie-dialog-initial button.g-button[type=\"submit\"]"],
|
|
35
|
+
"timeout": 500
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"waitForThenClick": ["page-footer", ".cookie-dialog-initial button.g-button[type=\"submit\"]"],
|
|
39
|
+
"timeout": 500
|
|
40
|
+
}
|
|
41
|
+
]
|
|
19
42
|
}
|
|
20
43
|
],
|
|
21
44
|
"optOut": [
|
|
22
45
|
{
|
|
23
|
-
"
|
|
46
|
+
"any": [
|
|
47
|
+
{
|
|
48
|
+
"waitForThenClick": ".cookie-dialog-initial button.g-button.outline",
|
|
49
|
+
"timeout": 500
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"waitForThenClick": ["menu-bar", ".cookie-dialog-initial button.g-button.outline"],
|
|
53
|
+
"timeout": 500
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"waitForThenClick": ["page-footer", ".cookie-dialog-initial button.g-button.outline"],
|
|
57
|
+
"timeout": 500
|
|
58
|
+
}
|
|
59
|
+
]
|
|
24
60
|
}
|
|
25
61
|
],
|
|
26
62
|
"test": [
|
|
63
|
+
{
|
|
64
|
+
"wait": 500
|
|
65
|
+
},
|
|
27
66
|
{
|
|
28
67
|
"cookieContains": "cookie_preferences=%7B%22allow%22%3A%5B%5D"
|
|
29
68
|
}
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"frame": false,
|
|
10
10
|
"urlPattern": "^https?://(\\w+\\.)?chatgpt\\.com/"
|
|
11
11
|
},
|
|
12
|
-
"comment": "Covers the server-rendered consent dialog. Once the app hydrates it swaps in a banner whose buttons are only distinguishable by text, which the heuristic rule handles better.",
|
|
13
|
-
"prehideSelectors": ["dialog[data-cookie-consent]:has(form[data-privacy-consent-form])"],
|
|
12
|
+
"comment": "Covers the server-rendered consent dialog. Once the app hydrates it swaps in a banner whose buttons are only distinguishable by text, which the heuristic rule handles better. Below 48rem the site reopens the dialog with showModal() and marks it aria-modal, so prehiding it would leave the page inert with nothing visible to dismiss.",
|
|
13
|
+
"prehideSelectors": ["dialog[data-cookie-consent]:not([aria-modal]):has(form[data-privacy-consent-form])"],
|
|
14
14
|
"detectCmp": [
|
|
15
15
|
{
|
|
16
16
|
"exists": "dialog[data-cookie-consent] form[data-privacy-consent-form] input[name=\"action\"][value=\"reject\"]"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "elsevier",
|
|
3
|
+
"vendorUrl": "https://www.elsevier.com/",
|
|
4
|
+
"runContext": {
|
|
5
|
+
"urlPattern": "^https?://(\\w+\\.)?elsevier\\.(com|support)/"
|
|
6
|
+
},
|
|
7
|
+
"prehideSelectors": ["[data-testid=\"cookie-banner\"]"],
|
|
8
|
+
"detectCmp": [
|
|
9
|
+
{
|
|
10
|
+
"exists": "[data-testid=\"cookie-banner\"] [data-testid=\"cookie-settings-button\"]"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"detectPopup": [
|
|
14
|
+
{
|
|
15
|
+
"visible": "[data-testid=\"cookie-banner\"] [data-testid=\"cookie-settings-button\"]"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"optIn": [
|
|
19
|
+
{
|
|
20
|
+
"waitForThenClick": "[data-testid=\"accept-all-cookies-button\"]"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"optOut": [
|
|
24
|
+
{
|
|
25
|
+
"if": {
|
|
26
|
+
"exists": "[data-testid=\"reject-all-cookies-button\"]"
|
|
27
|
+
},
|
|
28
|
+
"then": [
|
|
29
|
+
{
|
|
30
|
+
"comment": "the banner is rendered before React attaches its click handler, so a lost first click needs a retry",
|
|
31
|
+
"waitForThenClick": "[data-testid=\"reject-all-cookies-button\"]",
|
|
32
|
+
"retry": 3,
|
|
33
|
+
"retryInterval": 1000
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"else": [
|
|
37
|
+
{
|
|
38
|
+
"comment": "no reject button on the banner: open the OneTrust preference center it controls and reject there",
|
|
39
|
+
"waitForThenClick": "[data-testid=\"cookie-settings-button\"]",
|
|
40
|
+
"retry": 3,
|
|
41
|
+
"retryInterval": 1000
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"waitForVisible": "#onetrust-pc-sdk",
|
|
45
|
+
"timeout": 10000
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"waitForThenClick": "#onetrust-pc-sdk input.category-switch-handler:checked",
|
|
49
|
+
"all": true,
|
|
50
|
+
"optional": true
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"waitForThenClick": "#onetrust-pc-sdk .save-preference-btn-handler",
|
|
54
|
+
"timeout": 5000
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"waitForVisible": "[data-testid=\"cookie-banner\"]",
|
|
60
|
+
"check": "none",
|
|
61
|
+
"timeout": 10000
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"test": [
|
|
65
|
+
{
|
|
66
|
+
"cookieContains": "OptanonAlertBoxClosed"
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
{
|
|
12
12
|
"visible": "#fides-overlay-wrapper #fides-banner"
|
|
13
13
|
},
|
|
14
|
+
{
|
|
15
|
+
"visible": "#fides-overlay-wrapper .fides-banner-hidden",
|
|
16
|
+
"check": "none"
|
|
17
|
+
},
|
|
14
18
|
{
|
|
15
19
|
"eval": "EVAL_FIDES_DETECT_POPUP"
|
|
16
20
|
}
|
|
@@ -30,7 +34,9 @@
|
|
|
30
34
|
},
|
|
31
35
|
"then": [
|
|
32
36
|
{
|
|
33
|
-
"
|
|
37
|
+
"waitForThenClick": ".fides-banner:not(.fides-banner-hidden) .fides-reject-all-button",
|
|
38
|
+
"retry": 5,
|
|
39
|
+
"retryInterval": 500
|
|
34
40
|
}
|
|
35
41
|
],
|
|
36
42
|
"else": [
|
|
@@ -38,7 +44,9 @@
|
|
|
38
44
|
"click": "#fides-banner .fides-manage-preferences-button"
|
|
39
45
|
},
|
|
40
46
|
{
|
|
41
|
-
"waitForThenClick": ".fides-reject-all-button"
|
|
47
|
+
"waitForThenClick": ".fides-modal-content .fides-reject-all-button",
|
|
48
|
+
"retry": 5,
|
|
49
|
+
"retryInterval": 500
|
|
42
50
|
}
|
|
43
51
|
]
|
|
44
52
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lacoccinelle",
|
|
3
|
+
"vendorUrl": "https://www.lacoccinelle.net/",
|
|
4
|
+
"runContext": {
|
|
5
|
+
"urlPattern": "^https?://(\\w+\\.)?lacoccinelle\\.net/"
|
|
6
|
+
},
|
|
7
|
+
"prehideSelectors": ["#qc-cmp2-container"],
|
|
8
|
+
"detectCmp": [
|
|
9
|
+
{
|
|
10
|
+
"exists": "#qc-cmp2-container"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"detectPopup": [
|
|
14
|
+
{
|
|
15
|
+
"visible": "#qc-cmp2-ui, #qc-cmp2-usp",
|
|
16
|
+
"check": "any"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"optIn": [
|
|
20
|
+
{
|
|
21
|
+
"if": {
|
|
22
|
+
"exists": "#qc-cmp2-usp"
|
|
23
|
+
},
|
|
24
|
+
"then": [
|
|
25
|
+
{
|
|
26
|
+
"waitForThenClick": "#qc-cmp2-usp .qc-usp-close-icon"
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"else": [
|
|
30
|
+
{
|
|
31
|
+
"waitForThenClick": "#accept-btn"
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"optOut": [
|
|
37
|
+
{
|
|
38
|
+
"if": {
|
|
39
|
+
"exists": "#qc-cmp2-usp"
|
|
40
|
+
},
|
|
41
|
+
"then": [
|
|
42
|
+
{
|
|
43
|
+
"waitForThenClick": "#qc-cmp2-usp .qc-cmp2-toggle-switch > button[aria-checked=\"false\"]",
|
|
44
|
+
"all": true,
|
|
45
|
+
"retry": 3
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"waitForThenClick": "#qc-cmp2-usp .qc-usp-ui-content button[mode=\"primary\"]"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"waitForThenClick": "#qc-cmp2-mspa-success-modal-button-footer-container button",
|
|
52
|
+
"timeout": 5000,
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"else": [
|
|
57
|
+
{
|
|
58
|
+
"comment": "the \"more options\" button is repurposed as a paid offer, the partner list is the only free reject path",
|
|
59
|
+
"waitForThenClick": "#qc-cmp2-container button.qc-cmp2-link-inline"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"waitForThenClick": "#reject-all-btn"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"comment": "outside the EEA the choice is not saved automatically",
|
|
66
|
+
"waitForVisible": "#qc-cmp2-ui",
|
|
67
|
+
"check": "none",
|
|
68
|
+
"timeout": 1500,
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"if": {
|
|
73
|
+
"visible": "#save-and-exit",
|
|
74
|
+
"check": "any"
|
|
75
|
+
},
|
|
76
|
+
"then": [
|
|
77
|
+
{
|
|
78
|
+
"click": "#save-and-exit",
|
|
79
|
+
"all": true
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"waitForVisible": "#qc-cmp2-ui",
|
|
83
|
+
"check": "none",
|
|
84
|
+
"timeout": 5000
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"test": [
|
|
92
|
+
{
|
|
93
|
+
"visible": "#qc-cmp2-ui, #qc-cmp2-usp",
|
|
94
|
+
"check": "none"
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}
|
|
@@ -1,19 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "woo-commerce-com",
|
|
3
|
+
"comment": "The banner renders its buttons disabled until it is ready, and clicks before that are ignored, so every click waits for :not([disabled]).",
|
|
3
4
|
"prehideSelectors": [".wccom-comp-privacy-banner .wccom-privacy-banner"],
|
|
4
5
|
"detectCmp": [{ "exists": ".wccom-comp-privacy-banner .wccom-privacy-banner" }],
|
|
5
|
-
"detectPopup": [{ "
|
|
6
|
-
"optIn": [{ "
|
|
6
|
+
"detectPopup": [{ "visible": ".wccom-comp-privacy-banner .wccom-privacy-banner" }],
|
|
7
|
+
"optIn": [{ "waitForThenClick": ".wccom-privacy-banner__content-buttons button.is-style-primary:not([disabled])" }],
|
|
7
8
|
"optOut": [
|
|
8
9
|
{
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
10
|
+
"comment": "'Reject all' is only offered in some regions: it is the secondary button followed by another secondary ('Customize')",
|
|
11
|
+
"if": { "exists": ".wccom-privacy-banner__content-buttons button.is-style-secondary:has(+ button.is-style-secondary)" },
|
|
12
|
+
"then": [
|
|
13
|
+
{
|
|
14
|
+
"waitForThenClick": ".wccom-privacy-banner__content-buttons button.is-style-secondary:not([disabled]):has(+ button.is-style-secondary)"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"else": [
|
|
18
|
+
{
|
|
19
|
+
"waitForThenClick": ".wccom-privacy-banner__content-buttons button.is-style-secondary:not([disabled]):has(+ button.is-style-primary)"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"waitForVisible": ".wccom-modal__footer button.is-style-primary"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"click": ".wccom-customize-privacy-checkbox input[type=checkbox]:checked:not([disabled])",
|
|
26
|
+
"all": true,
|
|
27
|
+
"optional": true
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"waitForThenClick": ".wccom-modal__footer button.is-style-primary:not([disabled])"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
17
33
|
}
|
|
18
|
-
]
|
|
34
|
+
],
|
|
35
|
+
"test": [{ "cookieContains": "wccom_cookie_consent_analytics=denied" }]
|
|
19
36
|
}
|