Skip to content

reCAPTCHA v2 is no longer detected (and bought) as v3 - #8

Merged
jehrr merged 1 commit into
mainfrom
fix/recaptcha-v2-variant
Oct 22, 2026
Merged

jehrr merged 1 commit into
mainfrom
fix/recaptcha-v2-variant

Conversation

@jehrr

@jehrr jehrr commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator

Problem

A reCAPTCHA v2 widget rendered from a .g-recaptcha element loads api.js with no render parameter. The live detector had no rule for "no size, no render", so both the checkbox and the invisible form fell to its last branch and were classified v3. A v3 task bought for a v2 widget is refused by the solver (ERROR_CAPTCHA_UNSOLVABLE in the manual test that reported this).

The report saw the invisible demo come out as a plain checkbox; measured here it came out as v3, the same as the checkbox demo. Either way it was the wrong task type.

The HTML-only detector had the same mistake in another place: it labelled grecaptcha.render, which is v2's explicit-render call, as v3.

Behaviour after

Classification, strongest signal first:

signal kind
loader render=<sitekey> v3 (v3 cannot load without it)
render=explicit v2, invisible if the client says so
loader seen, no render v2; the badge then means invisible
loader not seen the previous fallback, unchanged

The badge is checked only after v3 has been ruled out, because v3 draws .grecaptcha-badge too. The discovery script now records whether the api.js/enterprise.js loader is on the page at all (loaderSeen); without it, "no render" means nothing.

Static detector: data-sitekey + grecaptcha.render → v2.

Evidence (2026-10-22, Google's own demo pages)

page before after
v2 checkbox, implicit v3 v2
v2 invisible, implicit (two demos) v3 v2-invisible
v2 checkbox, render=explicit v2 v2
v3, render=<key> v3 v3
  • The same in Playwright, Selenium and pyppeteer.
  • Paid check, bounded: with the corrected kind, the v2 checkbox and v2 invisible demos were solved (36 s and 15 s) and accepted by Google ("Verification Success"), $0.00299 total.
  • On the demos, grecaptcha.render appears only on the v2 explicit page and grecaptcha.execute only on the v3 page.

Tests

  • captures/recaptcha_discovery.json: what the discovery script returned on those pages, with sitekeys replaced by demo-sitekey (equality with render preserved, which is all the classifier reads). Pins the kind per page, plus "v3 has a badge and stays v3", "no loader seen falls back" and both static rules.
  • .gitignore ignored every *.json, so this fixture would never have reached git and its check would have skipped in CI, which reads as green. !captures/*.json added, plus a check that fails on any file in captures/ that git would not commit.
  • Controls in disposable copies, each caught by the named check: implicit-render rule removed, badge decides before v3 is ruled out, loader presence ignored, static render back to v3, JSON captures ignored again — 5 of 5.
  • Offline suite green with core requirements and in each engine venv; credential scan clean (59 files).

🤖 Generated with Claude Code

https://claude.ai/code/session_01VrpkXSoXyZMGV5nqF9JV6P

A v2 widget rendered from a `.g-recaptcha` element loads `api.js` with no
`render` parameter. The live detector had no rule for "no size, no render",
so both the checkbox and the invisible form fell through to its last branch
and came out as v3 — and a v3 task bought for a v2 widget is refused by the
solver (ERROR_CAPTCHA_UNSOLVABLE in the report that found this).

v3 cannot be loaded without `render=<sitekey>`. So a loader seen without
one is now v2, and the badge decides invisible vs checkbox — consulted only
after v3 is ruled out, because v3 draws `.grecaptcha-badge` too. The
discovery script now records whether the api.js/enterprise.js loader was on
the page at all, so a page whose loader was not seen keeps the old
fallback instead of being read as implicit v2.

The HTML-only detector had the same mistake in another form: it labelled
`grecaptcha.render` — v2's explicit-render call — as v3. On Google's demos
the v2 explicit page calls render and never execute, the v3 page calls
execute and never render.

Measured 2026-10-22 on Google's own demo pages:
- v2 checkbox (implicit), v2 invisible (two demos), v2 explicit and v3 all
  classify correctly in Playwright, Selenium and pyppeteer;
- with the corrected kind, the v2 checkbox and v2 invisible demos were both
  solved (36 s, 15 s) and accepted by Google ("Verification Success"), for
  $0.00299 in total.

Tests: `captures/recaptcha_discovery.json` holds what the discovery script
returned on those pages (sitekeys replaced by a placeholder) and pins the
kind per page, plus the badge-on-v3 and no-loader cases and both static
rules. `.gitignore` ignored every `*.json`, which would have kept that
fixture out of git and made its check skip in CI; JSON captures are now
committable, and a new check fails on any capture git would not commit.
Five controls, each planted in a disposable copy and caught by its named
check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VrpkXSoXyZMGV5nqF9JV6P
@jehrr
jehrr merged commit 15f13bf into main Oct 22, 2026
7 checks passed
@jehrr
jehrr deleted the fix/recaptcha-v2-variant branch October 22, 2026 21:46
@jehrr jehrr mentioned this pull request Oct 22, 2026
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.

1 participant