From aa54fa9a7b9262adffa59fd338e4daaf72d3611b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E8=89=BA=E9=9F=AC=28yangyitao=29?= Date: Sun, 15 Mar 2026 02:39:41 +0000 Subject: [PATCH] fix(ssrf): pass pinned DNS lookup to ProxyAgent via requestTls createPinnedDispatcher() passed the SSRF-safe pinned lookup to Agent and EnvHttpProxyAgent but skipped it for ProxyAgent (explicit-proxy mode). This meant DNS resolution for origin servers behind an explicit proxy bypassed the pinned hostname check, potentially allowing DNS rebinding. Pass the pinned lookup in requestTls for ProxyAgent so all dispatcher modes enforce DNS pinning consistently. Closes #46685 --- src/infra/net/ssrf.dispatcher.test.ts | 20 ++++++++++++++++++++ src/infra/net/ssrf.ts | 7 ++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/infra/net/ssrf.dispatcher.test.ts b/src/infra/net/ssrf.dispatcher.test.ts index 27060cdb89e..3303bf8ffaa 100644 --- a/src/infra/net/ssrf.dispatcher.test.ts +++ b/src/infra/net/ssrf.dispatcher.test.ts @@ -210,9 +210,29 @@ describe("createPinnedDispatcher", () => { expect(proxyAgentCtor).toHaveBeenCalledWith({ uri: "http://127.0.0.1:7890", + requestTls: { lookup }, proxyTls: { autoSelectFamily: false, }, }); }); + + it("passes pinned lookup via requestTls when proxyTls is absent", () => { + const lookup = vi.fn() as unknown as PinnedHostname["lookup"]; + const pinned: PinnedHostname = { + hostname: "api.telegram.org", + addresses: ["149.154.167.220"], + lookup, + }; + + createPinnedDispatcher(pinned, { + mode: "explicit-proxy", + proxyUrl: "http://127.0.0.1:7890", + }); + + expect(proxyAgentCtor).toHaveBeenCalledWith({ + uri: "http://127.0.0.1:7890", + requestTls: { lookup }, + }); + }); }); diff --git a/src/infra/net/ssrf.ts b/src/infra/net/ssrf.ts index fd633fcb20d..2e053885b46 100644 --- a/src/infra/net/ssrf.ts +++ b/src/infra/net/ssrf.ts @@ -416,11 +416,16 @@ export function createPinnedDispatcher( } const proxyUrl = policy.proxyUrl.trim(); + // Always pass the pinned lookup via requestTls so DNS resolution for the + // origin server goes through the SSRF-safe pinned lookup, not the default + // resolver. Without this, ProxyAgent bypasses DNS pinning (#46685). + const requestTls = withPinnedLookup(pinned.lookup); if (!policy.proxyTls) { - return new ProxyAgent(proxyUrl); + return new ProxyAgent({ uri: proxyUrl, requestTls }); } return new ProxyAgent({ uri: proxyUrl, + requestTls, proxyTls: { ...policy.proxyTls }, }); }