From 5a7cafdf855cba84d2641200ac8b70ac9f5937f3 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 17 Apr 2026 05:48:43 +0000 Subject: [PATCH] noise: reject non-HEAD on PingResponseHandler chi routes only HEAD to the handler, but assert explicitly so a future router config change cannot silently accept GET/POST and leak latency bytes or side-effects. Updates #3157 --- hscontrol/noise.go | 5 +++++ hscontrol/servertest/ping_test.go | 32 +++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/hscontrol/noise.go b/hscontrol/noise.go index 37ba456a..d8a26cb1 100644 --- a/hscontrol/noise.go +++ b/hscontrol/noise.go @@ -302,6 +302,11 @@ func (h *Headscale) PingResponseHandler( writer http.ResponseWriter, req *http.Request, ) { + if req.Method != http.MethodHead { + http.Error(writer, "method not allowed", http.StatusMethodNotAllowed) + return + } + pingID := req.URL.Query().Get("id") if pingID == "" { http.Error(writer, "missing ping ID", http.StatusBadRequest) diff --git a/hscontrol/servertest/ping_test.go b/hscontrol/servertest/ping_test.go index 4170ce02..3ab5dc04 100644 --- a/hscontrol/servertest/ping_test.go +++ b/hscontrol/servertest/ping_test.go @@ -1,6 +1,8 @@ package servertest_test import ( + "context" + "net/http" "testing" "time" @@ -167,3 +169,33 @@ func TestPingResolveByHostname(t *testing.T) { t.Fatal("ping response not received") } } + +// TestPingResponseHandlerRejectsNonHEAD verifies the endpoint returns 405 +// for method verbs other than HEAD, even when chi is configured to allow +// them. +func TestPingResponseHandlerRejectsNonHEAD(t *testing.T) { + t.Parallel() + + h := servertest.NewHarness(t, 1) + + for _, method := range []string{http.MethodGet, http.MethodPost, http.MethodPut} { + t.Run(method, func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, method, h.Server.URL+"/machine/ping-response?id=x", nil) + require.NoError(t, err) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + // chi may refuse the method entirely; that's equivalent to 405. + return + } + defer resp.Body.Close() + + assert.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode) + }) + } +}