fix(captcha): detect Cloudflare challenge pages - #2723
Conversation
|
@alectimison-maker is attempting to deploy a commit to the esokullu's projects Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Pull request overview
Adds browser-level Cloudflare Challenge Page detection and routes detected interstitials through the manual CAPTCHA gate.
Changes:
- Detects
cf-mitigated: challengeon top-level responses. - Persists sanitized challenge state across worker restarts.
- Adds symmetric Chrome/Firefox integration and regression tests.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
test/run.js |
Tests detection, persistence, gating, cleanup, and parity. |
src/chrome/src/background.js |
Wires Chrome network and navigation listeners. |
src/firefox/src/background.js |
Wires Firefox network and navigation listeners. |
src/chrome/src/agent/cloudflare-managed-challenge.js |
Implements Chrome challenge state transitions. |
src/firefox/src/agent/cloudflare-managed-challenge.js |
Implements Firefox challenge state transitions. |
src/chrome/src/agent/agent.js |
Integrates persistence and manual gating in Chrome. |
src/firefox/src/agent/agent.js |
Integrates persistence and manual gating in Firefox. |
Suppressed comments (2)
src/chrome/src/agent/agent.js:7967
- The Cloudflare gate is also persisted inside the conversation snapshot, but clearing the dedicated signal does not rewrite that snapshot. After a normal response clears the live gate and a later worker restart finds no dedicated signal, this line restores the stale conversation copy as an active Cloudflare gate; future normal responses cannot clear it because
_cloudflareManagedChallengesis empty. Treat the dedicated Cloudflare storage key as authoritative and never restore its gate fromcaptchaGateState.
src/firefox/src/agent/agent.js:984 - The Cloudflare gate is also persisted inside the conversation snapshot, but clearing the dedicated signal does not rewrite that snapshot. After a normal response clears the live gate and a later background restart finds no dedicated signal, this line restores the stale conversation copy as an active Cloudflare gate; future normal responses cannot clear it because
_cloudflareManagedChallengesis empty. Treat the dedicated Cloudflare storage key as authoritative and never restore its gate fromcaptchaGateState.
if (!cloudflareSignal) this._captchaGateStates.set(tabId, captchaGateState);
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Addressed the Copilot review in
Validation: 1571 unit tests passed (one unrelated upstream changelog failure remains), security 60/60, toolbar guard 33/33, and the new-head WebMCP E2E check passed. |
Summary
This is the third and final planned follow-up from #505, after #2720 and #2721. It adds a browser-level detector for full-page Cloudflare managed Challenge Pages and routes them into the existing manual CAPTCHA gate.
The branch now includes current main through #2721. The integration preserves the canonical CAPTCHA matcher and token normalization from main while keeping the dedicated Cloudflare response signal authoritative during restored-state hydration.
Cloudflare documents
cf-mitigated: challengeas the stable response signal for Challenge Pages. The implementation therefore arms only from that header on a top-levelmain_frameresponse. Requests under/cdn-cgi/challenge-platform/are deliberately insufficient on their own because Cloudflare also uses that endpoint for general bot-management JavaScript and embedded widgets.What changed
cf-mitigated: challenge.storage.session, without query strings, fragments, response headers, or page content, so a service-worker restart cannot fail open.solve_captchadispatch while allowing explicit navigation away from the challenged document.References:
Tests
node test/run.js— 1586/1586 passed.npm run test:toolbar-guard— 33/33 passed.npm run test:security— 60/60 passed.smoke— passed ona6bd6b82.The regression coverage includes header casing, XHR exclusion, Turnstile/challenge-platform false-positive prevention, query-string redaction, localized-page retention, mutation/solver blocking, navigation abandonment, worker restart hydration, restart-first response/navigation clearing, concurrent hydration blocking, serialized storage updates, authoritative stale-snapshot rejection, chat clearing, tab cleanup, and Chrome/Firefox listener parity.