mirror of
https://github.com/caddyserver/caddy.git
synced 2026-11-11 19:37:55 +00:00
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>
62 lines
2.0 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|