package api import ( "fmt" "net/http/httptest" "testing" ) func TestClientIPUsesRemoteAddrNotXFF(t *testing.T) { r := httptest.NewRequest("POST", "/api/pastes", nil) r.RemoteAddr = "203.0.113.7:4432" r.Header.Set("X-Forwarded-For", "1.2.3.4, 1.2.3.5, 203.0.113.9") r.Header.Set("X-Real-Ip", "203.0.113.10") if got := clientIP(r); got != "203.0.113.7" { t.Fatalf("clientIP = %q, want peer 203.0.113.7", got) } } // No client-controlled IP header is ever honored, including proxy-typical // ones when set by an attacker. func TestClientIPNeverTrustsHeaders(t *testing.T) { for _, h := range []struct{ name, val string }{ {"CF-Connecting-IP", "198.51.100.9"}, {"X-Forwarded-For", "198.51.100.1"}, {"X-Real-Ip", "198.51.100.2"}, } { r := httptest.NewRequest("POST", "/api/pastes", nil) r.RemoteAddr = "10.0.1.47:9999" r.Header.Set(h.name, h.val) if got := clientIP(r); got != "10.0.1.47" { t.Fatalf("%s header: clientIP = %q, want peer 10.0.1.47", h.name, got) } } } // Issue #280: rotating X-Forwarded-For must NOT reset the bucket. Pentest // repro was 8 creates with rotating XFF -> 6x201. func TestRotatingXFFDoesNotResetBucket(t *testing.T) { globalLimiter = newLimiter() s := defaultSettings(Config{}) var allowed, limited int for i := 0; i < 8; i++ { r := httptest.NewRequest("POST", "/api/pastes", nil) r.RemoteAddr = "198.51.100.1:5000" r.Header.Set("X-Forwarded-For", fmt.Sprintf("9.9.9.%d", i)) if rateLimitCreate(r, s) { allowed++ } else { limited++ } } if float64(allowed) != s.RateLimitBurst || limited != 8-int(s.RateLimitBurst) { t.Fatalf("rotating XFF: allowed=%d limited=%d, want allowed=%v (burst), limited=%d", allowed, limited, s.RateLimitBurst, 8-int(s.RateLimitBurst)) } }