feat: classify grok-web Cloudflare anti-bot blocks + gated browser-backed cf_clearance path (#8019) - #8241
Merged
Conversation
…cked cf_clearance path (#8019)
Closed
4 tasks
HouMinXi
pushed a commit
to HouMinXi/OmniRoute
that referenced
this pull request
Aug 2, 2026
…cked cf_clearance path (diegosouzapw#8019) (diegosouzapw#8241) Co-authored-by: Probe Test <probe@example.com>
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…cked cf_clearance path (diegosouzapw#8019) (diegosouzapw#8241) Co-authored-by: Probe Test <probe@example.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports the existing stealth-browser /
cf_clearanceinfra (already live forclaude-webandduckduckgo-web) togrok-web, in two parts.Part 1 (primary, fully unit-tested, no browser dependency).
grok-web.tsalready importedisCloudflareChallengefromgrokTlsClient.tsbut never called it — so a Cloudflare anti-bot block ("Request rejected by anti-bot rules") was misclassified as a genericauthentication_error, the exact complaint in #8019. A new pure helperclassifyGrokNullBodyError(status, text)now distinguishes:type: "cloudflare_challenge",code: "cf_mitigated_challenge", with an actionable message (cf_clearance is pinned to IP+TLS+UA and can't be replayed from a datacenter egress; use a residential IP or the official xAI API).authentication_error(unchanged behavior, re-paste SSO cookie).rate_limit_error(regression guard, unchanged behavior).upstream_error(unchanged).Part 2 (secondary, gated; real-world effectiveness is VPS-gated). Behind the existing
OMNIROUTE_BROWSER_POOL/WEB_COOKIE_USE_BROWSERopt-in gate (same env vars already used byclaude-web.ts/duckduckgo-web.ts), on a detected Cloudflare challenge the executor now attempts ONE browser-backedcf_clearancerefresh via the provider-agnostic browser pool (browserPool.ts→ newopen-sse/services/grokClearance.tshelper) and retriestlsFetchGrokonce with the fresh cookie injected. No new Turnstile solver —claudeTurnstileSolver.tsis claude.ai-specific and out of scope; this reuses the same generic pool primitive already live for the other two providers. On any acquisition/retry failure it falls through to the Part-1cloudflare_challengeerror — never throws.Gate stays off by default (env var unset). Nothing changes for existing grok-web users who don't opt in.
Files changed
open-sse/executors/grok-web.ts— classification + gated retry wiringopen-sse/services/grokClearance.ts(new) —shouldUseGrokBrowserBacked()gate +acquireFreshGrokClearance()(test-seam injectable, mirrorsbrowserBackedChat.ts's override pattern)tests/unit/grok-web-cloudflare-classification.test.ts(new) — 16 tests covering pure classification, executor wiring, and gated retry (gate OFF / gate ON+success / gate ON+acquisition-failure / gate ON+retry-still-challenged), all with a mocked TLS client and mocked browser acquisition — no real network/browser in CIchangelog.d/features/8019-grok-web-cloudflare-classification.mdTesting
node --import tsx/esm --test tests/unit/grok-web-cloudflare-classification.test.ts— 16/16 greennode --import tsx/esm --test tests/unit/grok-web.test.ts tests/unit/grok-web-executor-split.test.ts— 65/65 green (no regression)npm run typecheck:core/npm run typecheck:noimplicit:core— clean on changed files (pre-existing unrelated errors oncombo.ts/usageTracking.ts/cliRuntime.tsconfirmed identical onorigin/release/v3.8.49, not touched by this PR)npx eslint --suppressions-location config/quality/eslint-suppressions.jsonon changed files — cleannpm run check:complexity-ratchets— flags a pre-existing regression (2156 vs baseline 2130) that is identical on a cleanorigin/release/v3.8.49checkout with none of this PR's changes applied (verified in an isolated detached worktree) — confirmed base-red, not introduced here. My new/edited functions ingrok-web.tsdo not appear in the ESLint complexity report at all.node scripts/check/check-test-discovery.mjs— new test file collected, OKnode scripts/check/check-file-size.mjs— OK,grok-web.tswithin baselinenpm run check:cycles— OK, no new cyclesLive check needed before Part 2 is fully trusted
Part 2's real-world effectiveness (whether the browser pool actually mints a usable cf_clearance from a Cloudflare-flagged datacenter egress) needs a live VPS check per Hard Rule #18: deploy to 192.168.0.15, connect a grok-web sso cookie from a flagged egress, set
OMNIROUTE_BROWSER_POOL=on, issue a chat request, and confirm either a successful recovery or the distinctcloudflare_challengeerror. Part 1 (the classification fix) is fully proven by the unit tests above and needs no live check.Closes #8019