diff --git a/docs/internals/t3-connect.md b/docs/internals/t3-connect.md index 4c9bbcb780bee..1c9046124d826 100644 --- a/docs/internals/t3-connect.md +++ b/docs/internals/t3-connect.md @@ -168,8 +168,10 @@ The version comes from running `cloudflared version`, not from the folder name. Connectors from before `--no-autoupdate` replaced themselves in place, so a managed folder can hold another release. Such a binary still runs as a fallback while the pin installs. Every binary needs `CLOUDFLARED_MIN_VERSION`, the oldest -release that accepts every flag the connector is started with. Raise it whenever -a new flag is added. +release that accepts every flag the connector is started with and rejects the +compromised eMudhra intermediates. No binary below it runs, even as a fallback, +so a host without an eligible one stays off the relay until it has one. Raise it +whenever a new flag or security fix needs it. ## OAuth traps diff --git a/packages/shared/src/relayClient.test.ts b/packages/shared/src/relayClient.test.ts index 0a987832ff749..2c86930dbd7b7 100644 --- a/packages/shared/src/relayClient.test.ts +++ b/packages/shared/src/relayClient.test.ts @@ -497,7 +497,7 @@ describe("RelayClient", () => { ), ); - it.effect.skipIf(windowsHost)("skips a PATH binary older than the minimum version", () => { + it.effect.skipIf(windowsHost)("skips binaries older than the minimum version", () => { const versions: Record = {}; return run( (baseDir) => @@ -506,8 +506,11 @@ describe("RelayClient", () => { const newPath = `${baseDir}/new/cloudflared`; yield* writeExecutable(oldPath); yield* writeExecutable(newPath); - versions[oldPath] = "2023.8.2"; - versions[newPath] = RelayClient.CLOUDFLARED_MIN_VERSION; + const managed = managedPathFor(baseDir, "2026.10.0"); + yield* writeExecutable(managed); + versions[managed] = "2026.10.0"; + versions[oldPath] = "2026.10.2"; + versions[newPath] = "2026.10.3"; const manager = yield* RelayClient.makeCloudflaredRelayClient({ baseDir }); const env = { PATH: `${baseDir}/old:${baseDir}/new` }; expect( @@ -536,7 +539,7 @@ describe("RelayClient", () => { Effect.gen(function* () { const overridePath = `${baseDir}/override/cloudflared`; yield* writeExecutable(overridePath); - versions[overridePath] = "2025.6.0"; + versions[overridePath] = "2026.10.2"; const manager = yield* RelayClient.makeCloudflaredRelayClient({ baseDir }); const env = { PATH: "", T3CODE_CLOUDFLARED_PATH: overridePath }; const withEnv = Effect.provideService( @@ -569,9 +572,9 @@ describe("RelayClient", () => { const newerServer = managedPathFor(baseDir, "2099.1.0"); versions[newerServer] = "2099.1.0"; yield* writeExecutable(newerServer); - versions[pathBinary] = "2026.9.3"; + versions[pathBinary] = "2026.10.3"; versions[older] = "2025.9.0"; - versions[newer] = "2026.1.0"; + versions[newer] = "2026.10.3"; yield* writeExecutable(pathBinary); yield* writeExecutable(older); yield* writeExecutable(newer); @@ -584,7 +587,7 @@ describe("RelayClient", () => { Effect.provideService(HostProcess.Environment, { PATH: `${baseDir}/bin` }), ); - expect(yield* resolveWith).toMatchObject({ source: "managed", version: "2026.1.0" }); + expect(yield* resolveWith).toMatchObject({ source: "managed", version: "2026.10.3" }); yield* writeExecutable(pinned); expect(yield* resolveWith).toMatchObject({ @@ -599,11 +602,11 @@ describe("RelayClient", () => { ).toEqual(["2026.1.0", RelayClient.CLOUDFLARED_VERSION, "2099.1.0"]); yield* fileSystem.remove(pinned); - expect(yield* resolveWith).toMatchObject({ source: "managed", version: "2026.1.0" }); + expect(yield* resolveWith).toMatchObject({ source: "managed", version: "2026.10.3" }); yield* fileSystem.remove(`${baseDir}/tools/cloudflared/2026.1.0`, { recursive: true, }); - expect(yield* resolveWith).toMatchObject({ source: "path", version: "2026.9.3" }); + expect(yield* resolveWith).toMatchObject({ source: "path", version: "2026.10.3" }); }), { versions }, ); @@ -618,7 +621,7 @@ describe("RelayClient", () => { (baseDir) => Effect.gen(function* () { const pinned = managedPathFor(baseDir, RelayClient.CLOUDFLARED_VERSION); - versions[pinned] = "2026.9.3"; + versions[pinned] = "2026.10.3"; yield* writeExecutable(pinned); const manager = yield* RelayClient.makeCloudflaredRelayClient({ baseDir }); const resolved = yield* manager.resolve; @@ -626,7 +629,7 @@ describe("RelayClient", () => { status: "available", executablePath: pinned, source: "managed", - version: "2026.9.3", + version: "2026.10.3", }); expect( resolved.status === "available" && RelayClient.isPinnedManagedRelayClient(resolved), diff --git a/packages/shared/src/relayClient.ts b/packages/shared/src/relayClient.ts index 5933ea7e6a09c..c5c0d7a82f3d6 100644 --- a/packages/shared/src/relayClient.ts +++ b/packages/shared/src/relayClient.ts @@ -25,8 +25,8 @@ import { isWindowsRenameLock } from "./atomicWrite.ts"; import * as HostProcess from "./HostProcess.ts"; export const CLOUDFLARED_VERSION = "2026.10.4"; -// Minimum for --output support; older connectors exit on that flag. -export const CLOUDFLARED_MIN_VERSION = "2025.6.1"; +// Minimum for --output support and the eMudhra fix, which 2026.10.2 macOS builds lack. +export const CLOUDFLARED_MIN_VERSION = "2026.10.3"; const CLOUDFLARED_PATH_ENV_NAME = "T3CODE_CLOUDFLARED_PATH"; const CLOUDFLARED_DOWNLOAD_TIMEOUT = "10 minutes";