Skip to content

Commit f8a2c60

Browse files
authored
caddyhttp: properly sanitize requests for root path (#6360)
SanitizePathJoin protects against directory traversal attacks by checking for requests whose URL path look like they are trying to request something other than a local file, and returns the root directory in those cases. The method is also careful to ensure that requests which contain a trailing slash include a trailing slash in the returned value. However, for requests that contain only a slash (requests for the root path), the IsLocal check returns early before the matching trailing slash is re-added. This change updates SanitizePathJoin to only perform the filepath.IsLocal check if the cleaned request URL path is non-empty. --- This change also updates the existing SanitizePathJoin tests to use filepath.FromSlash rather than filepath.Join. This makes the expected value a little easier to read, but also has the advantage of not being processed by filepath.Clean like filepath.Join is. This means that the exact expect value will be compared, not the result of first cleaning it. Fixes #6352
1 parent 01308b4 commit f8a2c60

File tree

2 files changed

+19
-11
lines changed

2 files changed

+19
-11
lines changed

modules/caddyhttp/caddyhttp.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ func SanitizedPathJoin(root, reqPath string) string {
239239
}
240240

241241
relPath := path.Clean("/" + reqPath)[1:] // clean path and trim the leading /
242-
if !filepath.IsLocal(relPath) {
242+
if relPath != "" && !filepath.IsLocal(relPath) {
243243
// path is unsafe (see https://github.com/golang/go/issues/56336#issuecomment-1416214885)
244244
return root
245245
}

modules/caddyhttp/caddyhttp_test.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,28 @@ func TestSanitizedPathJoin(t *testing.T) {
2626
inputPath: "/",
2727
expect: ".",
2828
},
29+
{
30+
// fileserver.MatchFile passes an inputPath of "//" for some try_files values.
31+
// See https://github.com/caddyserver/caddy/issues/6352
32+
inputPath: "//",
33+
expect: filepath.FromSlash("./"),
34+
},
2935
{
3036
inputPath: "/foo",
3137
expect: "foo",
3238
},
3339
{
3440
inputPath: "/foo/",
35-
expect: "foo" + separator,
41+
expect: filepath.FromSlash("foo/"),
3642
},
3743
{
3844
inputPath: "/foo/bar",
39-
expect: filepath.Join("foo", "bar"),
45+
expect: filepath.FromSlash("foo/bar"),
4046
},
4147
{
4248
inputRoot: "/a",
4349
inputPath: "/foo/bar",
44-
expect: filepath.Join("/", "a", "foo", "bar"),
50+
expect: filepath.FromSlash("/a/foo/bar"),
4551
},
4652
{
4753
inputPath: "/foo/../bar",
@@ -50,32 +56,34 @@ func TestSanitizedPathJoin(t *testing.T) {
5056
{
5157
inputRoot: "/a/b",
5258
inputPath: "/foo/../bar",
53-
expect: filepath.Join("/", "a", "b", "bar"),
59+
expect: filepath.FromSlash("/a/b/bar"),
5460
},
5561
{
5662
inputRoot: "/a/b",
5763
inputPath: "/..%2fbar",
58-
expect: filepath.Join("/", "a", "b", "bar"),
64+
expect: filepath.FromSlash("/a/b/bar"),
5965
},
6066
{
6167
inputRoot: "/a/b",
6268
inputPath: "/%2e%2e%2fbar",
63-
expect: filepath.Join("/", "a", "b", "bar"),
69+
expect: filepath.FromSlash("/a/b/bar"),
6470
},
6571
{
72+
// inputPath fails the IsLocal test so only the root is returned,
73+
// but with a trailing slash since one was included in inputPath
6674
inputRoot: "/a/b",
6775
inputPath: "/%2e%2e%2f%2e%2e%2f",
68-
expect: "/a/b", // inputPath fails the IsLocal test so only the root is returned
76+
expect: filepath.FromSlash("/a/b/"),
6977
},
7078
{
7179
inputRoot: "/a/b",
7280
inputPath: "/foo%2fbar",
73-
expect: filepath.Join("/", "a", "b", "foo", "bar"),
81+
expect: filepath.FromSlash("/a/b/foo/bar"),
7482
},
7583
{
7684
inputRoot: "/a/b",
7785
inputPath: "/foo%252fbar",
78-
expect: filepath.Join("/", "a", "b", "foo%2fbar"),
86+
expect: filepath.FromSlash("/a/b/foo%2fbar"),
7987
},
8088
{
8189
inputRoot: "C:\\www",
@@ -92,7 +100,7 @@ func TestSanitizedPathJoin(t *testing.T) {
92100
// https://github.com/golang/go/issues/56336#issuecomment-1416214885
93101
inputRoot: "root",
94102
inputPath: "/a/b/../../c",
95-
expect: filepath.Join("root", "c"),
103+
expect: filepath.FromSlash("root/c"),
96104
},
97105
} {
98106
// we don't *need* to use an actual parsed URL, but it

0 commit comments

Comments
 (0)