From 28a6a274bb1fc0806c79850d15b3f8e030278694 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:33:28 +0000 Subject: [PATCH 1/5] Initial plan From 4abc501f7bebeeb9e4056e7ce0fd34cdc53d97e8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:42:11 +0000 Subject: [PATCH 2/5] Fix CAPTCHA integration for multiple providers Co-authored-by: sriail <225764385+sriail@users.noreply.github.com> --- public/sw.js | 5 +- src/utils/captcha-handler.ts | 193 ++++++++++++++++++++++++++++++++++- src/utils/proxy.ts | 2 +- 3 files changed, 194 insertions(+), 6 deletions(-) diff --git a/public/sw.js b/public/sw.js index efad95a..54fc0ba 100644 --- a/public/sw.js +++ b/public/sw.js @@ -18,7 +18,10 @@ const CAPTCHA_DOMAINS = [ "newassets.hcaptcha.com", "challenges.cloudflare.com", "cloudflare.com/cdn-cgi/challenge", - "turnstile.cloudflare.com" + "turnstile.cloudflare.com", + "yandex.com/captcha", + "yandex.ru/captcha", + "smartcaptcha.yandexcloud.net" ]; // Helper function to check if URL is CAPTCHA-related diff --git a/src/utils/captcha-handler.ts b/src/utils/captcha-handler.ts index 47a74e5..4a9e8cc 100644 --- a/src/utils/captcha-handler.ts +++ b/src/utils/captcha-handler.ts @@ -13,7 +13,10 @@ const CAPTCHA_DOMAINS = [ "gstatic.com", "hcaptcha.com", "cloudflare.com", - "challenges.cloudflare.com" + "challenges.cloudflare.com", + "yandex.com", + "yandex.ru", + "smartcaptcha.yandexcloud.net" ]; /** @@ -23,6 +26,12 @@ const CAPTCHA_DOMAINS = [ export function initializeCaptchaHandlers() { if (typeof window === "undefined") return; + // Fix MessagePort cloning errors for CAPTCHA iframes + fixMessagePortCloning(); + + // Add missing Cloudflare challenge solver functions + addCloudflareChallengeHandlers(); + // Ensure global CAPTCHA callbacks are accessible if (!window.___grecaptcha_cfg) { window.___grecaptcha_cfg = { clients: {} }; @@ -71,6 +80,112 @@ export function initializeCaptchaHandlers() { enhanceNetworkRequests(); } +/** + * Fix MessagePort cloning errors that occur in CAPTCHA iframes + * This prevents "DataCloneError: Failed to execute 'postMessage' on 'Window'" errors + */ +function fixMessagePortCloning() { + const originalPostMessage = window.postMessage.bind(window); + + // Override postMessage to properly handle MessagePort transfers + (window as any).postMessage = function (message: any, ...args: any[]) { + try { + // Handle both old (targetOrigin, transfer) and new (options) signatures + const targetOrigin = typeof args[0] === "string" ? args[0] : "*"; + let transfer = args[1]; + + // Handle new WindowPostMessageOptions signature + if (typeof args[0] === "object" && args[0] !== null && "targetOrigin" in args[0]) { + const options = args[0] as WindowPostMessageOptions; + transfer = options.transfer; + return originalPostMessage(message, options); + } + + // If transfer array contains MessagePort objects, ensure they are properly transferred + if (transfer && Array.isArray(transfer)) { + const hasMessagePort = transfer.some( + (item: any) => + item instanceof MessagePort || item?.constructor?.name === "MessagePort" + ); + + if (hasMessagePort) { + // Use the transfer parameter explicitly + return originalPostMessage(message, targetOrigin, transfer); + } + } + + // For other cases, check if message contains MessagePort and auto-detect transfer + if (message && typeof message === "object") { + const ports: MessagePort[] = []; + const collectPorts = (obj: any) => { + if (obj instanceof MessagePort || obj?.constructor?.name === "MessagePort") { + ports.push(obj); + } else if (obj && typeof obj === "object") { + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + collectPorts(obj[key]); + } + } + } + }; + collectPorts(message); + + if (ports.length > 0) { + // Auto-transfer detected MessagePorts + return originalPostMessage(message, targetOrigin, ports); + } + } + + // Standard call + if (transfer !== undefined) { + return originalPostMessage(message, targetOrigin, transfer); + } else { + return originalPostMessage(message, targetOrigin); + } + } catch (error) { + // Fallback: try without transfer parameter + console.warn("postMessage transfer failed, attempting without transfer:", error); + try { + const targetOrigin = typeof args[0] === "string" ? args[0] : "*"; + return originalPostMessage(message, targetOrigin); + } catch (fallbackError) { + console.error("postMessage completely failed:", fallbackError); + throw fallbackError; + } + } + }; +} + +/** + * Add missing Cloudflare challenge solver functions + * This fixes "ReferenceError: solveSimpleChallenge is not defined" errors + */ +function addCloudflareChallengeHandlers() { + // Define solveSimpleChallenge for Cloudflare Turnstile/Challenge pages + if (typeof (window as any).solveSimpleChallenge === "undefined") { + (window as any).solveSimpleChallenge = function () { + console.log("Simple challenge solver called"); + // The actual challenge solving is handled by Cloudflare's scripts + // This function just needs to exist to prevent the ReferenceError + }; + } + + // Add support for managed challenge callback + if (typeof (window as any).managedChallengeCallback === "undefined") { + (window as any).managedChallengeCallback = function (token: string) { + console.log("Managed challenge callback:", token); + // Handle the challenge token + }; + } + + // Add support for interactive challenge + if (typeof (window as any).interactiveChallenge === "undefined") { + (window as any).interactiveChallenge = function () { + console.log("Interactive challenge called"); + }; + } +} + /** * Enhance cookie handling to ensure CAPTCHA tokens are properly stored */ @@ -128,9 +243,25 @@ function enhanceNetworkRequests() { if (!init.headers.has("Accept")) { init.headers.set("Accept", "*/*"); } + + // Set mode to cors for CAPTCHA requests to avoid CORS issues + if (!init.mode || init.mode === "navigate") { + init.mode = "cors"; + } } - return originalFetch.call(this, input, init); + return originalFetch.call(this, input, init).catch((error) => { + // Enhanced error handling for CAPTCHA requests + if (isCaptchaRequest) { + console.warn("CAPTCHA fetch error:", url, error); + // Try again without custom init for preload compatibility + if (init && (init.credentials || init.mode)) { + console.log("Retrying CAPTCHA request with default settings"); + return originalFetch.call(this, input, { credentials: "include" }); + } + } + throw error; + }); }; // Store original XMLHttpRequest @@ -142,7 +273,13 @@ function enhanceNetworkRequests() { // Store original open method const originalOpen = xhr.open; - xhr.open = function (method: string, url: string | URL, ...args: any[]) { + xhr.open = function ( + method: string, + url: string | URL, + async: boolean = true, + username?: string | null, + password?: string | null + ) { const urlStr = url.toString(); const isCaptchaRequest = CAPTCHA_DOMAINS.some((domain) => urlStr.includes(domain)); @@ -151,7 +288,13 @@ function enhanceNetworkRequests() { xhr.withCredentials = true; } - return originalOpen.call(this, method, url, ...args); + if (username !== undefined && password !== undefined) { + return originalOpen.call(this, method, url, async, username, password); + } else if (username !== undefined) { + return originalOpen.call(this, method, url, async, username); + } else { + return originalOpen.call(this, method, url, async); + } }; return xhr; @@ -160,6 +303,48 @@ function enhanceNetworkRequests() { // Copy static properties Object.setPrototypeOf(window.XMLHttpRequest, OriginalXHR); Object.setPrototypeOf(window.XMLHttpRequest.prototype, OriginalXHR.prototype); + + // Fix preload resource loading for CAPTCHA scripts + enhancePreloadHandling(); +} + +/** + * Enhance preload handling to fix credential mode mismatches + */ +function enhancePreloadHandling() { + // Monitor for link elements being added to the page + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + mutation.addedNodes.forEach((node) => { + if (node instanceof HTMLLinkElement && node.rel === "preload") { + const href = node.href || ""; + // Check if this is a CAPTCHA-related resource + if (CAPTCHA_DOMAINS.some((domain) => href.includes(domain))) { + // Ensure crossorigin attribute is set for proper credential handling + if (!node.hasAttribute("crossorigin")) { + node.setAttribute("crossorigin", "use-credentials"); + } + } + } + // Also handle script tags that might be preloaded + if (node instanceof HTMLScriptElement) { + const src = node.src || ""; + if (CAPTCHA_DOMAINS.some((domain) => src.includes(domain))) { + // Ensure crossorigin attribute is set + if (!node.hasAttribute("crossorigin")) { + node.setAttribute("crossorigin", "use-credentials"); + } + } + } + }); + }); + }); + + // Start observing + observer.observe(document.documentElement, { + childList: true, + subtree: true + }); } /** diff --git a/src/utils/proxy.ts b/src/utils/proxy.ts index b65988e..e6b4c21 100644 --- a/src/utils/proxy.ts +++ b/src/utils/proxy.ts @@ -182,7 +182,7 @@ class SW { } }); if ("serviceWorker" in navigator) { - await this.#scramjetController.init(); + await this.#scramjetController!.init(); navigator.serviceWorker.ready.then(async (reg) => { console.log("SW ready to go!"); this.#serviceWorker = reg; From bb59acbb61b70e8423fded0676cf3cababcf3072 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:45:45 +0000 Subject: [PATCH 3/5] Address code review feedback for CAPTCHA fixes Co-authored-by: sriail <225764385+sriail@users.noreply.github.com> --- src/utils/captcha-handler.ts | 44 +++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/src/utils/captcha-handler.ts b/src/utils/captcha-handler.ts index 4a9e8cc..55d52b6 100644 --- a/src/utils/captcha-handler.ts +++ b/src/utils/captcha-handler.ts @@ -87,6 +87,24 @@ export function initializeCaptchaHandlers() { function fixMessagePortCloning() { const originalPostMessage = window.postMessage.bind(window); + // Helper to check if an object is a MessagePort + const isMessagePort = (obj: any): boolean => { + if (!obj) return false; + // Try instanceof first (most reliable) + if (obj instanceof MessagePort) return true; + // Fallback: check for MessagePort-like interface + if ( + typeof obj === "object" && + typeof obj.postMessage === "function" && + typeof obj.start === "function" && + typeof obj.close === "function" + ) { + // Additional check for constructor name as a hint (not definitive) + return obj.constructor?.name === "MessagePort" || obj.toString() === "[object MessagePort]"; + } + return false; + }; + // Override postMessage to properly handle MessagePort transfers (window as any).postMessage = function (message: any, ...args: any[]) { try { @@ -103,10 +121,7 @@ function fixMessagePortCloning() { // If transfer array contains MessagePort objects, ensure they are properly transferred if (transfer && Array.isArray(transfer)) { - const hasMessagePort = transfer.some( - (item: any) => - item instanceof MessagePort || item?.constructor?.name === "MessagePort" - ); + const hasMessagePort = transfer.some((item: any) => isMessagePort(item)); if (hasMessagePort) { // Use the transfer parameter explicitly @@ -117,13 +132,16 @@ function fixMessagePortCloning() { // For other cases, check if message contains MessagePort and auto-detect transfer if (message && typeof message === "object") { const ports: MessagePort[] = []; - const collectPorts = (obj: any) => { - if (obj instanceof MessagePort || obj?.constructor?.name === "MessagePort") { + const collectPorts = (obj: any, depth: number = 0) => { + // Limit recursion depth to prevent infinite loops + if (depth > 10) return; + + if (isMessagePort(obj)) { ports.push(obj); } else if (obj && typeof obj === "object") { for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { - collectPorts(obj[key]); + collectPorts(obj[key], depth + 1); } } } @@ -276,7 +294,7 @@ function enhanceNetworkRequests() { xhr.open = function ( method: string, url: string | URL, - async: boolean = true, + async?: boolean, username?: string | null, password?: string | null ) { @@ -288,12 +306,16 @@ function enhanceNetworkRequests() { xhr.withCredentials = true; } + // Call with appropriate number of arguments based on what was provided if (username !== undefined && password !== undefined) { - return originalOpen.call(this, method, url, async, username, password); + return originalOpen.call(this, method, url, async ?? true, username, password); } else if (username !== undefined) { - return originalOpen.call(this, method, url, async, username); - } else { + return originalOpen.call(this, method, url, async ?? true, username); + } else if (async !== undefined) { return originalOpen.call(this, method, url, async); + } else { + // Use the 2-argument overload + return (originalOpen as any).call(this, method, url); } }; From 802e891bd601ff04afc76a3befd20fab26f3cc3e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:50:32 +0000 Subject: [PATCH 4/5] Improve code quality based on review feedback Co-authored-by: sriail <225764385+sriail@users.noreply.github.com> --- src/utils/captcha-handler.ts | 45 ++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/utils/captcha-handler.ts b/src/utils/captcha-handler.ts index 55d52b6..744d858 100644 --- a/src/utils/captcha-handler.ts +++ b/src/utils/captcha-handler.ts @@ -85,6 +85,9 @@ export function initializeCaptchaHandlers() { * This prevents "DataCloneError: Failed to execute 'postMessage' on 'Window'" errors */ function fixMessagePortCloning() { + // Maximum recursion depth when searching for MessagePort objects + const MAX_RECURSION_DEPTH = 10; + const originalPostMessage = window.postMessage.bind(window); // Helper to check if an object is a MessagePort @@ -134,7 +137,7 @@ function fixMessagePortCloning() { const ports: MessagePort[] = []; const collectPorts = (obj: any, depth: number = 0) => { // Limit recursion depth to prevent infinite loops - if (depth > 10) return; + if (depth > MAX_RECURSION_DEPTH) return; if (isMessagePort(obj)) { ports.push(obj); @@ -177,29 +180,37 @@ function fixMessagePortCloning() { /** * Add missing Cloudflare challenge solver functions * This fixes "ReferenceError: solveSimpleChallenge is not defined" errors + * + * Note: These are stub implementations. Cloudflare's actual challenge solving + * is handled by their own scripts loaded in the page. These functions just need + * to exist to prevent reference errors when Cloudflare scripts try to call them. */ function addCloudflareChallengeHandlers() { // Define solveSimpleChallenge for Cloudflare Turnstile/Challenge pages + // This is a stub - actual challenge solving is done by Cloudflare's own scripts if (typeof (window as any).solveSimpleChallenge === "undefined") { (window as any).solveSimpleChallenge = function () { - console.log("Simple challenge solver called"); - // The actual challenge solving is handled by Cloudflare's scripts - // This function just needs to exist to prevent the ReferenceError + console.log("Simple challenge solver called - handled by Cloudflare scripts"); + // Empty implementation - Cloudflare's scripts handle the actual solving }; } // Add support for managed challenge callback + // This receives the challenge token from Cloudflare after successful verification if (typeof (window as any).managedChallengeCallback === "undefined") { (window as any).managedChallengeCallback = function (token: string) { - console.log("Managed challenge callback:", token); - // Handle the challenge token + console.log("Managed challenge callback received token:", token?.substring(0, 20) + "..."); + // The token is automatically used by Cloudflare's scripts + // This callback is just for logging/debugging purposes }; } // Add support for interactive challenge + // Called when user interaction is required (e.g., clicking a checkbox) if (typeof (window as any).interactiveChallenge === "undefined") { (window as any).interactiveChallenge = function () { - console.log("Interactive challenge called"); + console.log("Interactive challenge initiated - waiting for user interaction"); + // Cloudflare's scripts handle the actual UI and interaction }; } } @@ -290,8 +301,11 @@ function enhanceNetworkRequests() { const xhr = new OriginalXHR(); // Store original open method - const originalOpen = xhr.open; + const originalOpen = xhr.open.bind(xhr); + + // Override open method to add CAPTCHA support xhr.open = function ( + this: XMLHttpRequest, method: string, url: string | URL, async?: boolean, @@ -306,18 +320,19 @@ function enhanceNetworkRequests() { xhr.withCredentials = true; } - // Call with appropriate number of arguments based on what was provided + // Call original open with appropriate arguments + // Using type assertion to handle overloaded signature + const openFn = originalOpen as any; if (username !== undefined && password !== undefined) { - return originalOpen.call(this, method, url, async ?? true, username, password); + return openFn(method, url, async ?? true, username, password); } else if (username !== undefined) { - return originalOpen.call(this, method, url, async ?? true, username); + return openFn(method, url, async ?? true, username); } else if (async !== undefined) { - return originalOpen.call(this, method, url, async); + return openFn(method, url, async); } else { - // Use the 2-argument overload - return (originalOpen as any).call(this, method, url); + return openFn(method, url); } - }; + } as typeof xhr.open; return xhr; } as any; From 7d077c20e9055bbca0229eaa06f4bfead0b1893a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:52:52 +0000 Subject: [PATCH 5/5] Final code quality improvements and type safety Co-authored-by: sriail <225764385+sriail@users.noreply.github.com> --- src/utils/captcha-handler.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/utils/captcha-handler.ts b/src/utils/captcha-handler.ts index 744d858..d7606fa 100644 --- a/src/utils/captcha-handler.ts +++ b/src/utils/captcha-handler.ts @@ -109,7 +109,8 @@ function fixMessagePortCloning() { }; // Override postMessage to properly handle MessagePort transfers - (window as any).postMessage = function (message: any, ...args: any[]) { + // Note: Using unknown for message type as it can be any cloneable data + (window as any).postMessage = function (message: unknown, ...args: any[]) { try { // Handle both old (targetOrigin, transfer) and new (options) signatures const targetOrigin = typeof args[0] === "string" ? args[0] : "*"; @@ -199,7 +200,8 @@ function addCloudflareChallengeHandlers() { // This receives the challenge token from Cloudflare after successful verification if (typeof (window as any).managedChallengeCallback === "undefined") { (window as any).managedChallengeCallback = function (token: string) { - console.log("Managed challenge callback received token:", token?.substring(0, 20) + "..."); + const tokenPreview = token && token.length > 20 ? token.substring(0, 20) + "..." : token; + console.log("Managed challenge callback received token:", tokenPreview); // The token is automatically used by Cloudflare's scripts // This callback is just for logging/debugging purposes }; @@ -321,7 +323,9 @@ function enhanceNetworkRequests() { } // Call original open with appropriate arguments - // Using type assertion to handle overloaded signature + // Note: Using 'as any' here because XMLHttpRequest.open has overloaded signatures + // that TypeScript cannot properly infer when calling dynamically with variable arguments. + // This is safe because we're calling the same native method with its original signatures. const openFn = originalOpen as any; if (username !== undefined && password !== undefined) { return openFn(method, url, async ?? true, username, password);