package webui import ( "net/http" "net/http/httptest" "testing" ) func TestRequestIPTrustedProxy(t *testing.T) { a := &App{trustedProxies: parseTrustedProxies("10.0.0.0/8", nil)} cases := []struct { name string remoteAddr string headers map[string]string want string }{ { name: "untrusted RemoteAddr ignores X-Forwarded-For entirely", remoteAddr: "203.0.113.5:12345", headers: map[string]string{"X-Forwarded-For": "1.2.3.4"}, want: "203.0.113.5", // a client with nothing in front of it can't spoof its own IP }, { name: "trusted proxy: X-Forwarded-For honored", remoteAddr: "10.0.0.1:12345", headers: map[string]string{"X-Forwarded-For": "198.51.100.9"}, want: "198.51.100.9", }, { name: "trusted proxy: walks from the right, skipping other trusted hops", remoteAddr: "10.0.0.1:12345", headers: map[string]string{"X-Forwarded-For": "198.51.100.9, 10.0.0.2"}, want: "198.51.100.9", // 10.0.0.2 is itself trusted (in 10.0.0.0/8) — skip it, land on the real client }, { name: "trusted proxy: CF-Connecting-IP preferred over X-Forwarded-For", remoteAddr: "10.0.0.1:12345", headers: map[string]string{"CF-Connecting-IP": "198.51.100.9", "X-Forwarded-For": "attacker-spoofed-should-be-ignored"}, want: "198.51.100.9", }, { name: "no trusted_proxies configured: header ignored even from that same peer", remoteAddr: "203.0.113.5:12345", headers: map[string]string{"X-Real-IP": "1.2.3.4"}, want: "203.0.113.5", }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", nil) req.RemoteAddr = tc.remoteAddr for k, v := range tc.headers { req.Header.Set(k, v) } if got := a.requestIP(req); got != tc.want { t.Errorf("requestIP() = %q, want %q", got, tc.want) } }) } } func TestParseTrustedProxiesCloudflarePreset(t *testing.T) { prefixes := parseTrustedProxies("cloudflare", nil) if len(prefixes) != len(cloudflareRanges) { t.Fatalf("expected %d cloudflare ranges parsed, got %d", len(cloudflareRanges), len(prefixes)) } } func TestParseTrustedProxiesBareIP(t *testing.T) { prefixes := parseTrustedProxies("192.0.2.10", nil) if len(prefixes) != 1 { t.Fatalf("expected 1 prefix, got %d", len(prefixes)) } if prefixes[0].Bits() != 32 { t.Fatalf("expected a bare IPv4 to become a /32, got /%d", prefixes[0].Bits()) } }