diff options
Diffstat (limited to 'libgo/go/net/http/fs.go')
-rw-r--r-- | libgo/go/net/http/fs.go | 76 |
1 files changed, 58 insertions, 18 deletions
diff --git a/libgo/go/net/http/fs.go b/libgo/go/net/http/fs.go index 7572023..f61c138 100644 --- a/libgo/go/net/http/fs.go +++ b/libgo/go/net/http/fs.go @@ -17,6 +17,7 @@ import ( "os" "path" "path/filepath" + "sort" "strconv" "strings" "time" @@ -62,30 +63,34 @@ type FileSystem interface { type File interface { io.Closer io.Reader + io.Seeker Readdir(count int) ([]os.FileInfo, error) - Seek(offset int64, whence int) (int64, error) Stat() (os.FileInfo, error) } func dirList(w ResponseWriter, f File) { + dirs, err := f.Readdir(-1) + if err != nil { + // TODO: log err.Error() to the Server.ErrorLog, once it's possible + // for a handler to get at its Server via the ResponseWriter. See + // Issue 12438. + Error(w, "Error reading directory", StatusInternalServerError) + return + } + sort.Sort(byName(dirs)) + w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprintf(w, "<pre>\n") - for { - dirs, err := f.Readdir(100) - if err != nil || len(dirs) == 0 { - break - } - for _, d := range dirs { - name := d.Name() - if d.IsDir() { - name += "/" - } - // name may contain '?' or '#', which must be escaped to remain - // part of the URL path, and not indicate the start of a query - // string or fragment. - url := url.URL{Path: name} - fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", url.String(), htmlReplacer.Replace(name)) + for _, d := range dirs { + name := d.Name() + if d.IsDir() { + name += "/" } + // name may contain '?' or '#', which must be escaped to remain + // part of the URL path, and not indicate the start of a query + // string or fragment. + url := url.URL{Path: name} + fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", url.String(), htmlReplacer.Replace(name)) } fmt.Fprintf(w, "</pre>\n") } @@ -364,8 +369,8 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec } defer f.Close() - d, err1 := f.Stat() - if err1 != nil { + d, err := f.Stat() + if err != nil { msg, code := toHTTPError(err) Error(w, msg, code) return @@ -446,15 +451,44 @@ func localRedirect(w ResponseWriter, r *Request, newPath string) { // ServeFile replies to the request with the contents of the named // file or directory. // +// If the provided file or direcory name is a relative path, it is +// interpreted 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. +// // As a special case, ServeFile redirects any request where r.URL.Path // ends in "/index.html" to the same path, without the final // "index.html". To avoid such redirects either modify the path or // use ServeContent. func ServeFile(w ResponseWriter, r *Request, 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). + Error(w, "invalid URL path", StatusBadRequest) + return + } dir, file := filepath.Split(name) serveFile(w, r, Dir(dir), file, 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 == '\\' } + type fileHandler struct { root FileSystem } @@ -585,3 +619,9 @@ func sumRangesSize(ranges []httpRange) (size int64) { } return } + +type byName []os.FileInfo + +func (s byName) Len() int { return len(s) } +func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() } +func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |