import { describe, expect, test } from "vitest"; import { CLIENT_IP_HEADER, clientIp, rateLimitSubject, withClientIp } from "@/lib/rate-limit"; const h = (values: Record) => new Headers(values); describe("clientIp hinter Traefik", () => { test("Gegenstelle ist der letzte Eintrag in X-Forwarded-For, nie ein Eintrag des Clients", () => { expect(clientIp(h({ "x-forwarded-for": "1.2.3.4, 198.51.100.9", "x-real-ip": "1.2.3.4" }))).toBe("198.51.100.9"); }); test("über Cloudflare gefälschtes X-Real-IP zählt nicht, CF-Connecting-IP nur von Cloudflare-Edges", () => { const viaCloudflare = { "x-real-ip": "1.2.3.4", "x-forwarded-for": "1.2.3.4, 203.0.113.7, 172.70.1.2", "cf-connecting-ip": "203.0.113.7" }; expect(clientIp(h(viaCloudflare))).toBe("203.0.113.7"); expect(clientIp(h({ "x-forwarded-for": "198.51.100.9", "cf-connecting-ip": "203.0.113.7" }))).toBe("198.51.100.9"); }); test("ohne X-Forwarded-For (lokal) gilt X-Real-IP, ungültige Werte ergeben „unknown“", () => { expect(clientIp(h({ "x-real-ip": "198.51.100.9" }))).toBe("198.51.100.9"); expect(clientIp(h({}))).toBe("unknown"); expect(clientIp(h({ "x-forwarded-for": "1.2.3.4, kein-ip" }))).toBe("unknown"); }); test("Rate-Limits zählen IPv6 je /64-Netz, IPv4-gemappte Adressen wie IPv4", () => { expect(rateLimitSubject(clientIp(h({ "x-forwarded-for": "2001:db8:1:2:aaaa::1" })))).toBe("2001:db8:1:2::/64"); expect(rateLimitSubject("2001:0DB8:0001:0002:ffff:ffff:ffff:ffff")).toBe("2001:db8:1:2::/64"); expect(rateLimitSubject("2001:db8:1:3::1")).not.toBe(rateLimitSubject("2001:db8:1:2::1")); expect(rateLimitSubject("::ffff:198.51.100.9")).toBe("198.51.100.9"); expect(rateLimitSubject("198.51.100.9")).toBe("198.51.100.9"); }); test("better-auth bekommt eine einzelne gültige IP (es normalisiert IPv6 selbst auf /64)", () => { const request = new Request("https://example.org/api/auth/sign-in/email", { headers: { "x-forwarded-for": "2001:db8:1:2:aaaa::1" } }); expect(withClientIp(request).headers.get(CLIENT_IP_HEADER)).toBe("2001:db8:1:2:aaaa::1"); }); test("withClientIp setzt den Header für better-auth und überschreibt einen mitgeschickten Wert", () => { const request = new Request("https://example.org/api/auth/sign-in/email", { method: "POST", headers: { "x-forwarded-for": "1.2.3.4, 198.51.100.9", [CLIENT_IP_HEADER]: "1.2.3.4" }, body: "{}", }); expect(withClientIp(request).headers.get(CLIENT_IP_HEADER)).toBe("198.51.100.9"); }); });