Skip to content

fix(captcha): detect active challenge frames - #2615

Merged
esokullu merged 2 commits into
webbrain-one:mainfrom
alectimison-maker:fix/captcha-active-challenge-frames
Jul 29, 2026
Merged

fix(captcha): detect active challenge frames#2615
esokullu merged 2 commits into
webbrain-one:mainfrom
alectimison-maker:fix/captcha-active-challenge-frames

Conversation

@alectimison-maker

Copy link
Copy Markdown
Contributor

Summary

  • distinguish active CAPTCHA challenge frames from ordinary checkbox/anchor frames
  • arm the CAPTCHA gate from visible reCAPTCHA, hCaptcha, and Arkose challenge routes without relying on English dialog copy
  • keep Chrome and Firefox routing, diagnostics, and regression coverage aligned

Motivation

The gate currently starts from English phrases such as "Security verification".
Localized challenge dialogs therefore remain unguarded even when the page exposes
a language-neutral vendor challenge frame. This advances #505 after the response
token-state work from #512.

Design

The detector now records a separate activeChallengeFrame signal for exact
vendor routes:

  • reCAPTCHA /recaptcha/{api2,enterprise}/bframe
  • hCaptcha #frame=challenge
  • Arkose /fc/gc/

That signal is deliberately narrower than the existing generic
challengeFrame heuristic. Visible reCAPTCHA anchors, hCaptcha checkbox
frames, hidden frames, generic application CAPTCHA paths, and spoofed vendor
hostnames cannot arm it.

For an accessibility-tree result, local CAPTCHA detection runs early only when
the tree already exposes a dialog surface. Before the first mutation, the
existing read-only page preflight also recognizes a visible active challenge
frame. Supported visible frames route to solve_required; Arkose and
solver-disabled cases continue to fail closed as manual_required.

Testing

  • node test/run.js — 1354 passed; 1 inherited failure because
    package.json is 26.0.1 while the newest CHANGELOG.md entry is 26.0.0
  • node test/security/injection-corpus.mjs — 60/60 passed
  • node --check for both agent.js, captcha-gate.js, and
    captcha-frame-runtime.js copies — passed
  • Chrome/Firefox byte-parity checks for the mirrored CAPTCHA helper modules —
    passed
  • git diff --check — passed

Compatibility and risks

No public API, permission, network, or storage behavior changes. The remaining
risk is vendor URL drift; exact route matching intentionally prefers a missed
new route over a false-positive gate.

Scope

This PR does not add localized phrase tables, rewrite verification-state
cleanup, or detect full-page Cloudflare interstitials. Those remain separate
steps in #505.

Advances #505.

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 language-neutral detection of active CAPTCHA challenge frames across Chrome and Firefox.

Changes:

  • Classifies visible reCAPTCHA, hCaptcha, and Arkose challenge routes.
  • Uses active-frame signals for gate routing and diagnostics.
  • Adds regression and parity coverage.

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 Adds challenge-frame regression tests.
src/chrome/src/agent/captcha-gate.js Detects and reports active vendor frames.
src/firefox/src/agent/captcha-gate.js Mirrors Chrome gate detection.
src/chrome/src/agent/captcha-frame-runtime.js Classifies and ranks active frames.
src/firefox/src/agent/captcha-frame-runtime.js Mirrors Chrome frame runtime.
src/chrome/src/agent/agent.js Arms and routes the CAPTCHA gate.
src/firefox/src/agent/agent.js Mirrors Chrome gate routing.
Comments suppressed due to low confidence (2)

src/firefox/src/agent/agent.js:2851

  • The active-frame correlation branch does not require the selected candidate to be visible. If an English challenge dialog coexists with a hidden bframe outside that dialog, the hidden candidate now counts as correlated and routes to solve_required; previously that unrelated candidate failed closed. Require selected.visible === true for the active-frame branch.
    const selectedCorrelated = (
      detection?.selected?.dialogAssociated === true
      || detection?.selected?.activeChallengeFrame === true
    ) && detection?.selected?.frameVisible !== false;

src/chrome/src/agent/agent.js:3226

  • The active-frame correlation branch does not require the selected candidate to be visible. If an English challenge dialog coexists with a hidden bframe outside that dialog, the hidden candidate now counts as correlated and routes to solve_required; previously that unrelated candidate failed closed. Require selected.visible === true for the active-frame branch.
    const selectedCorrelated = (
      detection?.selected?.dialogAssociated === true
      || detection?.selected?.activeChallengeFrame === true
    ) && detection?.selected?.frameVisible !== false;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/chrome/src/agent/captcha-frame-runtime.js
Comment thread src/firefox/src/agent/captcha-frame-runtime.js
Comment thread src/chrome/src/agent/agent.js
Comment thread src/firefox/src/agent/agent.js
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

@esokullu is attempting to deploy a commit to the esokullu's projects Team on Vercel.

A member of the Team first needs to authorize it.

@esokullu
esokullu merged commit 10a215c into webbrain-one:main Jul 29, 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