@marcel91200 / caddy Mirror · read-only / commits / 0ee43782
Fork 0

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, 05:15 UTC

fileserver: Improve file hiding logic for directories and prefixes

Now, a filename to hide that is specified without a path separator will count as hidden if it appears in any component of the file path (not only the last component); semantically, this means hiding a file by only its name (without any part of a path) will hide both files and folders, e.g. hiding ".git" will hide "/.git" and also "/.git/foo". We also do prefix matching so that hiding "/.git" will hide "/.git" and "/.git/foo" but not "/.gitignore". The remaining logic is a globular match like before.

Matthew Holt committed Sep 11, 2020 at 12:20 UTC 0ee4378227cf3019dcbaddeb3977ddd28e58429a
2 files changed +95 -17
modules/caddyhttp/fileserver/staticfiles.go
+24 -15
@@ -329,28 +329,37 @@ func sanitizedPathJoin(root, reqPath string) string {
329 // fileHidden returns true if filename is hidden
330 // according to the hide list.
331 func fileHidden(filename string, hide []string) bool {
332 - nameOnly := filepath.Base(filename)
332 sep := string(filepath.Separator)
333 + var components []string
334
335 for _, h := range hide {
336 - // assuming h is a glob/shell-like pattern,
337 - // use it to compare the whole file path;
338 - // but if there is no separator in h, then
339 - // just compare against the file's name
340 - compare := filename
336 if !strings.Contains(h, sep) {
342 - compare = nameOnly
343 - }
344 -
345 - hidden, err := filepath.Match(h, compare)
346 - if err != nil {
347 - // malformed pattern; fallback by checking prefix
348 - if strings.HasPrefix(filename, h) {
337 + // if there is no separator in h, then we assume the user
338 + // wants to hide any files or folders that match that
339 + // name; thus we have to compare against each component
340 + // of the filename, e.g. hiding "bar" would hide "/bar"
341 + // as well as "/foo/bar/baz" but not "/barstool".
342 + if len(components) == 0 {
343 + components = strings.Split(filename, sep)
344 + }
345 + for _, c := range components {
346 + if c == h {
347 + return true
348 + }
349 + }
350 + } else if strings.HasPrefix(filename, h) {
351 + // otherwise, if there is a separator in h, and
352 + // filename is exactly prefixed with h, then we
353 + // can do a prefix match so that "/foo" matches
354 + // "/foo/bar" but not "/foobar".
355 + withoutPrefix := strings.TrimPrefix(filename, h)
356 + if strings.HasPrefix(withoutPrefix, sep) {
357 return true
358 }
359 }
352 - if hidden {
353 - // file name or path matches hide pattern
360 +
361 + // in the general case, a glob match will suffice
362 + if hidden, _ := filepath.Match(h, filename); hidden {
363 return true
364 }
365 }
modules/caddyhttp/fileserver/staticfiles_test.go
+71 -2
@@ -93,9 +93,78 @@ func TestSanitizedPathJoin(t *testing.T) {
93 }
94 actual := sanitizedPathJoin(tc.inputRoot, u.Path)
95 if actual != tc.expect {
96 - t.Errorf("Test %d: [%s %s] => %s (expected %s)", i, tc.inputRoot, tc.inputPath, actual, tc.expect)
96 + t.Errorf("Test %d: [%s %s] => %s (expected %s)",
97 + i, tc.inputRoot, tc.inputPath, actual, tc.expect)
98 }
99 }
100 }
101
101 -// TODO: test fileHidden
102 +func TestFileHidden(t *testing.T) {
103 + for i, tc := range []struct {
104 + inputHide []string
105 + inputPath string
106 + expect bool
107 + }{
108 + {
109 + inputHide: nil,
110 + inputPath: "",
111 + expect: false,
112 + },
113 + {
114 + inputHide: []string{".gitignore"},
115 + inputPath: "/.gitignore",
116 + expect: true,
117 + },
118 + {
119 + inputHide: []string{".git"},
120 + inputPath: "/.gitignore",
121 + expect: false,
122 + },
123 + {
124 + inputHide: []string{"/.git"},
125 + inputPath: "/.gitignore",
126 + expect: false,
127 + },
128 + {
129 + inputHide: []string{".git"},
130 + inputPath: "/.git",
131 + expect: true,
132 + },
133 + {
134 + inputHide: []string{".git"},
135 + inputPath: "/.git/foo",
136 + expect: true,
137 + },
138 + {
139 + inputHide: []string{".git"},
140 + inputPath: "/foo/.git/bar",
141 + expect: true,
142 + },
143 + {
144 + inputHide: []string{"/prefix"},
145 + inputPath: "/prefix/foo",
146 + expect: true,
147 + },
148 + {
149 + inputHide: []string{"/foo/*/bar"},
150 + inputPath: "/foo/asdf/bar",
151 + expect: true,
152 + },
153 + {
154 + inputHide: []string{"/foo"},
155 + inputPath: "/foo",
156 + expect: true,
157 + },
158 + {
159 + inputHide: []string{"/foo"},
160 + inputPath: "/foobar",
161 + expect: false,
162 + },
163 + } {
164 + actual := fileHidden(tc.inputPath, tc.inputHide)
165 + if actual != tc.expect {
166 + t.Errorf("Test %d: Is %s hidden in %v? Got %t but expected %t",
167 + i, tc.inputPath, tc.inputHide, actual, tc.expect)
168 + }
169 + }
170 +}