barebrowse 0.15.0 → 0.17.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/CHANGELOG.md CHANGED
@@ -1,5 +1,109 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.17.0] - 2026-07-10
4
+
5
+ ### Added
6
+
7
+ - **Firefox/BiDi observability parity (parity plan Phase 2).** The Firefox
8
+ engine now backs the same console/network capture and network-idle wait the
9
+ Chromium/CDP engine has, over WebDriver BiDi events instead of CDP:
10
+ - **`page.waitForNetworkIdle()`** works on Firefox (was a CDP-only stub that
11
+ threw). Wired to `network.beforeRequestSent` / `responseCompleted` /
12
+ `fetchError`, reusing the same orphan-resilient idle core as the CDP path
13
+ (`waitForNetworkIdleBiDi` in `network-idle.js`).
14
+ - **Daemon console + network log capture** on Firefox: the `console-logs`,
15
+ `network-log`, and `wait-idle` CLI commands now return real data for a
16
+ `--engine firefox` session (previously empty / unsupported). Backed by
17
+ `log.entryAdded` + `network.*` events (`attachBiDiCapture` in `daemon.js`),
18
+ mirroring the CDP `Runtime.consoleAPICalled` / `Network.*` capture. BiDi's
19
+ `warn` console level is normalized to CDP's `warning` so `--level` filters
20
+ match cross-engine.
21
+
22
+ Event shapes were measured against real Firefox before wiring (per the Phase 1
23
+ lesson). Unit-tested against those shapes in `test/unit/network-idle.test.js`
24
+ and `test/unit/daemon.test.js`; live-verified in `test/integration/firefox.test.js`.
25
+ Still Firefox gaps (Phase 3+): ad/tracker block, dialog capture, `saveState`,
26
+ `waitForNavigation`, hybrid mode.
27
+
28
+ ## [0.16.1] - 2026-07-10
29
+
30
+ ### Fixed
31
+
32
+ - **Firefox consent auto-dismiss no longer risks clicking an unrelated button.**
33
+ A code review of v0.16.0 found (and reproduced) a false-positive: when a
34
+ consent *dialog* was detected but contained no in-dialog accept button, the
35
+ Firefox walker fell back to a page-wide "accept" scan that could auto-click an
36
+ unrelated `Accept all …` control elsewhere on the page (e.g. a ToS/signup
37
+ button) during `goto()`. The page-wide scan now runs **only** for banner-style
38
+ consent (no dialog container at all). Trade-off: an accept button rendered
39
+ *outside* its own dialog (some SourcePoint deployments) is no longer
40
+ auto-dismissed on Firefox — we accept that miss rather than risk a wrong
41
+ click. Regression-tested in `test/unit/consent-firefox.test.js`.
42
+
43
+ ### Documentation
44
+
45
+ - **Firefox stealth + consent known limitations** captured in the PRD
46
+ (`docs/01-product/prd.md`), slated for the Phase 5 cross-engine fidelity
47
+ harness: the consent double AX-build per navigate (bounded latency, parity
48
+ with CDP), the single-click-without-re-verify behavior (`consent: false` to
49
+ opt out), and the latent `navigator.webdriver` own-property fallback. All
50
+ three were validated in the same review; none is a correctness defect on
51
+ current engines.
52
+
53
+ ## [0.16.0] - 2026-07-10
54
+
55
+ ### Added
56
+
57
+ - **Firefox anti-detection + consent auto-dismiss (BiDi parity Phase 1).** The
58
+ two most agent-breaking Firefox gaps are closed: headless bot-detection and
59
+ cookie-consent dialogs that were never dismissed. Both now work on
60
+ `connect({ engine: 'firefox' })` (and therefore the CLI/daemon/MCP Firefox
61
+ paths), behind the same flags as the CDP engine.
62
+ - **Stealth** (`src/stealth-firefox.js`, headless only, via BiDi
63
+ `script.addPreloadScript`). Deliberately *not* a port of Chromium's
64
+ `STEALTH_SCRIPT`: a POC measured stock Firefox-under-BiDi and found it
65
+ already exposes a realistic surface (no `window.chrome`, a real plugin set,
66
+ the real GPU, a UA with no "Headless" marker) — injecting Chromium's
67
+ Chrome-shims would have *created* a spoof tell worse than the one removed.
68
+ Firefox's only real tell is `navigator.webdriver` (`true`), so it gets a
69
+ minimal script: `navigator.webdriver` hiding + canvas-fingerprint noise.
70
+ `WEBDRIVER_PATCH` and `CANVAS_NOISE_PATCH` are now exported from
71
+ `stealth.js` and shared by both engines (single-sourcing the canvas
72
+ double-XOR fix).
73
+ - **Hardened `navigator.webdriver` hiding on both engines.** The shared
74
+ `WEBDRIVER_PATCH` now *deletes* the getter off `Navigator.prototype` instead
75
+ of shadowing it with an own property. A naive override hid the value but left
76
+ `navigator.hasOwnProperty('webdriver') === true` — a tell that advanced
77
+ anti-bot checks (e.g. sannysoft's "WebDriver New") detect. After the change
78
+ `navigator.webdriver` is `undefined` and both `hasOwnProperty` and
79
+ `'webdriver' in navigator` read `false`, matching a stock browser
80
+ (POC-verified on Chromium and Firefox). This improves the Chromium engine
81
+ too, not just Firefox.
82
+ - **Consent** (`src/consent-firefox.js`). A walker over the reconstructed
83
+ nested AX tree that clicks the "accept" button via the existing BiDi
84
+ `pointerClick`. The multilingual accept/dialog patterns were extracted to
85
+ `src/consent-patterns.js`, now imported by both the CDP (`consent.js`) and
86
+ BiDi walkers, so language coverage stays single-sourced. Runs after
87
+ `goto()` behind the same `consent !== false` flag as CDP.
88
+ - **Tests:** `test/unit/consent-firefox.test.js` (pure walker: dialog
89
+ scoping, pattern priority, non-English, banner fallback, false-positive
90
+ guard) plus Firefox integration tests (webdriver hidden at page-parse time,
91
+ no `window.chrome` spoof, consent auto-dismiss + a `consent: false` control
92
+ that proves the test can fail).
93
+
94
+ ### Known gaps (Firefox engine)
95
+
96
+ - `hybrid` mode, ad/tracker blocking, and console/network log capture remain
97
+ Chromium-only (BiDi parity Phases 2–4). `saveState`, `waitForNavigation`,
98
+ and `waitForNetworkIdle` still throw a clear "not supported on the
99
+ Firefox/BiDi engine" error; download/dialog logs read empty.
100
+ - `reload()` still cannot honour `ignoreCache` (Firefox BiDi does not support
101
+ it yet).
102
+ - Consent auto-dismiss clicks once without re-verifying dismissal (the CDP
103
+ path's synthetic→real retry is unnecessary here — BiDi `pointerClick` is
104
+ already a real pointer event). Accessible-name computation remains a
105
+ high-value subset of the full W3C accname spec.
106
+
3
107
  ## [0.15.0] - 2026-07-10
4
108
 
5
109
  ### Added
@@ -23,7 +127,13 @@
23
127
  parity with CDP.
24
128
  - Fidelity validated against real CDP snapshots; iframes (incl. nested +
25
129
  multi-tab), shadow DOM, CSP, and SPA timing covered in
26
- `test/integration/firefox.test.js` (15 tests).
130
+ `test/integration/firefox.test.js` (18 tests).
131
+ - `goto`/`reload`/history navigation honour a timeout (a page that never
132
+ finishes loading rejects instead of hanging). Proxy honours the URL scheme
133
+ (`http`/`https` → HTTP+SSL, `socks`/`socks5`/`socks4` → SOCKS). Cookie
134
+ injection is host-scoped like CDP (shared `scopedCookiesForUrl`, not the
135
+ whole jar). Snapshot fidelity: a collapsed `<select>` surfaces as its value
136
+ (not every `<option>`), and bare text directly under `<body>` is kept.
27
137
 
28
138
  - **Incognito mode — a clean, unauthenticated session (`incognito: true`).**
29
139
  Skips ALL auth injection: no cookie extraction/injection and no
package/README.md CHANGED
@@ -145,10 +145,27 @@ const page = await connect({ engine: 'firefox' }); // headless by default
145
145
 
146
146
  From the CLI: `barebrowse open <url> --engine firefox`. From MCP: set
147
147
  `BAREBROWSE_ENGINE=firefox`. Firefox cookies (plaintext) reuse into the same
148
- engine. Chromium (CDP) remains the default; `hybrid` mode is Chromium-only.
148
+ engine. Consent auto-dismiss and headless stealth work on Firefox too (as of
149
+ v0.16.0). Chromium (CDP) remains the default; `hybrid` mode, ad/tracker
150
+ blocking, and the daemon's console/network capture are still Chromium-only.
149
151
 
150
152
  No clone profile, no fresh cookies — the agent sees what you see.
151
153
 
154
+ ### Incognito (clean session)
155
+
156
+ Pass `incognito: true` for a clean, **unauthenticated** session — barebrowse
157
+ skips *all* auth injection (cookies + storage state), so the agent browses
158
+ logged out. It is not Chrome's `--incognito` flag: the session already runs in
159
+ a throwaway profile, so this gates the *other* auth source — your real browser
160
+ cookies. Works on both engines:
161
+
162
+ ```js
163
+ const page = await connect({ incognito: true });
164
+ ```
165
+
166
+ From the CLI: `barebrowse open <url> --incognito`. From MCP: set
167
+ `BAREBROWSE_INCOGNITO=1`.
168
+
152
169
  ## What it handles automatically
153
170
 
154
171
  Cookie consent walls (29 languages, with real mouse click fallback for stubborn CMPs), login walls (cookie extraction from your browsers), bot detection (ARIA node count heuristic + stealth patches + automatic headed fallback — snapshot shows `[BOT CHALLENGE DETECTED]` warning when blocked), permission prompts, SPA navigation, JS dialogs, off-screen elements, pre-filled inputs, ARIA noise, and profile locking. The agent doesn't think about any of it.
@@ -95,7 +95,7 @@ const snapshot = await browse('https://example.com', {
95
95
  | `close()` | -- | void | Close page, disconnect CDP, kill browser (if headless) |
96
96
 
97
97
  **connect() options** (in addition to mode/port/consent):
98
- - `engine: 'chromium'|'firefox'` — Browser engine. Default `chromium` (CDP). `firefox` drives Firefox over WebDriver BiDi (CDP is deprecated there) — a separate transport with the same `page.*` API; the ARIA snapshot is reconstructed in-page since BiDi has no `getFullAXTree`. Chromium-only: `hybrid` mode, consent auto-dismiss, stealth, and the CLI daemon's console/network capture. `reload()` ignores `ignoreCache` on Firefox (BiDi limitation).
98
+ - `engine: 'chromium'|'firefox'` — Browser engine. Default `chromium` (CDP). `firefox` drives Firefox over WebDriver BiDi (CDP is deprecated there) — a separate transport with the same `page.*` API; the ARIA snapshot is reconstructed in-page since BiDi has no `getFullAXTree`. Consent auto-dismiss and stealth (headless anti-detection) work on Firefox as of v0.16.0; the CLI daemon's console/network capture and `waitForNetworkIdle` work as of v0.17.0. Still Chromium-only: `hybrid` mode and ad/tracker blocking. `reload()` ignores `ignoreCache` on Firefox (BiDi limitation).
99
99
  - `port: 9222` — Attach to a Chromium already running with `--remote-debugging-port=N` instead of spawning one. The browser keeps running on `close()`. Stealth + permission denial + download capture are skipped to avoid mutating the user's running browser.
100
100
  - `proxy: 'http://...'` — HTTP/SOCKS proxy for browser
101
101
  - `viewport: '1280x720'` — Set viewport dimensions
@@ -189,8 +189,8 @@ barebrowse can inject cookies from the user's real browser sessions, bypassing l
189
189
 
190
190
  | Obstacle | How | Mode |
191
191
  |---|---|---|
192
- | Cookie consent | ARIA scan + jsClick accept button, 29 languages | Both |
193
- | Consent behind iframes | JS `.click()` via DOM.resolveNode bypasses overlays, real mouse click fallback for CMPs that ignore synthetic clicks | Both |
192
+ | Cookie consent | ARIA scan for the accept button, 29 languages. CDP: `jsClick` (overlay-bypassing) then a real-mouse-click fallback for CMPs that ignore synthetic clicks. Firefox/BiDi: a single real pointer click. A page-wide accept scan runs only for banner-style consent (no dialog) — when a dialog is detected but has no in-dialog accept button, no page-wide scan runs, to avoid clicking an unrelated `Accept all …` control | Both engines |
193
+ | Consent behind iframes | Scanned/clicked in the owning frame (CDP `DOM.resolveNode`; Firefox resolves the ref in its browsing context) | Both engines |
194
194
  | Permission prompts | Launch flags + CDP Browser.setPermission auto-deny | Both |
195
195
  | Media autoplay blocked | `--autoplay-policy=no-user-gesture-required` | Both |
196
196
  | Login walls | Cookie extraction from Firefox/Chromium + CDP injection | Both |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "barebrowse",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "description": "Authenticated web browsing for autonomous agents via CDP. URL in, pruned ARIA snapshot out.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,125 @@
1
+ /**
2
+ * consent-firefox.js — Auto-dismiss cookie consent dialogs on the Firefox/BiDi
3
+ * engine, by walking the reconstructed AX tree (ax-snapshot.js).
4
+ *
5
+ * The CDP walker (consent.js) can't be reused verbatim: it consumes CDP's flat
6
+ * getFullAXTree (nodes[] with parentId / role.value / backendDOMNodeId) and
7
+ * clicks via DOM.resolveNode + Input.dispatchMouseEvent. The Firefox tree is a
8
+ * DIFFERENT shape — nested `children`, string `role`/`name`, and each node's
9
+ * `nodeId` IS its ref (stamped as data-bb-ref) — and clicks go through
10
+ * input.performActions. So this is a parallel walker sharing only the language
11
+ * patterns (consent-patterns.js).
12
+ *
13
+ * It's written as a PURE function over (root tree, click(ref)) so it can be
14
+ * unit-tested against fixture trees with no browser: the caller (firefox-page)
15
+ * injects a real click that routes ref → pointerClick. The nested tree also
16
+ * makes "descendant of dialog" a plain subtree walk — no parentMap needed.
17
+ */
18
+
19
+ import { ACCEPT_PATTERNS, DIALOG_ROLES, CONSENT_DIALOG_HINTS } from './consent-patterns.js';
20
+
21
+ /** Roles whose text confirms a container is a consent dialog. */
22
+ const CONSENT_TEXT_ROLES = new Set(['heading', 'StaticText', 'generic']);
23
+
24
+ /** Depth-first walk yielding every node in the subtree rooted at `node`. */
25
+ function* walk(node) {
26
+ if (!node) return;
27
+ yield node;
28
+ for (const child of node.children || []) yield* walk(child);
29
+ }
30
+
31
+ /** True if any node in this subtree carries consent-hint text. */
32
+ function hasConsentContent(dialog) {
33
+ for (const node of walk(dialog)) {
34
+ if (node === dialog) continue;
35
+ if (CONSENT_TEXT_ROLES.has(node.role) && CONSENT_DIALOG_HINTS.some((p) => p.test(node.name || ''))) {
36
+ return true;
37
+ }
38
+ }
39
+ return false;
40
+ }
41
+
42
+ /**
43
+ * Find the best "accept" button inside a dialog subtree, honouring
44
+ * ACCEPT_PATTERNS priority (most specific first).
45
+ */
46
+ function findAcceptButton(dialog) {
47
+ for (const pattern of ACCEPT_PATTERNS) {
48
+ for (const node of walk(dialog)) {
49
+ if (node.role === 'button' && node.name && pattern.test(node.name)) return node;
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+
55
+ /**
56
+ * Fallback when no consent dialog container is found: scan the whole tree for a
57
+ * button matching a STRONG (multi-word) accept pattern. Excludes the bare
58
+ * ^accept$/^agree$/^ok$ fallbacks (last 3) so we don't false-match unrelated
59
+ * page buttons — mirrors consent.js's tryGlobalConsentButton.
60
+ */
61
+ function findGlobalAcceptButton(root) {
62
+ const safePatterns = ACCEPT_PATTERNS.slice(0, -3);
63
+ for (const pattern of safePatterns) {
64
+ for (const node of walk(root)) {
65
+ if (node.role === 'button' && node.name && pattern.test(node.name)) return node;
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+
71
+ /**
72
+ * Try to dismiss a cookie consent dialog in a reconstructed Firefox AX tree.
73
+ *
74
+ * @param {object} root - Spliced AX tree root (from firefox-page's buildTree).
75
+ * @param {(ref: string) => Promise<void>} click - Clicks the element for a ref
76
+ * (firefox-page injects one that routes to pointerClick / performActions).
77
+ * @returns {Promise<boolean>} true if an accept button was found and clicked.
78
+ */
79
+ export async function dismissConsentFirefox(root, click) {
80
+ if (!root) return false;
81
+
82
+ // Find dialog containers that look like consent dialogs.
83
+ const consentDialogs = [];
84
+ for (const node of walk(root)) {
85
+ if (!DIALOG_ROLES.has(node.role)) continue;
86
+ const name = node.name || '';
87
+ if (CONSENT_DIALOG_HINTS.some((p) => p.test(name)) || hasConsentContent(node)) {
88
+ consentDialogs.push(node);
89
+ }
90
+ }
91
+
92
+ // Accept button inside a consent dialog (preferred — scoped, low false-positive).
93
+ for (const dialog of consentDialogs) {
94
+ const button = findAcceptButton(dialog);
95
+ if (button?.nodeId) {
96
+ try {
97
+ await click(button.nodeId);
98
+ return true;
99
+ } catch {
100
+ // Click failed (stale ref / navigated) — try the next dialog.
101
+ }
102
+ }
103
+ }
104
+
105
+ // Banner-style consent (no dialog container at all): scan the page for a
106
+ // strong accept button. We deliberately DO NOT run this page-wide scan when a
107
+ // consent dialog WAS detected but had no in-scope accept button — a page-wide
108
+ // match there can click an UNRELATED "Accept all …" control elsewhere (e.g. a
109
+ // ToS/signup button), an automatic wrong mutation on goto(). The trade-off is
110
+ // losing the rare "accept button rendered outside its own dialog" pattern; we
111
+ // accept that miss rather than risk a wrong click. See the PRD "known
112
+ // limitations" note for the validated false-positive this guards against.
113
+ if (consentDialogs.length === 0) {
114
+ const global = findGlobalAcceptButton(root);
115
+ if (global?.nodeId) {
116
+ try {
117
+ await click(global.nodeId);
118
+ return true;
119
+ } catch {
120
+ return false;
121
+ }
122
+ }
123
+ }
124
+ return false;
125
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * consent-patterns.js — Shared cookie-consent detection data.
3
+ *
4
+ * The engine-agnostic half of consent handling: the multilingual regex sets
5
+ * and dialog roles used to recognise a consent dialog and its "accept" button.
6
+ * Both the CDP walker (consent.js) and the BiDi walker (consent-firefox.js)
7
+ * import these so the language coverage is single-sourced — a new language or
8
+ * pattern is added once and both engines get it.
9
+ */
10
+
11
+ // Button text patterns that mean "accept all" / "I agree" across common languages.
12
+ // Order matters: more specific patterns first to avoid false positives.
13
+ export const ACCEPT_PATTERNS = [
14
+ // English
15
+ /\baccept\s*all\b/i,
16
+ /\ballow\s*all\b/i,
17
+ /\bagree\s*to\s*all\b/i,
18
+ /\byes,?\s*i\s*agree\b/i,
19
+ /\bi\s*agree\b/i,
20
+ /\baccept\s*cookies?\b/i,
21
+ /\ballow\s*cookies?\b/i,
22
+ /\bgot\s*it\b/i,
23
+ // Dutch
24
+ /\balles\s*accepteren\b/i,
25
+ /\balles\s*toestaan\b/i,
26
+ /\baccepteren\b/i,
27
+ /\bakkoord\b/i,
28
+ // German
29
+ /\balle\s*akzeptieren\b/i,
30
+ /\ballem\s*zustimmen\b/i,
31
+ /\balle\s*cookies?\s*akzeptieren\b/i,
32
+ // French
33
+ /\btout\s*accepter\b/i,
34
+ /\baccepter\s*tout\b/i,
35
+ /\bj['']accepte\b/i,
36
+ // Spanish
37
+ /\baceptar\s*todo\b/i,
38
+ /\baceptar\s*todas?\b/i,
39
+ // Italian
40
+ /\baccetta\s*tutto\b/i,
41
+ /\baccetto\b/i,
42
+ // Portuguese
43
+ /\baceitar\s*tudo\b/i,
44
+ // Russian
45
+ /принять\s*все/i,
46
+ /принять/i,
47
+ /согласен/i,
48
+ // Ukrainian
49
+ /прийняти\s*все/i,
50
+ /прийняти/i,
51
+ // Polish
52
+ /zaakceptuj\s*wszystk/i,
53
+ /akceptuj\s*wszystk/i,
54
+ /zgadzam\s*się/i,
55
+ // Czech
56
+ /přijmout\s*vše/i,
57
+ /souhlasím/i,
58
+ // Turkish
59
+ /tümünü\s*kabul\s*et/i,
60
+ /kabul\s*et/i,
61
+ /kabul\s*ediyorum/i,
62
+ // Romanian
63
+ /acceptă\s*tot/i,
64
+ /accept\s*toate/i,
65
+ // Hungarian
66
+ /összes\s*elfogadás/i,
67
+ /elfogad/i,
68
+ // Greek
69
+ /αποδοχή\s*όλων/i,
70
+ /αποδέχομαι/i,
71
+ // Swedish
72
+ /acceptera\s*alla/i,
73
+ /godkänn\s*alla/i,
74
+ // Danish
75
+ /accepter\s*alle/i,
76
+ /acceptér\s*alle/i,
77
+ // Norwegian
78
+ /godta\s*alle/i,
79
+ /aksepter\s*alle/i,
80
+ // Finnish
81
+ /hyväksy\s*kaikki/i,
82
+ /hyväksyn/i,
83
+ // Arabic
84
+ /قبول\s*الكل/,
85
+ /قبول\s*الجميع/,
86
+ /موافق/,
87
+ /قبول/,
88
+ // Persian
89
+ /پذیرش\s*همه/,
90
+ /موافقم/,
91
+ /پذیرش/,
92
+ // Chinese (Simplified + Traditional)
93
+ /全部接受/,
94
+ /接受所有/,
95
+ /接受全部/,
96
+ /同意并继续/,
97
+ /全部接受/,
98
+ /接受/,
99
+ /同意/,
100
+ // Japanese
101
+ /すべて受け入れ/,
102
+ /すべて許可/,
103
+ /同意する/,
104
+ /同意します/,
105
+ // Korean
106
+ /모두\s*수락/,
107
+ /모두\s*동의/,
108
+ /동의합니다/,
109
+ /수락/,
110
+ // Vietnamese
111
+ /chấp\s*nhận\s*tất\s*cả/i,
112
+ /đồng\s*ý\s*tất\s*cả/i,
113
+ /đồng\s*ý/i,
114
+ // Thai
115
+ /ยอมรับทั้งหมด/,
116
+ /ยอมรับ/,
117
+ // Hindi
118
+ /सभी\s*स्वीकार/,
119
+ /स्वीकार\s*करें/,
120
+ /सहमत/,
121
+ // Indonesian / Malay
122
+ /terima\s*semua/i,
123
+ /setuju/i,
124
+ // Generic single-word fallbacks (only matched inside dialogs)
125
+ /^accept$/i,
126
+ /^agree$/i,
127
+ /^ok$/i,
128
+ ];
129
+
130
+ // Roles that indicate a consent dialog container.
131
+ export const DIALOG_ROLES = new Set(['dialog', 'alertdialog']);
132
+
133
+ // Text patterns in dialog names/headings that confirm it's about consent.
134
+ export const CONSENT_DIALOG_HINTS = [
135
+ /cookie/i,
136
+ /consent/i,
137
+ /privacy/i,
138
+ /before\s*you\s*continue/i,
139
+ /voordat\s*je\s*verdergaat/i, // Dutch
140
+ /bevor\s*du\s*fortf/i, // German
141
+ /avant\s*de\s*continuer/i, // French
142
+ /antes\s*de\s*continuar/i, // Spanish / Portuguese
143
+ /prima\s*di\s*continuare/i, // Italian
144
+ /zanim\s*przejdziesz/i, // Polish
145
+ /прежде\s*чем\s*продолжить/i, // Russian
146
+ /devam\s*etmeden\s*önce/i, // Turkish
147
+ /続行する前に/, // Japanese
148
+ /继续前/, // Chinese Simplified
149
+ /繼續前/, // Chinese Traditional
150
+ /계속하기\s*전에/, // Korean
151
+ /trước\s*khi\s*tiếp\s*tục/i, // Vietnamese
152
+ /ملفات\s*تعريف\s*الارتباط/, // Arabic: cookies
153
+ /คุกกี้/, // Thai: cookies
154
+ ];
package/src/consent.js CHANGED
@@ -4,152 +4,13 @@
4
4
  * Scans the page ARIA tree for consent dialogs and clicks the "accept" button.
5
5
  * Works across languages by matching common accept/agree patterns.
6
6
  * Runs once after page load — no polling, no mutation observers.
7
+ *
8
+ * The multilingual pattern sets live in consent-patterns.js (shared with the
9
+ * Firefox/BiDi walker in consent-firefox.js); this file owns the CDP-specific
10
+ * tree walk + click.
7
11
  */
8
12
 
9
- // Button text patterns that mean "accept all" / "I agree" across common languages.
10
- // Order matters: more specific patterns first to avoid false positives.
11
- const ACCEPT_PATTERNS = [
12
- // English
13
- /\baccept\s*all\b/i,
14
- /\ballow\s*all\b/i,
15
- /\bagree\s*to\s*all\b/i,
16
- /\byes,?\s*i\s*agree\b/i,
17
- /\bi\s*agree\b/i,
18
- /\baccept\s*cookies?\b/i,
19
- /\ballow\s*cookies?\b/i,
20
- /\bgot\s*it\b/i,
21
- // Dutch
22
- /\balles\s*accepteren\b/i,
23
- /\balles\s*toestaan\b/i,
24
- /\baccepteren\b/i,
25
- /\bakkoord\b/i,
26
- // German
27
- /\balle\s*akzeptieren\b/i,
28
- /\ballem\s*zustimmen\b/i,
29
- /\balle\s*cookies?\s*akzeptieren\b/i,
30
- // French
31
- /\btout\s*accepter\b/i,
32
- /\baccepter\s*tout\b/i,
33
- /\bj['']accepte\b/i,
34
- // Spanish
35
- /\baceptar\s*todo\b/i,
36
- /\baceptar\s*todas?\b/i,
37
- // Italian
38
- /\baccetta\s*tutto\b/i,
39
- /\baccetto\b/i,
40
- // Portuguese
41
- /\baceitar\s*tudo\b/i,
42
- // Russian
43
- /принять\s*все/i,
44
- /принять/i,
45
- /согласен/i,
46
- // Ukrainian
47
- /прийняти\s*все/i,
48
- /прийняти/i,
49
- // Polish
50
- /zaakceptuj\s*wszystk/i,
51
- /akceptuj\s*wszystk/i,
52
- /zgadzam\s*się/i,
53
- // Czech
54
- /přijmout\s*vše/i,
55
- /souhlasím/i,
56
- // Turkish
57
- /tümünü\s*kabul\s*et/i,
58
- /kabul\s*et/i,
59
- /kabul\s*ediyorum/i,
60
- // Romanian
61
- /acceptă\s*tot/i,
62
- /accept\s*toate/i,
63
- // Hungarian
64
- /összes\s*elfogadás/i,
65
- /elfogad/i,
66
- // Greek
67
- /αποδοχή\s*όλων/i,
68
- /αποδέχομαι/i,
69
- // Swedish
70
- /acceptera\s*alla/i,
71
- /godkänn\s*alla/i,
72
- // Danish
73
- /accepter\s*alle/i,
74
- /acceptér\s*alle/i,
75
- // Norwegian
76
- /godta\s*alle/i,
77
- /aksepter\s*alle/i,
78
- // Finnish
79
- /hyväksy\s*kaikki/i,
80
- /hyväksyn/i,
81
- // Arabic
82
- /قبول\s*الكل/,
83
- /قبول\s*الجميع/,
84
- /موافق/,
85
- /قبول/,
86
- // Persian
87
- /پذیرش\s*همه/,
88
- /موافقم/,
89
- /پذیرش/,
90
- // Chinese (Simplified + Traditional)
91
- /全部接受/,
92
- /接受所有/,
93
- /接受全部/,
94
- /同意并继续/,
95
- /全部接受/,
96
- /接受/,
97
- /同意/,
98
- // Japanese
99
- /すべて受け入れ/,
100
- /すべて許可/,
101
- /同意する/,
102
- /同意します/,
103
- // Korean
104
- /모두\s*수락/,
105
- /모두\s*동의/,
106
- /동의합니다/,
107
- /수락/,
108
- // Vietnamese
109
- /chấp\s*nhận\s*tất\s*cả/i,
110
- /đồng\s*ý\s*tất\s*cả/i,
111
- /đồng\s*ý/i,
112
- // Thai
113
- /ยอมรับทั้งหมด/,
114
- /ยอมรับ/,
115
- // Hindi
116
- /सभी\s*स्वीकार/,
117
- /स्वीकार\s*करें/,
118
- /सहमत/,
119
- // Indonesian / Malay
120
- /terima\s*semua/i,
121
- /setuju/i,
122
- // Generic single-word fallbacks (only matched inside dialogs)
123
- /^accept$/i,
124
- /^agree$/i,
125
- /^ok$/i,
126
- ];
127
-
128
- // Roles that indicate a consent dialog container.
129
- const DIALOG_ROLES = new Set(['dialog', 'alertdialog']);
130
-
131
- // Text patterns in dialog names/headings that confirm it's about consent.
132
- const CONSENT_DIALOG_HINTS = [
133
- /cookie/i,
134
- /consent/i,
135
- /privacy/i,
136
- /before\s*you\s*continue/i,
137
- /voordat\s*je\s*verdergaat/i, // Dutch
138
- /bevor\s*du\s*fortf/i, // German
139
- /avant\s*de\s*continuer/i, // French
140
- /antes\s*de\s*continuar/i, // Spanish / Portuguese
141
- /prima\s*di\s*continuare/i, // Italian
142
- /zanim\s*przejdziesz/i, // Polish
143
- /прежде\s*чем\s*продолжить/i, // Russian
144
- /devam\s*etmeden\s*önce/i, // Turkish
145
- /続行する前に/, // Japanese
146
- /继续前/, // Chinese Simplified
147
- /繼續前/, // Chinese Traditional
148
- /계속하기\s*전에/, // Korean
149
- /trước\s*khi\s*tiếp\s*tục/i, // Vietnamese
150
- /ملفات\s*تعريف\s*الارتباط/, // Arabic: cookies
151
- /คุกกี้/, // Thai: cookies
152
- ];
13
+ import { ACCEPT_PATTERNS, DIALOG_ROLES, CONSENT_DIALOG_HINTS } from './consent-patterns.js';
153
14
 
154
15
  /**
155
16
  * Click a node via JavaScript .click() instead of mouse events.