mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-27 06:03:48 +03:00
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
This commit is contained in:
parent
2ebfda1ae9
commit
33c70f418f
6 changed files with 62 additions and 3 deletions
|
@ -20,6 +20,7 @@ import (
|
|||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -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 = "."
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
@ -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",
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
weakrand "math/rand"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
@ -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",
|
||||
|
|
1
modules/caddyhttp/fileserver/testdata/%D9%85%D9%84%D9%81.txt
vendored
Normal file
1
modules/caddyhttp/fileserver/testdata/%D9%85%D9%84%D9%81.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
%D9%85%D9%84%D9%81.txt
|
1
modules/caddyhttp/fileserver/testdata/ملف.txt
vendored
Normal file
1
modules/caddyhttp/fileserver/testdata/ملف.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
ملف.txt
|
Loading…
Reference in a new issue