| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package http_test |
| |
| import ( |
| "io/fs" |
| "log" |
| "net/http" |
| "os" |
| "slices" |
| "strings" |
| ) |
| |
| // containsDotFile reports whether name contains a path element starting with a period. |
| // The name is delimited by forward slashes, as guaranteed by the fs.FS interface. |
| func containsDotFile(name string) bool { |
| if name == "." { |
| return false // allow the root directory, ".". |
| } |
| for part := range strings.SplitSeq(name, "/") { |
| if strings.HasPrefix(part, ".") { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // dotFileHidingFile is the fs.File use in dotFileHidingFileSystem. |
| // It is used to wrap the Readdir method of fs.ReadDirFile so that we can |
| // remove files and directories that start with a period from its output. |
| type dotFileHidingFile struct { |
| fs.ReadDirFile |
| } |
| |
| // Readdir is a wrapper around the Readdir method of the embedded File |
| // that filters out all files that start with a period in their name. |
| func (f dotFileHidingFile) ReadDir(n int) ([]fs.DirEntry, error) { |
| ents, err := f.ReadDirFile.ReadDir(n) |
| ents = slices.DeleteFunc(ents, func(ent fs.DirEntry) bool { |
| return strings.HasPrefix(ent.Name(), ".") |
| }) |
| return ents, err |
| } |
| |
| // dotFileHidingFileSystem is an http.FileSystem that hides |
| // hidden "dot files" from being served. |
| type dotFileHidingFileSystem struct { |
| fs.FS |
| } |
| |
| // Open is a wrapper around the Open method of the embedded FileSystem |
| // that serves a 403 permission error when name has a file or directory |
| // with whose name starts with a period in its path. |
| func (fsys dotFileHidingFileSystem) Open(name string) (fs.File, error) { |
| if containsDotFile(name) { // If dot file, return 403 response |
| return nil, fs.ErrPermission |
| } |
| file, err := fsys.FS.Open(name) |
| if rdf, ok := file.(fs.ReadDirFile); ok { |
| file = dotFileHidingFile{rdf} |
| } |
| return file, err |
| } |
| |
| // FileServerFS will serve files starting with a dot, which can expose sensitive |
| // directories such as .git or sensitive files such as .htpassword. |
| // |
| // This example demonstrates hiding dot files by wrapping the fs.FS. |
| func ExampleFileServerFS_dotFileHiding() { |
| root, err := os.OpenRoot("doc") |
| if err != nil { |
| log.Fatal(err) |
| } |
| fsys := dotFileHidingFileSystem{root.FS()} |
| handler := http.FileServerFS(fsys) |
| log.Fatal(http.ListenAndServe(":8080", handler)) |
| } |