diff --git a/apps/web/src/environments/primary/bootstrap.test.ts b/apps/web/src/environments/primary/bootstrap.test.ts index e200ecf2ff87..b08717d7c413 100644 --- a/apps/web/src/environments/primary/bootstrap.test.ts +++ b/apps/web/src/environments/primary/bootstrap.test.ts @@ -155,6 +155,24 @@ describe("environmentBootstrap", () => { }); }); + it("keeps an uppercase wss scheme secure when deriving the http url", () => { + vi.stubEnv("VITE_WS_URL", "WSS://remote.example.com"); + + expect(readPrimaryEnvironmentTarget().target).toEqual({ + httpBaseUrl: "https://remote.example.com/", + wsBaseUrl: "wss://remote.example.com/", + }); + }); + + it("keeps an uppercase https scheme secure when deriving the websocket url", () => { + vi.stubEnv("VITE_HTTP_URL", "HTTPS://remote.example.com"); + + expect(readPrimaryEnvironmentTarget().target).toEqual({ + httpBaseUrl: "https://remote.example.com/", + wsBaseUrl: "wss://remote.example.com/", + }); + }); + it("uses the current origin as the descriptor base for local dev environments", async () => { installTestBrowser("http://localhost:5735/"); await installDescriptorApi(); diff --git a/apps/web/src/environments/primary/target.ts b/apps/web/src/environments/primary/target.ts index cc002419fe78..5aa957e8665d 100644 --- a/apps/web/src/environments/primary/target.ts +++ b/apps/web/src/environments/primary/target.ts @@ -189,14 +189,18 @@ function resolveConfiguredPrimaryTarget(): PrimaryEnvironmentTarget | null { return null; } + // Scheme checks run on the raw configured string, while the URL parser + // folds schemes to lowercase ("WSS://host" parses fine). Without the + // case folding an uppercase scheme would be classified as plaintext and + // swapped to http/ws, silently downgrading TLS. const resolvedHttpBaseUrl = configuredHttpBaseUrl ?? - (configuredWsBaseUrl?.startsWith("wss:") + (configuredWsBaseUrl?.toLowerCase().startsWith("wss:") ? swapBaseUrlProtocol(configuredWsBaseUrl, "https:", "websocket-base-url") : swapBaseUrlProtocol(configuredWsBaseUrl!, "http:", "websocket-base-url")); const resolvedWsBaseUrl = configuredWsBaseUrl ?? - (configuredHttpBaseUrl?.startsWith("https:") + (configuredHttpBaseUrl?.toLowerCase().startsWith("https:") ? swapBaseUrlProtocol(configuredHttpBaseUrl, "wss:", "http-base-url") : swapBaseUrlProtocol(configuredHttpBaseUrl!, "ws:", "http-base-url"));