package main import ( "bytes" "log/slog" "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // hyperguild#155: since #149 every REST route sits behind BearerMiddleware, // and chassis v0.2.0 refused without logging, so an unknown caller got // silent 401s. The chassis now writes an `mcp auth rejected` line. func TestBuildMuxLogsARESTAuthRejection(t *testing.T) { var buf bytes.Buffer prev := slog.Default() slog.SetDefault(slog.New(slog.NewTextHandler(&buf, nil))) t.Cleanup(func() { slog.SetDefault(prev) }) code, _ := do(t, testMux(t), "POST", "/query", "Bearer wrong") require.Equal(t, http.StatusUnauthorized, code) assert.Contains(t, buf.String(), "mcp auth rejected") assert.Contains(t, buf.String(), "path=/query") assert.NotContains(t, buf.String(), "Bearer wrong", "the credential itself is never logged") } // An authenticated REST request is labelled with the REST mux's pattern: // the inner mux sets it on the same *http.Request the outer middleware reads. func TestMetricsLabelAnAuthenticatedRESTRequestWithItsRoutePattern(t *testing.T) { mux, reg := testMuxAndRegistry(t) code, _ := do(t, mux, "GET", "/pending", "Bearer "+routesTok) require.Equal(t, http.StatusOK, code) rec := httptest.NewRecorder() reg.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/metrics", nil)) assert.Contains(t, rec.Body.String(), `path="GET /pending"`) assert.NotContains(t, rec.Body.String(), `path="/"`) } // hyperguild#155: a request refused by REST auth never reaches the REST mux, // so it was labelled path="/" and a 4xx on /query could not be told from one // on /write. It is now labelled with the REST pattern it was addressed to. func TestMetricsLabelARejectedRESTRequestWithItsRoutePattern(t *testing.T) { mux, reg := testMuxAndRegistry(t) code, _ := do(t, mux, "POST", "/query", "") require.Equal(t, http.StatusUnauthorized, code) rec := httptest.NewRecorder() reg.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/metrics", nil)) assert.Contains(t, rec.Body.String(), `path="POST /query",status="4xx"`) } // A path no REST route matches keeps the catch-all label, so an attacker // probing random URLs cannot grow the series set. func TestMetricsLabelARejectedUnknownPathWithTheCatchAll(t *testing.T) { mux, reg := testMuxAndRegistry(t) code, _ := do(t, mux, "POST", "/no-such-route-zq", "") require.Equal(t, http.StatusUnauthorized, code) rec := httptest.NewRecorder() reg.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/metrics", nil)) assert.Contains(t, rec.Body.String(), `path="/",status="4xx"`) assert.NotContains(t, rec.Body.String(), "no-such-route-zq") }