Skip to content

Commit

Permalink
fileserver: properly handle escaped/non-ascii paths (#4332)
Browse files Browse the repository at this point in the history
* fileserver: properly handle escaped/non-ascii paths

* fileserver: tests: accommodate Windows hate of colons in files names
  • Loading branch information
mohammed90 authored Sep 16, 2021
1 parent 2ebfda1 commit 33c70f4
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 3 deletions.
2 changes: 2 additions & 0 deletions modules/caddyhttp/caddyhttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"io"
"net"
"net/http"
"net/url"
"path/filepath"
"strconv"
"strings"
Expand Down Expand Up @@ -227,6 +228,7 @@ func StatusCodeMatches(actual, configured int) bool {
// never be outside of root. The resulting path can be used
// with the local file system.
func SanitizedPathJoin(root, reqPath string) string {
reqPath, _ = url.PathUnescape(reqPath)
if root == "" {
root = "."
}
Expand Down
7 changes: 4 additions & 3 deletions modules/caddyhttp/fileserver/browsetplcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,16 @@ func (fsrv *FileServer) directoryListing(files []os.FileInfo, canGoUp bool, root

isDir := f.IsDir() || isSymlinkTargetDir(f, root, urlPath)

u := url.URL{Path: url.PathEscape(name)}

// add the slash after the escape of path to avoid escaping the slash as well
if isDir {
name += "/"
u.Path += "/"
dirCount++
} else {
fileCount++
}

u := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name

fileInfos = append(fileInfos, fileInfo{
IsDir: isDir,
IsSymlink: isSymlink(f),
Expand Down
43 changes: 43 additions & 0 deletions modules/caddyhttp/fileserver/matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,31 @@ package fileserver
import (
"net/http"
"net/url"
"os"
"runtime"
"testing"

"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)

func TestFileMatcher(t *testing.T) {

// Windows doesn't like colons in files names
isWindows := runtime.GOOS == "windows"
if !isWindows {
filename := "with:in-name.txt"
f, err := os.Create("./testdata/" + filename)
if err != nil {
t.Fail()
return
}
t.Cleanup(func() {
os.Remove("./testdata/" + filename)
})
f.WriteString(filename)
f.Close()
}

for i, tc := range []struct {
path string
expectedPath string
Expand Down Expand Up @@ -63,6 +82,30 @@ func TestFileMatcher(t *testing.T) {
path: "/missingfile.php",
matched: false,
},
{
path: "ملف.txt", // the path file name is not escaped
expectedPath: "ملف.txt",
expectedType: "file",
matched: true,
},
{
path: url.PathEscape("ملف.txt"), // singly-escaped path
expectedPath: "ملف.txt",
expectedType: "file",
matched: true,
},
{
path: url.PathEscape(url.PathEscape("ملف.txt")), // doubly-escaped path
expectedPath: "%D9%85%D9%84%D9%81.txt",
expectedType: "file",
matched: true,
},
{
path: "./with:in-name.txt", // browsers send the request with the path as such
expectedPath: "with:in-name.txt",
expectedType: "file",
matched: !isWindows,
},
} {
m := &MatchFile{
Root: "./testdata",
Expand Down
11 changes: 11 additions & 0 deletions modules/caddyhttp/fileserver/staticfiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
weakrand "math/rand"
"mime"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
Expand Down Expand Up @@ -165,6 +166,16 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
filesToHide := fsrv.transformHidePaths(repl)

root := repl.ReplaceAll(fsrv.Root, ".")
// PathUnescape returns an error if the escapes aren't well-formed,
// meaning the count % matches the RFC. Return early if the escape is
// improper.
if _, err := url.PathUnescape(r.URL.Path); err != nil {
fsrv.logger.Debug("improper path escape",
zap.String("site_root", root),
zap.String("request_path", r.URL.Path),
zap.Error(err))
return err
}
filename := caddyhttp.SanitizedPathJoin(root, r.URL.Path)

fsrv.logger.Debug("sanitized path join",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
%D9%85%D9%84%D9%81.txt
1 change: 1 addition & 0 deletions modules/caddyhttp/fileserver/testdata/ملف.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ملف.txt

0 comments on commit 33c70f4

Please sign in to comment.