Skip to content

fix(captcha): detect Cloudflare challenge pages - #2723

Merged
esokullu merged 3 commits into
webbrain-one:mainfrom
alectimison-maker:fix/cloudflare-managed-challenge
Aug 10, 2026
Merged

fix(captcha): detect Cloudflare challenge pages#2723
esokullu merged 3 commits into
webbrain-one:mainfrom
alectimison-maker:fix/cloudflare-managed-challenge

Conversation

@alectimison-maker

@alectimison-maker alectimison-maker commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

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: challenge as the stable response signal for Challenge Pages. The implementation therefore arms only from that header on a top-level main_frame response. 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

  • Observe top-level response headers in both Chrome and Firefox and arm a manual gate only for cf-mitigated: challenge.
  • Treat challenge-platform requests as supporting diagnostics only after a response-backed gate is active.
  • Keep the signal independent of English dialog text, so localized or script-rendered interstitials remain blocked.
  • Persist a sanitized origin + pathname document identity in storage.session, without query strings, fragments, response headers, or page content, so a service-worker restart cannot fail open.
  • Block mutation and automatic solve_captcha dispatch while allowing explicit navigation away from the challenged document.
  • Clear the gate on a subsequent normal top-level response, navigation to a different document, or tab cleanup. Clearing the chat alone intentionally preserves the active tab-level guard.
  • Keep the state-transition module and behavior tests symmetric across the Chrome and Firefox builds.

References:

Tests

  • node test/run.js — 1586/1586 passed.
  • npm run test:toolbar-guard — 33/33 passed.
  • npm run test:security — 60/60 passed.
  • Chrome and Firefox Cloudflare state modules are byte-identical.
  • GitHub Actions smoke — passed on a6bd6b82.

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.

@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

@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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds browser-level Cloudflare Challenge Page detection and routes detected interstitials through the manual CAPTCHA gate.

Changes:

  • Detects cf-mitigated: challenge on 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 _cloudflareManagedChallenges is empty. Treat the dedicated Cloudflare storage key as authoritative and never restore its gate from captchaGateState.
    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 _cloudflareManagedChallenges is empty. Treat the dedicated Cloudflare storage key as authoritative and never restore its gate from captchaGateState.
          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.

Comment thread src/chrome/src/agent/agent.js Outdated
Comment thread src/firefox/src/agent/agent.js Outdated
Comment thread src/chrome/src/agent/agent.js
Comment thread src/firefox/src/agent/agent.js
@alectimison-maker

Copy link
Copy Markdown
Contributor Author

Addressed the Copilot review in 50106d10:

  • hydrate and serialize response/navigation/platform transitions per tab, including restart-first clear events;
  • share one in-flight hydration promise so concurrent callers remain fail-closed;
  • make the dedicated Cloudflare storage key authoritative, omit its gate from conversation snapshots, and reject legacy stale copies (including the suppressed review concern);
  • add Chrome/Firefox regressions for restart ordering, concurrent hydration, serialized storage writes, and stale snapshot rejection.

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.

@esokullu
esokullu merged commit 9091417 into webbrain-one:main Aug 10, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants