From 8877bfd11ec7760b115b2d0d7500a45da2749747 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 21 Feb 2026 01:23:21 -0500 Subject: [PATCH] gateway: trust-proxy-aware X-Forwarded-For resolution (#22466) --- src/gateway/net.ts | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/gateway/net.ts b/src/gateway/net.ts index b484f9f0d3b..ab43477963a 100644 --- a/src/gateway/net.ts +++ b/src/gateway/net.ts @@ -146,16 +146,37 @@ function stripOptionalPort(ip: string): string { return ip; } -export function parseForwardedForClientIp(forwardedFor?: string): string | undefined { +export function parseForwardedForClientIp( + forwardedFor?: string, + trustedProxies?: string[], +): string | undefined { const entries = forwardedFor ?.split(",") .map((entry) => entry.trim()) .filter((entry) => entry.length > 0); - const raw = entries?.at(-1); - if (!raw) { + if (!entries?.length) { return undefined; } - return normalizeIp(stripOptionalPort(raw)); + + if (!trustedProxies?.length) { + const raw = entries.at(-1); + if (!raw) { + return undefined; + } + return normalizeIp(stripOptionalPort(raw)); + } + + for (let index = entries.length - 1; index >= 0; index -= 1) { + const normalized = normalizeIp(stripOptionalPort(entries[index])); + if (!normalized) { + continue; + } + if (!isTrustedProxyAddress(normalized, trustedProxies)) { + return normalized; + } + } + + return undefined; } function parseRealIp(realIp?: string): string | undefined { @@ -247,7 +268,10 @@ export function resolveGatewayClientIp(params: { // Fail closed when traffic comes from a trusted proxy but client-origin headers // are missing or invalid. Falling back to the proxy's own IP can accidentally // treat unrelated requests as local/trusted. - return parseForwardedForClientIp(params.forwardedFor) ?? parseRealIp(params.realIp); + return ( + parseForwardedForClientIp(params.forwardedFor, params.trustedProxies) ?? + parseRealIp(params.realIp) + ); } export function isLocalGatewayAddress(ip: string | undefined): boolean {