mirror of
https://github.com/caddyserver/caddy.git
synced 2026-11-11 19:37:55 +00:00
Apply the existing header-alias rules before method and host validation, so early errors cannot carry spoofed identity aliases into user-defined error routes. Verify all early error paths with synthetic authentication headers. Addresses scan report 3ebe3dfa-8541-44a7-8f1f-cd5071d55a67. The supplied checker has a malformed Authorization header; regression tests use independently constructed requests. Code written by GitHub Copilot (GPT-6.1 Sol, Claude Opus 5.5); reviewed by mholt. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
64 lines
2.0 KiB
Go
64 lines
2.0 KiB
Go
package caddyhttp
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func TestServerErrorRoutesReceiveSanitizedHeaders(t *testing.T) {
|
|
for _, invalid := range []string{"method", "missing host", "invalid host"} {
|
|
t.Run(invalid, func(t *testing.T) {
|
|
reachedErrorRoute := false
|
|
server := &Server{
|
|
logger: zap.NewNop(), errorLogger: zap.NewNop(), accessLogger: zap.NewNop(),
|
|
Errors: &HTTPErrorConfig{Routes: RouteList{{}}},
|
|
primaryHandlerChain: HandlerFunc(func(http.ResponseWriter, *http.Request) error {
|
|
t.Fatal("invalid request reached primary routes")
|
|
return nil
|
|
}),
|
|
errorHandlerChain: HandlerFunc(func(w http.ResponseWriter, req *http.Request) error {
|
|
reachedErrorRoute = true
|
|
if req.Header.Get("Authorization") != "synthetic" {
|
|
t.Fatal("normal authorization header was removed")
|
|
}
|
|
// Stand in for a trusted authentication response copied into the request.
|
|
req.Header.Set("Remote-User", "alice")
|
|
var users []string
|
|
for name, values := range req.Header {
|
|
if strings.NewReplacer("-", "_", ".", "_").Replace(strings.ToUpper(name)) == "REMOTE_USER" {
|
|
users = append(users, values...)
|
|
}
|
|
}
|
|
if len(users) != 1 || users[0] != "alice" {
|
|
t.Fatalf("backend identity collision: %v", users)
|
|
}
|
|
w.WriteHeader(http.StatusForbidden)
|
|
return nil
|
|
}),
|
|
}
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.Header.Set("Authorization", "synthetic")
|
|
req.Header.Set("Remote_user", "admin")
|
|
req.Header.Set("Remote.user", "admin")
|
|
req.Header.Set("Remote_.user", "admin")
|
|
switch invalid {
|
|
case "method":
|
|
req.Method = strings.Repeat("A", 33)
|
|
case "missing host":
|
|
req.Host = ""
|
|
case "invalid host":
|
|
req.Host = "..."
|
|
}
|
|
rec := httptest.NewRecorder()
|
|
server.ServeHTTP(rec, req)
|
|
if !reachedErrorRoute || rec.Code != http.StatusForbidden {
|
|
t.Fatalf("error route not reached: status %d", rec.Code)
|
|
}
|
|
})
|
|
}
|
|
}
|