@marcel91200 / caddy Mirror · read-only / commits / 33c70f41
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 04:00 UTC

fileserver: properly handle escaped/non-ascii paths (#4332)

* fileserver: properly handle escaped/non-ascii paths * fileserver: tests: accommodate Windows hate of colons in files names

Mohammed Al Sahaf committed Sep 16, 2021 at 23:40 UTC 33c70f418f780f8e9524c73fbf4bbdbdbb9d7500
6 files changed +62 -3
modules/caddyhttp/caddyhttp.go
+2
@@ -20,6 +20,7 @@ import (
20 "io"
21 "net"
22 "net/http"
23 + "net/url"
24 "path/filepath"
25 "strconv"
26 "strings"
@@ -227,6 +228,7 @@ func StatusCodeMatches(actual, configured int) bool {
228 // never be outside of root. The resulting path can be used
229 // with the local file system.
230 func SanitizedPathJoin(root, reqPath string) string {
231 + reqPath, _ = url.PathUnescape(reqPath)
232 if root == "" {
233 root = "."
234 }
modules/caddyhttp/fileserver/browsetplcontext.go
+4 -3
@@ -42,15 +42,16 @@ func (fsrv *FileServer) directoryListing(files []os.FileInfo, canGoUp bool, root
42
43 isDir := f.IsDir() || isSymlinkTargetDir(f, root, urlPath)
44
45 + u := url.URL{Path: url.PathEscape(name)}
46 +
47 + // add the slash after the escape of path to avoid escaping the slash as well
48 if isDir {
46 - name += "/"
49 + u.Path += "/"
50 dirCount++
51 } else {
52 fileCount++
53 }
54
52 - u := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name
53 -
55 fileInfos = append(fileInfos, fileInfo{
56 IsDir: isDir,
57 IsSymlink: isSymlink(f),
modules/caddyhttp/fileserver/matcher_test.go
+43
@@ -17,12 +17,31 @@ package fileserver
17 import (
18 "net/http"
19 "net/url"
20 + "os"
21 + "runtime"
22 "testing"
23
24 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
25 )
26
27 func TestFileMatcher(t *testing.T) {
28 +
29 + // Windows doesn't like colons in files names
30 + isWindows := runtime.GOOS == "windows"
31 + if !isWindows {
32 + filename := "with:in-name.txt"
33 + f, err := os.Create("./testdata/" + filename)
34 + if err != nil {
35 + t.Fail()
36 + return
37 + }
38 + t.Cleanup(func() {
39 + os.Remove("./testdata/" + filename)
40 + })
41 + f.WriteString(filename)
42 + f.Close()
43 + }
44 +
45 for i, tc := range []struct {
46 path string
47 expectedPath string
@@ -63,6 +82,30 @@ func TestFileMatcher(t *testing.T) {
82 path: "/missingfile.php",
83 matched: false,
84 },
85 + {
86 + path: "ملف.txt", // the path file name is not escaped
87 + expectedPath: "ملف.txt",
88 + expectedType: "file",
89 + matched: true,
90 + },
91 + {
92 + path: url.PathEscape("ملف.txt"), // singly-escaped path
93 + expectedPath: "ملف.txt",
94 + expectedType: "file",
95 + matched: true,
96 + },
97 + {
98 + path: url.PathEscape(url.PathEscape("ملف.txt")), // doubly-escaped path
99 + expectedPath: "%D9%85%D9%84%D9%81.txt",
100 + expectedType: "file",
101 + matched: true,
102 + },
103 + {
104 + path: "./with:in-name.txt", // browsers send the request with the path as such
105 + expectedPath: "with:in-name.txt",
106 + expectedType: "file",
107 + matched: !isWindows,
108 + },
109 } {
110 m := &MatchFile{
111 Root: "./testdata",
modules/caddyhttp/fileserver/staticfiles.go
+11
@@ -19,6 +19,7 @@ import (
19 weakrand "math/rand"
20 "mime"
21 "net/http"
22 + "net/url"
23 "os"
24 "path"
25 "path/filepath"
@@ -165,6 +166,16 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
166 filesToHide := fsrv.transformHidePaths(repl)
167
168 root := repl.ReplaceAll(fsrv.Root, ".")
169 + // PathUnescape returns an error if the escapes aren't well-formed,
170 + // meaning the count % matches the RFC. Return early if the escape is
171 + // improper.
172 + if _, err := url.PathUnescape(r.URL.Path); err != nil {
173 + fsrv.logger.Debug("improper path escape",
174 + zap.String("site_root", root),
175 + zap.String("request_path", r.URL.Path),
176 + zap.Error(err))
177 + return err
178 + }
179 filename := caddyhttp.SanitizedPathJoin(root, r.URL.Path)
180
181 fsrv.logger.Debug("sanitized path join",
modules/caddyhttp/fileserver/testdata/%D9%85%D9%84%D9%81.txt new
+1
@@ -0,0 +1 @@
1 +%D9%85%D9%84%D9%81.txt
\ No newline at end of file
"b/modules/caddyhttp/fileserver/testdata/\331\205\331\204\331\201.txt" new
+1
@@ -0,0 +1 @@
1 +ملف.txt
\ No newline at end of file