diff --git a/open-sse/utils/proxyFetch.js b/open-sse/utils/proxyFetch.js index 837d29e..43b7164 100644 --- a/open-sse/utils/proxyFetch.js +++ b/open-sse/utils/proxyFetch.js @@ -155,8 +155,13 @@ async function createBypassRequest(parsedUrl, realIP, options) { socket.connect(HTTPS_PORT, realIP, () => { const reqOptions = { socket, + // SNI + cert hostname are validated against the hostname the caller + // asked for, not the IP we connected to. This keeps the DNS-bypass + // (avoiding /etc/hosts MITM) while still rejecting on-path attackers + // that present a different cert. The MITM_BYPASS_HOSTS targets are + // all public-CA-issued (Google / GitHub / AWS / Cursor) so default + // verification works without any extra trust store. servername: parsedUrl.hostname, - rejectUnauthorized: false, path: parsedUrl.pathname + parsedUrl.search, method: options.method || "POST", headers: { diff --git a/src/app/callback/page.js b/src/app/callback/page.js index 4064b03..e55370d 100644 --- a/src/app/callback/page.js +++ b/src/app/callback/page.js @@ -26,19 +26,29 @@ function CallbackContent() { let relayed = false; - // Check if this callback is from expected origin/port + // Trusted origins that may receive this callback. The OAuth code/state + // must only be relayed to the dashboard window we expect to be the opener + // (same origin) or the Codex helper that listens on a fixed loopback port. + // Any other origin is treated as hostile (drive-by attacker that opened + // the popup against the well-known redirect_uri to phish the code). const expectedOrigins = [ window.location.origin, // Same origin (for most providers) "http://localhost:1455", // Codex specific port ]; - + // Method 1: postMessage to opener (popup mode) + // Send once per expected origin. The browser delivers the message only + // when the opener's origin matches the targetOrigin we pass — using "*" + // here would leak the code/state to any opener (e.g. an attacker page + // that opened this URL in a popup), so iterate over the allowlist. if (window.opener) { - try { - window.opener.postMessage({ type: "oauth_callback", data: callbackData }, "*"); - relayed = true; - } catch (e) { - console.log("postMessage failed:", e); + for (const origin of expectedOrigins) { + try { + window.opener.postMessage({ type: "oauth_callback", data: callbackData }, origin); + relayed = true; + } catch (e) { + console.log("postMessage failed:", e); + } } }