From d4fcd25632af82eadec3b8b397f8a3b44be0d99f Mon Sep 17 00:00:00 2001 From: "grafana-delivery-bot[bot]" <132647405+grafana-delivery-bot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 18:48:43 +0100 Subject: [PATCH] [release-11.6.8] Stricter validation for redirect URLs (#113863) Stricter validation for redirect URLs (#113852) (cherry picked from commit 3f48a6358fc782b642de6fe29e55477f3b9cf325) --- pkg/api/login.go | 26 ++++++-------------------- pkg/middleware/org_redirect.go | 13 +++++++------ 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/pkg/api/login.go b/pkg/api/login.go index 7426f7590a0..54fd9ffd771 100644 --- a/pkg/api/login.go +++ b/pkg/api/login.go @@ -7,7 +7,6 @@ import ( "fmt" "net/http" "net/url" - "path" "regexp" "strings" @@ -41,8 +40,10 @@ var getViewIndex = func() string { return viewIndex } -// Only allow redirects that start with a slash followed by an alphanumerical character, a dash or an underscore. -var redirectRe = regexp.MustCompile(`^/[a-zA-Z0-9-_].*`) +var redirectAllowRe = regexp.MustCompile(`^/[a-zA-Z0-9-_./]*$`) + +// Do not allow redirect URLs that contain "//" or ".." +var redirectDenyRe = regexp.MustCompile(`(//|\.\.)`) var ( errAbsoluteRedirectTo = errors.New("absolute URLs are not allowed for redirect_to cookie value") @@ -64,26 +65,11 @@ func (hs *HTTPServer) ValidateRedirectTo(redirectTo string) error { return errForbiddenRedirectTo } - // path should have exactly one leading slash - if !strings.HasPrefix(to.Path, "/") { + if redirectDenyRe.MatchString(to.Path) { return errForbiddenRedirectTo } - if strings.HasPrefix(to.Path, "//") { - return errForbiddenRedirectTo - } - - if to.Path != "/" && !redirectRe.MatchString(to.Path) { - return errForbiddenRedirectTo - } - - cleanPath := path.Clean(to.Path) - // "." is what path.Clean returns for empty paths - if cleanPath == "." { - return errForbiddenRedirectTo - } - - if cleanPath != "/" && !redirectRe.MatchString(cleanPath) { + if to.Path != "/" && !redirectAllowRe.MatchString(to.Path) { return errForbiddenRedirectTo } diff --git a/pkg/middleware/org_redirect.go b/pkg/middleware/org_redirect.go index e507b9bbfe7..ab2734f0f0a 100644 --- a/pkg/middleware/org_redirect.go +++ b/pkg/middleware/org_redirect.go @@ -3,7 +3,6 @@ package middleware import ( "fmt" "net/http" - "path" "regexp" "strconv" @@ -13,8 +12,10 @@ import ( "github.com/grafana/grafana/pkg/web" ) -// Only allow redirects that start with a slash followed by an alphanumerical character, a dash or an underscore. -var redirectRe = regexp.MustCompile(`^/?[a-zA-Z0-9-_].*`) +var redirectAllowRe = regexp.MustCompile(`^/?[a-zA-Z0-9-_./]*$`) + +// Do not allow redirect URLs that contain "//" or ".." +var redirectDenyRe = regexp.MustCompile(`(//|\.\.)`) // OrgRedirect changes org and redirects users if the // querystring `orgId` doesn't match the active org. @@ -66,9 +67,9 @@ func OrgRedirect(cfg *setting.Cfg, userSvc user.Service) web.Handler { } func validRedirectPath(p string) bool { - if p != "" && p != "/" && !redirectRe.MatchString(p) { + if redirectDenyRe.MatchString(p) { return false } - cleanPath := path.Clean(p) - return cleanPath == "." || cleanPath == "/" || redirectRe.MatchString(cleanPath) + + return p == "" || p == "/" || redirectAllowRe.MatchString(p) }