SPB Git

spb/fetcha

Public

browser: never block anti-bot verification assets (/cdn-cgi trace pixels, widget assets); escalated renders load every asset

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Sep 8, 2026) parent 23a4b63 Browse files

2 changed files +7 −2

modified packages/browser/src/index.ts +4 −1
@@ -106,6 +106,9 @@ export interface BrowserStatus {
106 captchaSolver: { provider: string; requested: number; solved: number; failed: number; spentUsd: number; lastError: string | null } | null; 106 captchaSolver: { provider: string; requested: number; solved: number; failed: number; spentUsd: number; lastError: string | null } | null;
107 } 107 }
108 108
109+/** Assets that anti-bot vendors use to verify a real browser (trace pixels, widget assets): never block them. */
110+const ANTIBOT_ASSET_RE = /\/cdn-cgi\/|challenges\.cloudflare\.com|captcha-delivery\.com|datadome|perimeterx|px-cdn|px-cloud|kasada|kpsdk|imperva|incapsula|_Incapsula_Resource|hcaptcha\.com|recaptcha|gstatic\.com\/recaptcha|arkoselabs|funcaptcha|awswaf|distil|shape-security|vercel\.com\/_vercel|\/_vercel\/|akamaihd|akam\/|\/akam\//i;
111+
109 const CHALLENGE_SELECTORS = ["#challenge-running", "#challenge-form", "#challenge-stage", ".cf-turnstile", "iframe[src*='challenges.cloudflare.com']", "#px-captcha", "#datadome", "iframe[src*='captcha-delivery.com']", "#sec-cpt-if", "form#challenge", "#cmsg", "[data-testid='challenge']"]; 112 const CHALLENGE_SELECTORS = ["#challenge-running", "#challenge-form", "#challenge-stage", ".cf-turnstile", "iframe[src*='challenges.cloudflare.com']", "#px-captcha", "#datadome", "iframe[src*='captcha-delivery.com']", "#sec-cpt-if", "form#challenge", "#cmsg", "[data-testid='challenge']"];
110 113
111 export class BrowserPool { 114 export class BrowserPool {
@@ -386,7 +389,7 @@ export class BrowserPool {
386 // fallback() (not continue()) lets Patchright's context-level route inject the init scripts. 389 // fallback() (not continue()) lets Patchright's context-level route inject the init scripts.
387 return route.fallback(); 390 return route.fallback();
388 } 391 }
389− if (blockResources) { 392+ if (blockResources && !ANTIBOT_ASSET_RE.test(r.url())) {
390 const type = r.resourceType(); 393 const type = r.resourceType();
391 if (type === "image" || type === "media" || type === "font" || type === "manifest" || type === "texttrack") return route.abort("blockedbyclient"); 394 if (type === "image" || type === "media" || type === "font" || type === "manifest" || type === "texttrack") return route.abort("blockedbyclient");
392 } 395 }
modified packages/routing/src/executor.ts +3 −1
@@ -224,7 +224,9 @@ export class FetchExecutor {
224 waitFor: request.wait_for ?? null, 224 waitFor: request.wait_for ?? null,
225 waitMs: request.wait_ms ?? null, 225 waitMs: request.wait_ms ?? null,
226 javascript: request.javascript !== false, 226 javascript: request.javascript !== false,
227− blockResources: request.block_resources, 227+ // A site that already blocked us (or is known to need the browser) gets every asset: anti-bot
228+ // verification checks that images/fonts really load.
229+ blockResources: request.block_resources && !browserEscalated && !knowledgeSaysBrowser,
228 screenshot: request.screenshot, 230 screenshot: request.screenshot,
229 solveCaptcha: request.solve_captcha, 231 solveCaptcha: request.solve_captcha,
230 maxResponseBytes: maxBytes, 232 maxResponseBytes: maxBytes,
231 233
Menu

Theme