From c9da50f9be5667f781135a33592abb1c24d9f1e7 Mon Sep 17 00:00:00 2001 From: Kriday Dave Date: Fri, 17 Jul 2026 22:29:42 +0530 Subject: [PATCH] Normalize protocol-relative remote host input as https (#3971) Ported-from: t3code c49d424e3 --- packages/shared/src/remote.test.ts | 64 ++++++++++++++++++++++++++++++ packages/shared/src/remote.ts | 8 ++-- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/packages/shared/src/remote.test.ts b/packages/shared/src/remote.test.ts index 50f4d381c56..d16b22eb46a 100644 --- a/packages/shared/src/remote.test.ts +++ b/packages/shared/src/remote.test.ts @@ -53,6 +53,70 @@ describe("remote", () => { }); }); + it("treats a protocol-relative host as https", () => { + expect( + resolveRemotePairingTarget({ + host: "//remote.example.com", + pairingCode: "pairing-token", + }), + ).toEqual({ + credential: "pairing-token", + httpBaseUrl: "https://remote.example.com/", + wsBaseUrl: "wss://remote.example.com/", + }); + }); + + it("preserves the port when normalizing a protocol-relative host", () => { + expect( + resolveRemotePairingTarget({ + host: "//remote.example.com:3000", + pairingCode: "pairing-token", + }), + ).toEqual({ + credential: "pairing-token", + httpBaseUrl: "https://remote.example.com:3000/", + wsBaseUrl: "wss://remote.example.com:3000/", + }); + }); + + it("normalizes a protocol-relative host from a hosted pairing link", () => { + expect( + resolveRemotePairingTarget({ + pairingUrl: "https://app.t3.codes/pair?host=%2F%2Fremote.example.com#token=pairing-token", + }), + ).toEqual({ + credential: "pairing-token", + httpBaseUrl: "https://remote.example.com/", + wsBaseUrl: "wss://remote.example.com/", + }); + }); + + it("collapses extra leading slashes instead of producing an empty host", () => { + expect( + resolveRemotePairingTarget({ + host: "///example.com", + pairingCode: "pairing-token", + }), + ).toEqual({ + credential: "pairing-token", + httpBaseUrl: "https://example.com/", + wsBaseUrl: "wss://example.com/", + }); + }); + + it("does not double-prepend https when the host already carries a scheme", () => { + expect( + resolveRemotePairingTarget({ + host: "//https://example.com", + pairingCode: "pairing-token", + }), + ).toEqual({ + credential: "pairing-token", + httpBaseUrl: "https://example.com/", + wsBaseUrl: "wss://example.com/", + }); + }); + it("preserves host ports when normalizing a bare host input", () => { expect( resolveRemotePairingTarget({ diff --git a/packages/shared/src/remote.ts b/packages/shared/src/remote.ts index c2d6079680d..72edf1702ae 100644 --- a/packages/shared/src/remote.ts +++ b/packages/shared/src/remote.ts @@ -11,10 +11,10 @@ const normalizeRemoteBaseUrl = (rawValue: string): URL => { throw new Error("Enter a backend URL."); } - const normalizedInput = - /^[a-zA-Z][a-zA-Z\d+-]*:\/\//.test(trimmed) || trimmed.startsWith("//") - ? trimmed - : `https://${trimmed}`; + const withoutLeadingSlashes = trimmed.replace(/^\/+/, ""); + const normalizedInput = /^[a-zA-Z][a-zA-Z\d+-]*:\/\//.test(withoutLeadingSlashes) + ? withoutLeadingSlashes + : `https://${withoutLeadingSlashes}`; const url = new URL(normalizedInput); url.pathname = "/"; url.search = "";