Files
caddy/modules/caddyhttp/server_request_target_test.go
Matthew HoltandCopilot 13ae3457cf caddyhttp: reject opaque targets before routing and proxying
Reject opaque HTTP request targets before either normal or error routes can interpret them. Clear URL.Opaque defensively on proxy clones. Verify HTTP/1 parsing, path authentication, supported target forms and outbound serialization without contacting an upstream.

Addresses scan report e17d2fcd-d50c-4d68-82d1-96519a437a24.

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>
2026-11-11 11:06:41 -07:00

62 lines
2.0 KiB
Go

package caddyhttp
import (
"bufio"
"net/http"
"net/http/httptest"
"strings"
"testing"
"go.uber.org/zap"
)
func TestServerRequestTargetForms(t *testing.T) {
for _, tc := range []struct {
method, target string
code int
routed bool
}{
{http.MethodGet, "/public", http.StatusOK, true},
{http.MethodGet, "/admin/secret", http.StatusUnauthorized, true},
{http.MethodGet, "http://public.example/public", http.StatusOK, true},
{http.MethodGet, "http://public.example/admin/secret", http.StatusUnauthorized, true},
{http.MethodConnect, "public.example:443", http.StatusOK, true},
{http.MethodOptions, "*", http.StatusOK, true},
{http.MethodGet, "http:http://upstream.example/admin/secret", http.StatusBadRequest, false},
{http.MethodGet, "http:http://private.example/secret", http.StatusBadRequest, false},
{http.MethodGet, "http:secret", http.StatusBadRequest, false},
} {
t.Run(tc.method+" "+tc.target, func(t *testing.T) {
raw := tc.method + " " + tc.target + " HTTP/1.1\r\nHost: public.example\r\n\r\n"
req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(raw)))
if err != nil {
t.Fatal(err)
}
defer req.Body.Close()
routed := false
server := &Server{
logger: zap.NewNop(), errorLogger: zap.NewNop(), accessLogger: zap.NewNop(),
Errors: &HTTPErrorConfig{Routes: RouteList{{}}},
errorHandlerChain: HandlerFunc(func(http.ResponseWriter, *http.Request) error {
t.Fatal("opaque target reached error routes")
return nil
}),
primaryHandlerChain: HandlerFunc(func(w http.ResponseWriter, req *http.Request) error {
routed = true
if (MatchPath{"/admin/*"}).Match(req) {
w.WriteHeader(http.StatusUnauthorized)
} else {
w.WriteHeader(http.StatusOK)
}
return nil
}),
}
rec := httptest.NewRecorder()
server.ServeHTTP(rec, req)
if rec.Code != tc.code || routed != tc.routed {
t.Fatalf("got status=%d routed=%v, want status=%d routed=%v", rec.Code, routed, tc.code, tc.routed)
}
})
}
}