// specific directory tree.
//
// While the [FileSystem.Open] method takes '/'-separated paths, a Dir's string
-// value is a filename on the native file system, not a URL, so it is separated
+// value is a directory path on the native file system, not a URL, so it is separated
// by [filepath.Separator], which isn't necessarily '/'.
//
// Note that Dir could expose sensitive files and directories. Dir will follow
localRedirect(w, r, path.Base(url)+"/")
return
}
- } else {
- if url[len(url)-1] == '/' {
- localRedirect(w, r, "../"+path.Base(url))
+ } else if url[len(url)-1] == '/' {
+ base := path.Base(url)
+ if base == "/" || base == "." {
+ // The FileSystem maps a path like "/" or "/./" to a file instead of a directory.
+ msg := "http: attempting to traverse a non-directory"
+ Error(w, msg, StatusInternalServerError)
return
}
+ localRedirect(w, r, "../"+base)
+ return
}
}
fw.Flush()
}
}
+
+// Issue 63769
+func TestFileServerDirWithRootFile(t *testing.T) { run(t, testFileServerDirWithRootFile) }
+func testFileServerDirWithRootFile(t *testing.T, mode testMode) {
+ testDirFile := func(t *testing.T, h Handler) {
+ ts := newClientServerTest(t, mode, h).ts
+ defer ts.Close()
+
+ res, err := ts.Client().Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if g, w := res.StatusCode, StatusInternalServerError; g != w {
+ t.Errorf("StatusCode mismatch: got %d, want: %d", g, w)
+ }
+ res.Body.Close()
+ }
+
+ t.Run("FileServer", func(t *testing.T) {
+ testDirFile(t, FileServer(Dir("testdata/index.html")))
+ })
+
+ t.Run("FileServerFS", func(t *testing.T) {
+ testDirFile(t, FileServerFS(os.DirFS("testdata/index.html")))
+ })
+}