From ad1e5d4e1dc1cff520651c78b22af4364ac39f51 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Wed, 5 Aug 2026 18:38:38 +0200 Subject: [PATCH] Theseus: SameSite=None cookie shim for cross-site faucet embeds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Captcha-gated testnet faucets in the faucet hub set session cookies with no SameSite attribute; Chromium defaults those to Lax and withholds them inside cross-site iframes, so cookie-bound captcha endpoints 500 (tbch.googol.cash /captcha: 500 cookieless, 200 with the session cookie). applyEmbedCookieShim() rewrites Set-Cookie on an allowlist of embed hosts to append "; SameSite=None; Secure" so the cookie is frame-eligible. Allowlist-scoped only — SameSite is CSRF protection, never relaxed globally. --- main.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/main.js b/main.js index 613448d..16418cb 100644 --- a/main.js +++ b/main.js @@ -381,6 +381,24 @@ function applyPermissions() { return true; }); } +// Cookie shim for cross-site embeds. Sites like the faucet hub's captcha-gated testnet +// faucets set session cookies with no SameSite attribute; Chromium defaults those to +// Lax and withholds them inside cross-site iframes, so cookie-bound captcha endpoints +// fail (tbch.googol.cash /captcha 500s without its session cookie). Rewriting their +// Set-Cookie to SameSite=None; Secure makes the cookie frame-eligible. Allowlist only — +// SameSite is CSRF protection, never relax it globally. NOTE: Electron keeps a single +// onHeadersReceived listener per session; if another is ever added, merge them. +const EMBED_COOKIE_SITES = ["https://tbch.googol.cash/*", "https://signetfaucet.com/*"]; +function applyEmbedCookieShim() { + session.defaultSession.webRequest.onHeadersReceived({ urls: EMBED_COOKIE_SITES }, (details, callback) => { + const headers = details.responseHeaders || {}; + for (const key of Object.keys(headers)) { + if (key.toLowerCase() !== "set-cookie") continue; + headers[key] = headers[key].map((c) => (/;\s*samesite=/i.test(c) ? c : c + "; SameSite=None; Secure")); + } + callback({ responseHeaders: headers }); + }); +} protocol.registerSchemesAsPrivileged([ { scheme: "bns", privileges: { standard: true, secure: true, supportFetchAPI: true, stream: true } }, @@ -1208,6 +1226,7 @@ if (!process.env.THESEUS_NO_AUTOSTART) { loadBookmarks(); loadCollisions(); applyPermissions(); + applyEmbedCookieShim(); applyAcceptLanguage(); protocol.handle("bns", serveBns); installDownloadTracker();