VYPR
Moderate severityNVD Advisory· Published Mar 19, 2026· Updated Mar 25, 2026

OpenClaw < 2026.2.21 - Client IP Spoofing via X-Forwarded-For Header Parsing

CVE-2026-32029

Description

OpenClaw versions prior to 2026.2.21 improperly parse the left-most X-Forwarded-For header value when requests originate from configured trusted proxies, allowing attackers to spoof client IP addresses. In proxy chains that append or preserve header values, attackers can inject malicious header content to influence security decisions including authentication rate-limiting and IP-based access controls.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
openclawnpm
< 2026.2.212026.2.21

Affected products

1

Patches

2
8877bfd11ec7

gateway: trust-proxy-aware X-Forwarded-For resolution (#22466)

https://github.com/openclaw/openclawVincent KocFeb 21, 2026via ghsa
1 file changed · +29 5
  • src/gateway/net.ts+29 5 modified
    @@ -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 {
    
07039dc089e5

Gateway: harden trusted proxy X-Forwarded-For parsing (#22429)

https://github.com/openclaw/openclawVincent KocFeb 21, 2026via ghsa
3 files changed · +16 2
  • CHANGELOG.md+1 0 modified
    @@ -108,6 +108,7 @@ Docs: https://docs.openclaw.ai
     - iOS/Watch: refresh iOS and watch app icon assets with the lobster icon set to keep phone/watch branding aligned. (#21997) Thanks @mbelinky.
     - CLI/Onboarding: fix Anthropic-compatible custom provider verification by normalizing base URLs to avoid duplicate `/v1` paths during setup checks. (#21336) Thanks @17jmumford.
     - Security/Dependencies: bump transitive `hono` usage to `4.11.10` to incorporate timing-safe authentication comparison hardening for `basicAuth`/`bearerAuth` (`GHSA-gq3j-xvxp-8hrf`). Thanks @vincentkoc.
    +- Security/Gateway: parse `X-Forwarded-For` with trust-preserving semantics when requests come from configured trusted proxies, preventing proxy-chain spoofing from influencing client IP classification and rate-limit identity. Thanks @AnthonyDiSanti and @vincentkoc.
     - iOS/Gateway/Tools: prefer uniquely connected node matches when duplicate display names exist, surface actionable `nodes invoke` pairing-required guidance with request IDs, and refresh active iOS gateway registration after location-capability setting changes so capability updates apply immediately. (#22120) thanks @mbelinky.
     
     ## 2026.2.19
    
  • src/gateway/net.test.ts+10 1 modified
    @@ -145,12 +145,21 @@ describe("resolveGatewayClientIp", () => {
       it("returns forwarded client IP when the remote is a trusted proxy", () => {
         const ip = resolveGatewayClientIp({
           remoteAddr: "127.0.0.1",
    -      forwardedFor: "10.0.0.2, 127.0.0.1",
    +      forwardedFor: "127.0.0.1, 10.0.0.2",
           trustedProxies: ["127.0.0.1"],
         });
         expect(ip).toBe("10.0.0.2");
       });
     
    +  it("does not trust the left-most X-Forwarded-For value when behind a trusted proxy", () => {
    +    const ip = resolveGatewayClientIp({
    +      remoteAddr: "127.0.0.1",
    +      forwardedFor: "198.51.100.99, 10.0.0.9, 127.0.0.1",
    +      trustedProxies: ["127.0.0.1"],
    +    });
    +    expect(ip).toBe("127.0.0.1");
    +  });
    +
       it("fails closed when trusted proxy headers are missing", () => {
         const ip = resolveGatewayClientIp({
           remoteAddr: "127.0.0.1",
    
  • src/gateway/net.ts+5 1 modified
    @@ -147,7 +147,11 @@ function stripOptionalPort(ip: string): string {
     }
     
     export function parseForwardedForClientIp(forwardedFor?: string): string | undefined {
    -  const raw = forwardedFor?.split(",")[0]?.trim();
    +  const entries = forwardedFor
    +    ?.split(",")
    +    .map((entry) => entry.trim())
    +    .filter((entry) => entry.length > 0);
    +  const raw = entries?.at(-1);
       if (!raw) {
         return undefined;
       }
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.