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
This commit is contained in:
杨艺韬(yangyitao) 2026-03-15 02:39:41 +00:00
parent 5e417b44e1
commit aa54fa9a7b
2 changed files with 26 additions and 1 deletions

View File

@ -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 },
});
});
});

View File

@ -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 },
});
}