From a20ba749783513510240cf5e60ed8639c3f39d80 Mon Sep 17 00:00:00 2001 From: Teddy Tennant Date: Fri, 20 Mar 2026 15:45:06 -0400 Subject: [PATCH] test: add SSRF guard coverage for URL credential bypass vectors (#50523) * security: add SSRF guard tests for URL credential bypass vectors * test(security): strengthen SSRF redirect guard coverage --------- Co-authored-by: Vincent Koc --- src/infra/net/fetch-guard.ssrf.test.ts | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/infra/net/fetch-guard.ssrf.test.ts b/src/infra/net/fetch-guard.ssrf.test.ts index f90df5271f1..dc57971af4b 100644 --- a/src/infra/net/fetch-guard.ssrf.test.ts +++ b/src/infra/net/fetch-guard.ssrf.test.ts @@ -278,6 +278,40 @@ describe("fetchWithSsrFGuard hardening", () => { }); }); + it("blocks URLs that use credentials to obscure a private host", async () => { + const fetchImpl = vi.fn(); + // http://attacker.com@127.0.0.1:8080/ — URL parser extracts hostname as 127.0.0.1 + await expect( + fetchWithSsrFGuard({ + url: "http://attacker.com@127.0.0.1:8080/internal", + fetchImpl, + }), + ).rejects.toThrow(/private|internal|blocked/i); + expect(fetchImpl).not.toHaveBeenCalled(); + }); + + it("blocks private IPv6 addresses embedded in URLs with credentials", async () => { + const fetchImpl = vi.fn(); + await expect( + fetchWithSsrFGuard({ + url: "http://user:pass@[::1]:8080/internal", + fetchImpl, + }), + ).rejects.toThrow(/private|internal|blocked/i); + expect(fetchImpl).not.toHaveBeenCalled(); + }); + + it("blocks redirect to a URL using credentials to obscure a private host", async () => { + const lookupFn = createPublicLookup(); + const fetchImpl = await expectRedirectFailure({ + url: "https://public.example/start", + responses: [redirectResponse("http://public@127.0.0.1:6379/")], + expectedError: /private|internal|blocked/i, + lookupFn, + }); + expect(fetchImpl).toHaveBeenCalledTimes(1); + }); + it("ignores env proxy by default to preserve DNS-pinned destination binding", async () => { await runProxyModeDispatcherTest({ mode: GUARDED_FETCH_MODE.STRICT,