diff --git a/changelog.d/fixes/10720-proxy-password-only-auth.md b/changelog.d/fixes/10720-proxy-password-only-auth.md new file mode 100644 index 0000000000..ca51ccba65 --- /dev/null +++ b/changelog.d/fixes/10720-proxy-password-only-auth.md @@ -0,0 +1 @@ +- fix(proxy): keep password-only proxy credentials instead of dropping them when no username is set (#10720) diff --git a/open-sse/utils/proxyDispatcher.ts b/open-sse/utils/proxyDispatcher.ts index 25d065a2c9..05e7468050 100644 --- a/open-sse/utils/proxyDispatcher.ts +++ b/open-sse/utils/proxyDispatcher.ts @@ -249,9 +249,8 @@ function normalizePort(port: string | number | null | undefined, protocol: strin * listen on these ports, so we must always include the port explicitly. */ function buildProxyUrlString(parsed: URL, port: string): string { - const auth = parsed.username - ? `${parsed.username}${parsed.password ? `:${parsed.password}` : ""}@` - : ""; + const auth = + parsed.username || parsed.password ? `${parsed.username}:${parsed.password}@` : ""; return `${parsed.protocol}//${auth}${parsed.hostname}:${port}`; } @@ -390,9 +389,10 @@ export function proxyConfigToUrl( const port = normalizePort(config.port, protocol); // Build the URL string manually to preserve the port through normalization. - const auth = config.username - ? `${encodeURIComponent(config.username)}:${config.password ? encodeURIComponent(config.password) : ""}@` - : ""; + const auth = + config.username || config.password + ? `${encodeURIComponent(config.username || "")}:${encodeURIComponent(config.password || "")}@` + : ""; const proxyUrlStr = `${type}://${auth}${config.host}:${port}`; diff --git a/tests/unit/probe-10720-proxy-password-only-auth.test.ts b/tests/unit/probe-10720-proxy-password-only-auth.test.ts new file mode 100644 index 0000000000..37c58c55bb --- /dev/null +++ b/tests/unit/probe-10720-proxy-password-only-auth.test.ts @@ -0,0 +1,36 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { proxyConfigToUrl } from "../../open-sse/utils/proxyDispatcher.ts"; + +describe("#10720 — proxyConfigToUrl drops password-only auth (no username)", () => { + it("keeps a password-only credential in the built proxy URL", () => { + const url = proxyConfigToUrl({ + type: "http", + host: "127.0.0.1", + port: 20130, + username: "", + password: "s3cret", + }); + assert.ok(url.includes(":s3cret@"), `expected password in URL, got: ${url}`); + }); + + it("still builds a normal username:password URL", () => { + const url = proxyConfigToUrl({ + type: "http", + host: "127.0.0.1", + port: 20130, + username: "user", + password: "s3cret", + }); + assert.ok(url.includes("user:s3cret@"), `expected user:pass in URL, got: ${url}`); + }); + + it("builds no auth segment when neither username nor password is set", () => { + const url = proxyConfigToUrl({ + type: "http", + host: "127.0.0.1", + port: 20130, + }); + assert.ok(!url.includes("@"), `expected no auth segment, got: ${url}`); + }); +});