aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdnan Maolood <[email protected]>2021-02-17 11:17:13 -0500
committerAdnan Maolood <[email protected]>2021-02-17 11:17:13 -0500
commitfb9b50871c57e248feb046c5838df5cd9debad60 (patch)
tree976a2a5ca8862590a9e3b720d4a8146eefe114c9
parentfs: Add ServeContent function (diff)
downloadgo-gemini-fb9b50871c57e248feb046c5838df5cd9debad60.tar.xz
go-gemini-fb9b50871c57e248feb046c5838df5cd9debad60.zip
fs: Reject potentially unsafe requests in ServeFile
Reject requests where r.URL.Path contains a ".." path element to protect against callers who might unsafely use filepath.Join on r.URL.Path without sanitizing it.
-rw-r--r--fs.go36
1 files changed, 36 insertions, 0 deletions
diff --git a/fs.go b/fs.go
index bf5c589..67d9206 100644
--- a/fs.go
+++ b/fs.go
@@ -60,10 +60,46 @@ func serveContent(w ResponseWriter, name string, content io.Reader) {
// relative to the current directory and may ascend to parent directories. If
// the provided name is constructed from user input, it should be sanitized
// before calling ServeFile.
+//
+// As a precaution, ServeFile will reject requests where r.URL.Path contains a
+// ".." path element; this protects against callers who might unsafely use
+// filepath.Join on r.URL.Path without sanitizing it and then use that
+// filepath.Join result as the name argument.
+//
+// As another special case, ServeFile redirects any request where r.URL.Path
+// ends in "/index.gmi" to the same path, without the final "index.gmi". To
+// avoid such redirects either modify the path or use ServeContent.
+//
+// Outside of those two special cases, ServeFile does not use r.URL.Path for
+// selecting the file or directory to serve; only the file or directory
+// provided in the name argument is used.
func ServeFile(w ResponseWriter, r *Request, fsys fs.FS, name string) {
+ if containsDotDot(r.URL.Path) {
+ // Too many programs use r.URL.Path to construct the argument to
+ // serveFile. Reject the request under the assumption that happened
+ // here and ".." may not be wanted.
+ // Note that name might not contain "..", for example if code (still
+ // incorrectly) used filepath.Join(myDir, r.URL.Path).
+ w.Header(StatusBadRequest, "invalid URL path")
+ return
+ }
serveFile(w, r, fsys, name, false)
}
+func containsDotDot(v string) bool {
+ if !strings.Contains(v, "..") {
+ return false
+ }
+ for _, ent := range strings.FieldsFunc(v, isSlashRune) {
+ if ent == ".." {
+ return true
+ }
+ }
+ return false
+}
+
+func isSlashRune(r rune) bool { return r == '/' || r == '\\' }
+
func serveFile(w ResponseWriter, r *Request, fsys fs.FS, name string, redirect bool) {
const indexPage = "/index.gmi"