Source file src/net/http/example_filesystem_test.go

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package http_test
     6  
     7  import (
     8  	"io/fs"
     9  	"log"
    10  	"net/http"
    11  	"os"
    12  	"slices"
    13  	"strings"
    14  )
    15  
    16  // containsDotFile reports whether name contains a path element starting with a period.
    17  // The name is delimited by forward slashes, as guaranteed by the fs.FS interface.
    18  func containsDotFile(name string) bool {
    19  	if name == "." {
    20  		return false // allow the root directory, ".".
    21  	}
    22  	for part := range strings.SplitSeq(name, "/") {
    23  		if strings.HasPrefix(part, ".") {
    24  			return true
    25  		}
    26  	}
    27  	return false
    28  }
    29  
    30  // dotFileHidingFile is the fs.File use in dotFileHidingFileSystem.
    31  // It is used to wrap the Readdir method of fs.ReadDirFile so that we can
    32  // remove files and directories that start with a period from its output.
    33  type dotFileHidingFile struct {
    34  	fs.ReadDirFile
    35  }
    36  
    37  // Readdir is a wrapper around the Readdir method of the embedded File
    38  // that filters out all files that start with a period in their name.
    39  func (f dotFileHidingFile) ReadDir(n int) ([]fs.DirEntry, error) {
    40  	ents, err := f.ReadDirFile.ReadDir(n)
    41  	ents = slices.DeleteFunc(ents, func(ent fs.DirEntry) bool {
    42  		return strings.HasPrefix(ent.Name(), ".")
    43  	})
    44  	return ents, err
    45  }
    46  
    47  // dotFileHidingFileSystem is an http.FileSystem that hides
    48  // hidden "dot files" from being served.
    49  type dotFileHidingFileSystem struct {
    50  	fs.FS
    51  }
    52  
    53  // Open is a wrapper around the Open method of the embedded FileSystem
    54  // that serves a 403 permission error when name has a file or directory
    55  // with whose name starts with a period in its path.
    56  func (fsys dotFileHidingFileSystem) Open(name string) (fs.File, error) {
    57  	if containsDotFile(name) { // If dot file, return 403 response
    58  		return nil, fs.ErrPermission
    59  	}
    60  	file, err := fsys.FS.Open(name)
    61  	if rdf, ok := file.(fs.ReadDirFile); ok {
    62  		file = dotFileHidingFile{rdf}
    63  	}
    64  	return file, err
    65  }
    66  
    67  // FileServerFS will serve files starting with a dot, which can expose sensitive
    68  // directories such as .git or sensitive files such as .htpassword.
    69  //
    70  // This example demonstrates hiding dot files by wrapping the fs.FS.
    71  func ExampleFileServerFS_dotFileHiding() {
    72  	root, err := os.OpenRoot("doc")
    73  	if err != nil {
    74  		log.Fatal(err)
    75  	}
    76  	fsys := dotFileHidingFileSystem{root.FS()}
    77  	handler := http.FileServerFS(fsys)
    78  	log.Fatal(http.ListenAndServe(":8080", handler))
    79  }
    80  

View as plain text