diff --git a/cgi.go b/cgi.go index 064bc14ac8..c5a10f4340 100644 --- a/cgi.go +++ b/cgi.go @@ -345,7 +345,7 @@ func sanitizedPathJoin(root, reqPath string) string { // Clean with "path", not "filepath": on Windows filepath.Clean keeps the // ".." behind a leading "//" (UNC prefix), letting reqPath escape root. - relPath := path.Clean("/" + reqPath)[1:] + relPath := path.Clean(ensureLeadingSlash(reqPath))[1:] // A cleaned rooted path is always local on POSIX: only Windows needs the check if runtime.GOOS == "windows" && relPath != "" && !filepath.IsLocal(relPath) { // path is unsafe (see https://github.com/golang/go/issues/56336#issuecomment-1416214885) diff --git a/cgi_test.go b/cgi_test.go index 6675dd19e4..e6c11d5b92 100644 --- a/cgi_test.go +++ b/cgi_test.go @@ -50,6 +50,9 @@ func TestSanitizedPathJoin(t *testing.T) { expected string }{ {"leading slash", "/index.php", filepath.Join(root, "index.php")}, + {"multiple leading slashes", "///index.php", filepath.Join(root, "index.php")}, + {"multiple root slashes", "//", filepath.Clean(root) + separator}, + {"rooted nested traversal", "//foo/../../bar/", filepath.Join(root, "bar") + separator}, {"no leading slash", "index.php", filepath.Join(root, "index.php")}, {"nested", "/foo/bar/index.php", filepath.Join(root, "foo", "bar", "index.php")}, {"empty req path returns root", "", filepath.Clean(root)},