package main import ( "net/http" "net/http/httptest" "strings" "testing" "time" ) func TestStripIdentifying(t *testing.T) { h := http.Header{} // Headers that MUST be removed before the request reaches the target. removed := []string{ "X-Weircon-Random-Ip", "X-Weircon-Proxy-Id", "X-Forwarded-For", "X-Forwarded-Host", "X-Forwarded-Proto", "X-Forwarded-Port", "X-Forwarded-Custom-Vendor", // caught by prefix sweep "X-Real-Ip", "Forwarded", "Via", "Cf-Connecting-Ip", "True-Client-Ip", "Fastly-Client-Ip", "X-Original-Forwarded-For", "X-Client-Ip", "X-Weircon-Anything-New", // caught by prefix sweep "From", "Sec-Ch-Ua", "Sec-Ch-Ua-Platform", } for _, k := range removed { h.Set(k, "leak") } // Headers a crawler legitimately sets — these MUST survive untouched. kept := map[string]string{ "User-Agent": "my-crawler/1.0", "Cookie": "session=abc", "Accept": "text/html", "Accept-Language": "en-US", "Referer": "https://example.com", "X-Forward": "not-a-forwarding-header", // does not match "X-Forwarded" prefix } for k, v := range kept { h.Set(k, v) } stripIdentifying(h) for _, k := range removed { if got := h.Get(k); got != "" { t.Errorf("expected %q to be stripped, still present: %q", k, got) } } for k, want := range kept { if got := h.Get(k); got != want { t.Errorf("expected %q to survive as %q, got %q", k, want, got) } } } func TestAnonymizeOverridesUserAgent(t *testing.T) { for _, ua := range []string{"crawlkit-roles", "python-requests/2.32", "Go-http-client/1.1", ""} { h := http.Header{} if ua != "" { h.Set("User-Agent", ua) } h.Set("X-Forwarded-For", "10.0.0.1") anonymize(h, defaultUserAgent) if got := h.Values("User-Agent"); len(got) != 1 || got[0] != defaultUserAgent { t.Errorf("caller UA %q: outbound User-Agent = %q, want only the browser UA", ua, got) } if h.Get("X-Forwarded-For") != "" { t.Errorf("anonymize must still strip forwarding headers") } } } func TestDefaultUserAgentIsBrowser(t *testing.T) { if !strings.HasPrefix(defaultUserAgent, "Mozilla/5.0 (") || !strings.Contains(defaultUserAgent, "Firefox/") { t.Errorf("default UA should be a plain Firefox string, got %q", defaultUserAgent) } } // End to end: what the TARGET receives when a careless client goes through the // full handler. This is the no-leak contract, not just the helper. func TestHandlerTargetSeesOnlyBrowser(t *testing.T) { var got http.Header target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { got = r.Header.Clone() })) defer target.Close() cfg := Config{UserAgent: defaultUserAgent} mgr := newProxyManager(1, 0, 0, time.Minute) h := newHandler(cfg, []*http.Transport{{}}, mgr) req := httptest.NewRequest(http.MethodGet, "/", nil) req.Header.Set("X-Weircon-Random-Ip", "secret-key") req.Header.Set("X-Weircon-Random-Ip-Redirect", target.URL+"/x") req.Header.Set("User-Agent", "crawlkit-roles") req.Header.Set("From", "someone@example.com") req.Header.Set("X-Forwarded-For", "192.168.10.144") req.Header.Set("X-Real-Ip", "192.168.10.144") req.Header.Set("Sec-Ch-Ua", `"Chromium";v="125"`) rec := httptest.NewRecorder() h.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status %d: %s", rec.Code, rec.Body.String()) } if ua := got.Values("User-Agent"); len(ua) != 1 || ua[0] != defaultUserAgent { t.Errorf("target saw User-Agent %q", ua) } for k, v := range got { lk := strings.ToLower(k + ":" + strings.Join(v, ",")) for _, bad := range []string{"weircon", "crawlkit", "secret-key", "192.168.", "example.com", "forwarded", "sec-ch-ua", "from:"} { if strings.Contains(lk, bad) { t.Errorf("target saw identifying header %s: %v", k, v) } } } }