From d8ee545002ab53cc5d1fc673ef0223f52cae4504 Mon Sep 17 00:00:00 2001 From: jubnl Date: Sun, 5 Apr 2026 05:59:09 +0200 Subject: [PATCH] fix(ssrf): handle Node 20+ Happy Eyeballs dns lookup signature in pinned agent Node 20+ enables autoSelectFamily by default, causing internal dns lookups to be called with `all: true`. This expects the callback to receive an array of address objects instead of a flat (address, family) pair, causing webhook requests to fail with "Invalid IP address: undefined". --- server/src/utils/ssrfGuard.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/server/src/utils/ssrfGuard.ts b/server/src/utils/ssrfGuard.ts index 6927a1d..afaf201 100644 --- a/server/src/utils/ssrfGuard.ts +++ b/server/src/utils/ssrfGuard.ts @@ -112,10 +112,15 @@ export async function checkSsrf(rawUrl: string, bypassInternalIpAllowed: boolean */ export function createPinnedAgent(resolvedIp: string, protocol: string): http.Agent | https.Agent { const options = { - lookup: (_hostname: string, _opts: unknown, callback: (err: Error | null, addr: string, family: number) => void) => { + lookup: (_hostname: string, opts: Record, callback: Function) => { // Determine address family from IP format const family = resolvedIp.includes(':') ? 6 : 4; - callback(null, resolvedIp, family); + // Node.js 18+ may call lookup with `all: true`, expecting an array of address objects + if (opts && opts.all) { + callback(null, [{ address: resolvedIp, family }]); + } else { + callback(null, resolvedIp, family); + } }, }; return protocol === 'https:' ? new https.Agent(options) : new http.Agent(options);