Honor RoundTripper contract in middleware; validate incoming X-Request-Id
BearerAuth, BasicAuth and DefaultHeaders mutated the caller's request, which violates the RoundTripper contract and risks races on shared/retried requests; clone before writing headers (matching RequestID). Validate the incoming X-Request-Id (length and character set) before propagating it to logs and the response header, preventing log forging and header splitting from a client-controlled value.
This commit is contained in:
@@ -214,6 +214,36 @@ func TestRequestID(t *testing.T) {
|
||||
t.Fatalf("expected empty, got %q", id)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("rejects unsafe incoming ID", func(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"header injection": "abc\r\nX-Injected: 1",
|
||||
"contains space": "has space",
|
||||
"too long": strings.Repeat("a", 200),
|
||||
}
|
||||
for name, badID := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
var gotID string
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotID = server.RequestIDFromContext(r.Context())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
mw := server.RequestID()(handler)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
req.Header.Set("X-Request-Id", badID)
|
||||
mw.ServeHTTP(w, req)
|
||||
|
||||
if gotID == badID {
|
||||
t.Fatalf("unsafe incoming ID was propagated verbatim: %q", gotID)
|
||||
}
|
||||
if len(gotID) != 36 {
|
||||
t.Fatalf("expected a freshly generated UUID, got %q (len %d)", gotID, len(gotID))
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRequestID_UUIDFormat(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user