mirror of https://github.com/grafana/grafana.git
[release-11.4.9] Forbid more redirect patterns (#110506)
This commit is contained in:
parent
36e7095840
commit
c100d2c684
|
@ -41,7 +41,7 @@ var getViewIndex = func() string {
|
||||||
return viewIndex
|
return viewIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only allow redirects that start with an alphanumerical character, a dash or an underscore.
|
// 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 redirectRe = regexp.MustCompile(`^/[a-zA-Z0-9-_].*`)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -73,12 +73,17 @@ func (hs *HTTPServer) ValidateRedirectTo(redirectTo string) error {
|
||||||
return errForbiddenRedirectTo
|
return errForbiddenRedirectTo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if to.Path != "/" && !redirectRe.MatchString(to.Path) {
|
||||||
|
return errForbiddenRedirectTo
|
||||||
|
}
|
||||||
|
|
||||||
cleanPath := path.Clean(to.Path)
|
cleanPath := path.Clean(to.Path)
|
||||||
// "." is what path.Clean returns for empty paths
|
// "." is what path.Clean returns for empty paths
|
||||||
if cleanPath == "." {
|
if cleanPath == "." {
|
||||||
return errForbiddenRedirectTo
|
return errForbiddenRedirectTo
|
||||||
}
|
}
|
||||||
if to.Path != "/" && !redirectRe.MatchString(cleanPath) {
|
|
||||||
|
if cleanPath != "/" && !redirectRe.MatchString(cleanPath) {
|
||||||
return errForbiddenRedirectTo
|
return errForbiddenRedirectTo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,7 @@ func TestHTTPServer_RotateUserAuthTokenRedirect(t *testing.T) {
|
||||||
|
|
||||||
// Invalid redirects should be converted to root
|
// Invalid redirects should be converted to root
|
||||||
{"backslash domain", `/\grafana.com`, "/"},
|
{"backslash domain", `/\grafana.com`, "/"},
|
||||||
|
{"backslash domain at the start of the path", `/\grafana.com/../a`, "/"},
|
||||||
{"traversal backslash domain", `/a/../\grafana.com`, "/"},
|
{"traversal backslash domain", `/a/../\grafana.com`, "/"},
|
||||||
{"double slash", "//grafana", "/"},
|
{"double slash", "//grafana", "/"},
|
||||||
{"missing initial slash", "missingInitialSlash", "/"},
|
{"missing initial slash", "missingInitialSlash", "/"},
|
||||||
|
@ -232,7 +233,7 @@ func TestHTTPServer_RotateUserAuthTokenRedirect(t *testing.T) {
|
||||||
res, err := server.Send(req)
|
res, err := server.Send(req)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, 302, redirectStatusCode)
|
assert.Equal(t, 302, redirectStatusCode)
|
||||||
assert.Equal(t, redirectCase.expectedUrl, redirectLocation)
|
assert.Equal(t, redirectCase.expectedUrl, redirectLocation, "redirectTo=%s", redirectCase.redirectUrl)
|
||||||
|
|
||||||
require.NoError(t, res.Body.Close())
|
require.NoError(t, res.Body.Close())
|
||||||
})
|
})
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Only allow redirects that start with an alphanumerical character, a dash or an underscore.
|
// 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 redirectRe = regexp.MustCompile(`^/?[a-zA-Z0-9-_].*`)
|
||||||
|
|
||||||
// OrgRedirect changes org and redirects users if the
|
// OrgRedirect changes org and redirects users if the
|
||||||
|
@ -67,6 +67,9 @@ func OrgRedirect(cfg *setting.Cfg, userSvc user.Service) web.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
func validRedirectPath(p string) bool {
|
func validRedirectPath(p string) bool {
|
||||||
|
if p != "" && p != "/" && !redirectRe.MatchString(p) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
cleanPath := path.Clean(p)
|
cleanPath := path.Clean(p)
|
||||||
return cleanPath == "." || cleanPath == "/" || redirectRe.MatchString(cleanPath)
|
return cleanPath == "." || cleanPath == "/" || redirectRe.MatchString(cleanPath)
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,12 +71,17 @@ func TestOrgRedirectMiddleware(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
middlewareScenario(t, "when redirecting to an invalid path", func(t *testing.T, sc *scenarioContext) {
|
middlewareScenario(t, "when redirecting to an invalid path", func(t *testing.T, sc *scenarioContext) {
|
||||||
sc.withIdentity(&authn.Identity{})
|
testPaths := []string{
|
||||||
|
url.QueryEscape(`/\example.com`),
|
||||||
|
`/%2fexample.com`,
|
||||||
|
}
|
||||||
|
for _, path := range testPaths {
|
||||||
|
sc.withIdentity(&authn.Identity{})
|
||||||
|
|
||||||
path := url.QueryEscape(`/\example.com`)
|
sc.m.Get(url.QueryEscape(path), sc.defaultHandler)
|
||||||
sc.m.Get(url.QueryEscape(path), sc.defaultHandler)
|
sc.fakeReq("GET", fmt.Sprintf("%s?orgId=3", path)).exec()
|
||||||
sc.fakeReq("GET", fmt.Sprintf("%s?orgId=3", path)).exec()
|
|
||||||
|
|
||||||
require.Equal(t, 404, sc.resp.Code)
|
require.Equal(t, 404, sc.resp.Code, "path: %s", path)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue