From 4de6d6b902e5ea57ffbf52e994ff697601105595 Mon Sep 17 00:00:00 2001 From: LBF38 Date: Thu, 8 Jan 2026 14:26:04 +0100 Subject: [PATCH] Validate X-Forwarded-Prefix value for dashboard redirect Co-authored-by: Kevin Pollet --- pkg/api/dashboard/dashboard.go | 8 ++++- pkg/api/dashboard/dashboard_test.go | 48 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/pkg/api/dashboard/dashboard.go b/pkg/api/dashboard/dashboard.go index b44a14bba..dd8c335b5 100644 --- a/pkg/api/dashboard/dashboard.go +++ b/pkg/api/dashboard/dashboard.go @@ -79,7 +79,13 @@ func Append(router *mux.Router, basePath string, customAssets fs.FS) error { router.Methods(http.MethodGet). Path(basePath). HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - prefix := strings.TrimSuffix(req.Header.Get("X-Forwarded-Prefix"), "/") + xfPrefix := req.Header.Get("X-Forwarded-Prefix") + if strings.Contains(xfPrefix, "//") { + log.Error().Msgf("X-Forwarded-Prefix contains an invalid value: %s, defaulting to empty prefix", xfPrefix) + xfPrefix = "" + } + + prefix := strings.TrimSuffix(xfPrefix, "/") http.Redirect(resp, req, prefix+dashboardPath, http.StatusFound) }) diff --git a/pkg/api/dashboard/dashboard_test.go b/pkg/api/dashboard/dashboard_test.go index b07bff4f6..f7d387e0c 100644 --- a/pkg/api/dashboard/dashboard_test.go +++ b/pkg/api/dashboard/dashboard_test.go @@ -9,7 +9,9 @@ import ( "testing/fstest" "time" + "github.com/gorilla/mux" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_ContentSecurityPolicy(t *testing.T) { @@ -60,6 +62,52 @@ func Test_ContentSecurityPolicy(t *testing.T) { } } +func Test_XForwardedPrefix(t *testing.T) { + testCases := []struct { + desc string + prefix string + expected string + }{ + { + desc: "location in X-Forwarded-Prefix", + prefix: "//foobar/test", + expected: "/dashboard/", + }, + { + desc: "scheme in X-Forwarded-Prefix", + prefix: "http://foobar", + expected: "/dashboard/", + }, + { + desc: "path in X-Forwarded-Prefix", + prefix: "foobar", + expected: "/foobar/dashboard/", + }, + } + + router := mux.NewRouter() + err := Append(router, "/", fstest.MapFS{"index.html": &fstest.MapFile{ + Mode: 0o755, + ModTime: time.Now(), + }}) + require.NoError(t, err) + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + req := httptest.NewRequest(http.MethodGet, "/", http.NoBody) + req.Header.Set("X-Forwarded-Prefix", test.prefix) + rw := httptest.NewRecorder() + + router.ServeHTTP(rw, req) + + assert.Equal(t, http.StatusFound, rw.Code) + assert.Equal(t, test.expected, rw.Result().Header.Get("Location")) + }) + } +} + type errorFS struct{} func (e errorFS) Open(name string) (fs.File, error) {