From 5b7b1dfd094bb93c63c40003ff53b0474a6e001b Mon Sep 17 00:00:00 2001 From: Jordan Wesolowski Date: Wed, 29 Apr 2026 10:26:41 -0600 Subject: [PATCH 1/2] fix(net): honor Host header override by setting req.Host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Go's net/http ignores req.Header.Set("Host", ...); the value must be assigned to req.Host. Previously, putting `Host` in a target's `headers` array had no effect on the wire — the server saw the URL's hostname. This broke the common probe pattern of hitting a load balancer directly while overriding Host for virtual-host routing (ingress controllers, ALB listener rules, etc.), which curl supports via `--header 'Host: ...'`. Detect Host (case-insensitive) in the headers loop and assign req.Host; keep recording it in RequestHeaders so logs reflect what was sent. --- net/net.go | 6 +++++- net/net_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/net/net.go b/net/net.go index cf37aaa..a987587 100644 --- a/net/net.go +++ b/net/net.go @@ -320,7 +320,11 @@ func makeHTTPRequest(urlStr string, options HTTPRequestOptions, config NetworkCo } for name, value := range options.Headers { - req.Header.Set(name, value) + if strings.EqualFold(name, "Host") { + req.Host = value + } else { + req.Header.Set(name, value) + } result.RequestHeaders.Set(name, value) } diff --git a/net/net_test.go b/net/net_test.go index efd11c8..38df18d 100644 --- a/net/net_test.go +++ b/net/net_test.go @@ -380,6 +380,38 @@ func TestCheckWebsiteWithHeaders(t *testing.T) { } } +func TestCheckWebsiteWithHostHeader(t *testing.T) { + const wantHost = "virtual.example.com" + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Host != wantHost { + w.WriteHeader(400) + _, _ = w.Write([]byte(r.Host)) + return + } + w.WriteHeader(200) + _, _ = w.Write([]byte("OK")) + })) + defer server.Close() + + config := NetworkConfig{ + Timeout: 5 * time.Second, + Headers: []string{"Host: " + wantHost}, + } + + result := CheckWebsite(server.URL, config) + + if !result.IsUp { + t.Errorf("CheckWebsite() with Host header should succeed, got status %d body %q", result.StatusCode, result.ResponseBody) + } + if result.StatusCode != 200 { + t.Errorf("CheckWebsite() StatusCode = %d, want 200", result.StatusCode) + } + if got := result.RequestHeaders.Get("Host"); got != wantHost { + t.Errorf("RequestHeaders Host = %q, want %q", got, wantHost) + } +} + func TestCheckWebsiteBodyLimit(t *testing.T) { tests := []struct { name string From 65e8d230e791171cc64f133da508363d73a002c9 Mon Sep 17 00:00:00 2001 From: Jordan Wesolowski Date: Wed, 29 Apr 2026 10:32:25 -0600 Subject: [PATCH 2/2] test: drop reflective body write to satisfy gosec G705 golangci-lint flagged writing r.Host to the response as a taint-based XSS. The body was only for debug failures; the status code already makes assertions clear. --- net/net_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/net_test.go b/net/net_test.go index 38df18d..a68d4d2 100644 --- a/net/net_test.go +++ b/net/net_test.go @@ -386,7 +386,6 @@ func TestCheckWebsiteWithHostHeader(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Host != wantHost { w.WriteHeader(400) - _, _ = w.Write([]byte(r.Host)) return } w.WriteHeader(200) @@ -402,7 +401,7 @@ func TestCheckWebsiteWithHostHeader(t *testing.T) { result := CheckWebsite(server.URL, config) if !result.IsUp { - t.Errorf("CheckWebsite() with Host header should succeed, got status %d body %q", result.StatusCode, result.ResponseBody) + t.Errorf("CheckWebsite() with Host header should succeed, got status %d", result.StatusCode) } if result.StatusCode != 200 { t.Errorf("CheckWebsite() StatusCode = %d, want 200", result.StatusCode)