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