From e63db782c11c7b8bfce30be4c902e7ef8f9f33d2 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Tue, 28 Jul 2020 10:08:03 +0200 Subject: [PATCH] fix: clean X-Forwarded-Prefix header for the dashboard. --- pkg/api/dashboard.go | 23 +++++++++++++++-- pkg/api/dashboard_test.go | 54 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 pkg/api/dashboard_test.go diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index e71252e48..b3168de8e 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -2,6 +2,7 @@ package api import ( "net/http" + "net/url" "github.com/containous/traefik/v2/pkg/log" assetfs "github.com/elazarl/go-bindata-assetfs" @@ -23,11 +24,29 @@ func (g DashboardHandler) Append(router *mux.Router) { // Expose dashboard router.Methods(http.MethodGet). Path("/"). - HandlerFunc(func(response http.ResponseWriter, request *http.Request) { - http.Redirect(response, request, request.Header.Get("X-Forwarded-Prefix")+"/dashboard/", http.StatusFound) + HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + http.Redirect(resp, req, safePrefix(req)+"/dashboard/", http.StatusFound) }) router.Methods(http.MethodGet). PathPrefix("/dashboard/"). Handler(http.StripPrefix("/dashboard/", http.FileServer(g.Assets))) } + +func safePrefix(req *http.Request) string { + prefix := req.Header.Get("X-Forwarded-Prefix") + if prefix == "" { + return "" + } + + parse, err := url.Parse(prefix) + if err != nil { + return "" + } + + if parse.Host != "" { + return "" + } + + return parse.Path +} diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go new file mode 100644 index 000000000..c945a7000 --- /dev/null +++ b/pkg/api/dashboard_test.go @@ -0,0 +1,54 @@ +package api + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_safePrefix(t *testing.T) { + testCases := []struct { + desc string + value string + expected string + }{ + { + desc: "host", + value: "https://example.com", + expected: "", + }, + { + desc: "host with path", + value: "https://example.com/foo/bar?test", + expected: "", + }, + { + desc: "path", + value: "/foo/bar", + expected: "/foo/bar", + }, + { + desc: "path without leading slash", + value: "foo/bar", + expected: "foo/bar", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + req, err := http.NewRequest(http.MethodGet, "http://localhost", nil) + require.NoError(t, err) + + req.Header.Set("X-Forwarded-Prefix", test.value) + + prefix := safePrefix(req) + + assert.Equal(t, test.expected, prefix) + }) + } +}