Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 01:00 UTC
fileserver: don't match regular file when path ends by slash (#8326)
Kévin Dunglas committed
Nov 1, 2026 at 21:57 UTC
aea046c97833b7aaf1006db2f4796681733411e6
2 files changed
+53
-8
modules/caddyhttp/fileserver/matcher.go
+5
-3
@@ -381,10 +381,12 @@ func (m MatchFile) selectFile(r *http.Request) (bool, error) {
381
// clean the path and split, if configured -- we must split before
382
// globbing so that the file system doesn't include the remainder
383
// ("afterSplit") in the filename; be sure to restore trailing slash
384
- beforeSplit, afterSplit := m.firstSplit(path.Clean(expandedFile))
385
- if strings.HasSuffix(file, "/") {
386
- beforeSplit += "/"
384
+ // so that "/file/" or "/file/./" cannot match a regular file
385
+ cleanedFile := path.Clean(expandedFile)
386
+ if strings.HasSuffix(expandedFile, "/") && !strings.HasSuffix(cleanedFile, "/") {
387
+ cleanedFile += "/"
388
}
389
+ beforeSplit, afterSplit := m.firstSplit(cleanedFile)
390
391
// create the full path to the file by prepending the site root
392
fullPattern := caddyhttp.SanitizedPathJoin(root, beforeSplit)
modules/caddyhttp/fileserver/matcher_test.go
+48
-5
@@ -55,19 +55,52 @@ func TestFileMatcher(t *testing.T) {
55
f.Close()
56
}
57
58
+ if err := os.WriteFile("./testdata/.env", []byte("SECRET=1"), 0o644); err != nil {
59
+ t.Fatal(err)
60
+ }
61
+ t.Cleanup(func() {
62
+ os.Remove("./testdata/.env")
63
+ })
64
+
65
for i, tc := range []testCase{
66
{
60
- path: "/foo.txt",
61
- expectedPath: "/foo.txt",
67
+ path: "/.env",
68
+ expectedPath: "/.env",
69
expectedType: "file",
70
matched: true,
71
},
72
{
66
- path: "/foo.txt/",
73
+ path: "/.env/",
74
+ matched: false,
75
+ },
76
+ {
77
+ path: "/.env/./",
78
+ matched: false,
79
+ },
80
+ {
81
+ path: "/foo.txt",
82
expectedPath: "/foo.txt",
83
expectedType: "file",
84
matched: true,
85
},
86
+ {
87
+ path: "/foo.txt/",
88
+ matched: false,
89
+ },
90
+ {
91
+ path: "/foo.txt/./",
92
+ matched: false,
93
+ },
94
+ {
95
+ path: "/foo.txt//",
96
+ matched: false,
97
+ },
98
+ {
99
+ path: "/foodir/./",
100
+ expectedPath: "/foodir/",
101
+ expectedType: "directory",
102
+ matched: true,
103
+ },
104
{
105
path: "/foo.txt?a=b",
106
expectedPath: "/foo.txt",
@@ -330,8 +363,18 @@ func TestPHPFileMatcher(t *testing.T) {
363
matched: true,
364
},
365
{
333
- path: "/notphp.php.txt/",
334
- expectedPath: "/notphp.php.txt",
366
+ path: "/notphp.php.txt/",
367
+ matched: false,
368
+ },
369
+ {
370
+ path: "/index.php/",
371
+ expectedPath: "/index.php",
372
+ expectedType: "file",
373
+ matched: true,
374
+ },
375
+ {
376
+ path: "/index.php/somewhere/",
377
+ expectedPath: "/index.php",
378
expectedType: "file",
379
matched: true,
380
},