1
0
Fork 0

Validate X-Forwarded-Prefix value for dashboard redirect

Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
This commit is contained in:
LBF38 2026-01-08 14:26:04 +01:00 committed by GitHub
parent 1778ff3bac
commit 4de6d6b902
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 55 additions and 1 deletions

View file

@ -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)
})

View file

@ -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) {