From 9f06a83b6410e9d05beaa13b90da8181728ad96b Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 7 Sep 2026 13:17:02 +0200 Subject: [PATCH] Fix Firefox silently upgrading ws:// to wss:// against the dev backend Firefox's implicit default extension-pages CSP includes upgrade-insecure-requests, which rewrites the WS client's plain ws://localhost:8080/ws connection to wss:// -- which nothing is listening on, since the local dev backend has no TLS (deliberately; see backend/CLAUDE.md). Symptom was silent: a CSP console message about the upgrade, then a failed connection with no other signal. Declaring an explicit content_security_policy.extension_pages in wxt.config.ts (otherwise identical to Firefox's own default) replaces the implicit one and drops the upgrade directive. Gated to browser === 'firefox' since Chrome doesn't have this behavior and its MV3 CSP can't be loosened this way regardless. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01YXiHuScXrjxBh7yFGAPq3B --- CLAUDE.md | 1 + wxt.config.ts | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 4ff19ac..ba78b49 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -86,6 +86,7 @@ Note the naming mismatch with the wire protocol below: the frontend's internal ` ## Backend connection (implemented) - `src/shared/config.ts` — `BACKEND_HTTP_URL`/`BACKEND_WS_URL`, currently hardcoded to `localhost:8080` (dev only; `host_permissions` in `wxt.config.ts` must stay in sync with whatever host is configured here). Deliberately not `:3000` — that's this extension's own Vite dev server port (`npm run dev`), and running the backend on the same port breaks the dev popup silently: its script tags point at Vite, but the backend answers instead, so nothing ever renders. If you see a blank popup with `http://localhost:3000/...` script tags in "View Page Source" that 404 or return something unexpected, this port collision is the first thing to check. +- **Firefox-only CSP override** in `wxt.config.ts`: Firefox's *implicit* default extension-pages CSP includes `upgrade-insecure-requests`, which silently rewrites the WS client's `ws://localhost:8080/ws` connection to `wss://` and breaks it against the plaintext local dev backend (no TLS in dev, deliberately — see backend/CLAUDE.md). Symptom: `Content-Security-Policy: Upgrading insecure request 'ws://...' to use 'wss'` in the console, followed by a failed connection, no other error. Fixed by declaring an explicit `content_security_policy.extension_pages` (otherwise identical to Firefox's own default) for the Firefox build only — an explicit CSP replaces the implicit one entirely, dropping the upgrade directive. Chrome doesn't have this behavior, so the override is gated on `browser === 'firefox'` in the manifest function. If the real deployed backend ever moves to plain `ws://` too (vs. `wss://` behind a real domain), this override needs to travel with it; if the backend gets TLS, this whole override becomes unnecessary and should be removed rather than left as dead configuration. - `src/background/backend-client.ts` — session token storage only (`getSessionToken()`/`storeSessionToken()`/`clearSessionToken()`). Getting a token in the first place is `wallet-auth.ts`'s job. - `src/background/wallet-auth.ts` — runs the REST auth flow (`POST /auth/nonce` → Phantom signature → `POST /auth/verify` → session token) once a content script reports a connected wallet; also `requestWalletReconnect()`, used after the backend invalidates a session. - `src/background/ws-client.ts` — the WS client described above: connects to `/ws?token=...`. `connectWsClient()` returns a controller with `reconnectNow()` so the background script can short-circuit the backoff wait right after a fresh token arrives. Auth failures (`4001` close, `auth_expired`/`session_revoked` errors) do **not** auto-retry with backoff — they call `onAuthExpired()` instead, since retrying with a known-bad token can't succeed; only real disconnects (network drop, backgrounded browser) use the protocol's suggested backoff schedule. diff --git a/wxt.config.ts b/wxt.config.ts index 6251196..4c3e1a2 100644 --- a/wxt.config.ts +++ b/wxt.config.ts @@ -6,7 +6,7 @@ export default defineConfig({ modules: ['@wxt-dev/module-react'], // Target Manifest V3 on both Chromium and Firefox (modern Firefox / Zen support it). manifestVersion: 3, - manifest: { + manifest: ({ browser }) => ({ name: 'Nexa', description: 'Your blockchain powered agent to help with your trading emotions.', permissions: ['storage'], @@ -20,5 +20,15 @@ export default defineConfig({ id: 'nexa-extension@nexa-sol.dev', }, }, - }, + // Firefox's implicit default extension-pages CSP includes + // upgrade-insecure-requests, which silently rewrites our ws:// WS client + // connections to wss:// and breaks them against the plaintext local dev + // backend (no TLS in dev — see backend/CLAUDE.md). Declaring our own CSP + // (identical to the standard default otherwise) replaces Firefox's + // implicit one and drops that directive. Chrome doesn't have this + // behavior, so this is Firefox-only. + ...(browser === 'firefox' + ? { content_security_policy: { extension_pages: "script-src 'self'; object-src 'self'" } } + : {}), + }), });