mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 10:37:24 -04:00 
			
		
		
		
	browse: Jail the root directory using http.Dir()
This commit is contained in:
		
							parent
							
								
									a41e3d2515
								
							
						
					
					
						commit
						72bc6932b0
					
				| @ -3,6 +3,7 @@ package setup | |||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | 	"net/http" | ||||||
| 	"text/template" | 	"text/template" | ||||||
| 
 | 
 | ||||||
| 	"github.com/mholt/caddy/middleware" | 	"github.com/mholt/caddy/middleware" | ||||||
| @ -17,7 +18,6 @@ func Browse(c *Controller) (middleware.Middleware, error) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	browse := browse.Browse{ | 	browse := browse.Browse{ | ||||||
| 		Root:          c.Root, |  | ||||||
| 		Configs:       configs, | 		Configs:       configs, | ||||||
| 		IgnoreIndexes: false, | 		IgnoreIndexes: false, | ||||||
| 	} | 	} | ||||||
| @ -50,6 +50,16 @@ func browseParse(c *Controller) ([]browse.Config, error) { | |||||||
| 		} else { | 		} else { | ||||||
| 			bc.PathScope = "/" | 			bc.PathScope = "/" | ||||||
| 		} | 		} | ||||||
|  | 		bc.Root = http.Dir(c.Root) | ||||||
|  | 		theRoot, err := bc.Root.Open("/") // catch a missing path early | ||||||
|  | 		if err != nil { | ||||||
|  | 			return configs, err | ||||||
|  | 		} | ||||||
|  | 		defer theRoot.Close() | ||||||
|  | 		_, err = theRoot.Readdir(-1) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return configs, err | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		// Second argument would be the template file to use | 		// Second argument would be the template file to use | ||||||
| 		var tplText string | 		var tplText string | ||||||
|  | |||||||
| @ -9,7 +9,6 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path" | 	"path" | ||||||
| 	"path/filepath" |  | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| @ -24,7 +23,6 @@ import ( | |||||||
| // directories in the given paths are specified. | // directories in the given paths are specified. | ||||||
| type Browse struct { | type Browse struct { | ||||||
| 	Next          middleware.Handler | 	Next          middleware.Handler | ||||||
| 	Root          string |  | ||||||
| 	Configs       []Config | 	Configs       []Config | ||||||
| 	IgnoreIndexes bool | 	IgnoreIndexes bool | ||||||
| } | } | ||||||
| @ -32,6 +30,7 @@ type Browse struct { | |||||||
| // Config is a configuration for browsing in a particular path. | // Config is a configuration for browsing in a particular path. | ||||||
| type Config struct { | type Config struct { | ||||||
| 	PathScope string | 	PathScope string | ||||||
|  | 	Root      http.FileSystem | ||||||
| 	Variables interface{} | 	Variables interface{} | ||||||
| 	Template  *template.Template | 	Template  *template.Template | ||||||
| } | } | ||||||
| @ -247,8 +246,7 @@ func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { | |||||||
| inScope: | inScope: | ||||||
| 
 | 
 | ||||||
| 	// Browse works on existing directories; delegate everything else | 	// Browse works on existing directories; delegate everything else | ||||||
| 	requestedFilepath := filepath.Join(b.Root, r.URL.Path) | 	requestedFilepath, err := bc.Root.Open(r.URL.Path) | ||||||
| 	info, err := os.Stat(requestedFilepath) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		switch { | 		switch { | ||||||
| 		case os.IsPermission(err): | 		case os.IsPermission(err): | ||||||
| @ -259,6 +257,19 @@ inScope: | |||||||
| 			return b.Next.ServeHTTP(w, r) | 			return b.Next.ServeHTTP(w, r) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	defer requestedFilepath.Close() | ||||||
|  | 
 | ||||||
|  | 	info, err := requestedFilepath.Stat() | ||||||
|  | 	if err != nil { | ||||||
|  | 		switch { | ||||||
|  | 		case os.IsPermission(err): | ||||||
|  | 			return http.StatusForbidden, err | ||||||
|  | 		case os.IsExist(err): | ||||||
|  | 			return http.StatusGone, err | ||||||
|  | 		default: | ||||||
|  | 			return b.Next.ServeHTTP(w, r) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	if !info.IsDir() { | 	if !info.IsDir() { | ||||||
| 		return b.Next.ServeHTTP(w, r) | 		return b.Next.ServeHTTP(w, r) | ||||||
| 	} | 	} | ||||||
| @ -283,15 +294,8 @@ inScope: | |||||||
| 	return b.ServeListing(w, r, requestedFilepath, bc) | 	return b.ServeListing(w, r, requestedFilepath, bc) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (b Browse) loadDirectoryContents(requestedFilepath, urlPath string) (*Listing, bool, error) { | func (b Browse) loadDirectoryContents(requestedFilepath http.File, urlPath string) (*Listing, bool, error) { | ||||||
| 	// Load directory contents | 	files, err := requestedFilepath.Readdir(-1) | ||||||
| 	file, err := os.Open(requestedFilepath) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, false, err |  | ||||||
| 	} |  | ||||||
| 	defer file.Close() |  | ||||||
| 
 |  | ||||||
| 	files, err := file.Readdir(-1) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, false, err | 		return nil, false, err | ||||||
| 	} | 	} | ||||||
| @ -351,7 +355,7 @@ func (b Browse) handleSortOrder(w http.ResponseWriter, r *http.Request, scope st | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ServeListing returns a formatted view of 'requestedFilepath' contents'. | // ServeListing returns a formatted view of 'requestedFilepath' contents'. | ||||||
| func (b Browse) ServeListing(w http.ResponseWriter, r *http.Request, requestedFilepath string, bc *Config) (int, error) { | func (b Browse) ServeListing(w http.ResponseWriter, r *http.Request, requestedFilepath http.File, bc *Config) (int, error) { | ||||||
| 	listing, containsIndex, err := b.loadDirectoryContents(requestedFilepath, r.URL.Path) | 	listing, containsIndex, err := b.loadDirectoryContents(requestedFilepath, r.URL.Path) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		switch { | 		switch { | ||||||
| @ -367,7 +371,7 @@ func (b Browse) ServeListing(w http.ResponseWriter, r *http.Request, requestedFi | |||||||
| 		return b.Next.ServeHTTP(w, r) | 		return b.Next.ServeHTTP(w, r) | ||||||
| 	} | 	} | ||||||
| 	listing.Context = middleware.Context{ | 	listing.Context = middleware.Context{ | ||||||
| 		Root: http.Dir(b.Root), | 		Root: bc.Root, | ||||||
| 		Req:  r, | 		Req:  r, | ||||||
| 		URL:  r.URL, | 		URL:  r.URL, | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -114,10 +114,10 @@ func TestBrowseHTTPMethods(t *testing.T) { | |||||||
| 		Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { | 		Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { | ||||||
| 			return http.StatusTeapot, nil // not t.Fatalf, or we will not see what other methods yield | 			return http.StatusTeapot, nil // not t.Fatalf, or we will not see what other methods yield | ||||||
| 		}), | 		}), | ||||||
| 		Root: "./testdata", |  | ||||||
| 		Configs: []Config{ | 		Configs: []Config{ | ||||||
| 			{ | 			{ | ||||||
| 				PathScope: "/photos", | 				PathScope: "/photos", | ||||||
|  | 				Root:      http.Dir("./testdata"), | ||||||
| 				Template:  tmpl, | 				Template:  tmpl, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @ -153,10 +153,10 @@ func TestBrowseTemplate(t *testing.T) { | |||||||
| 			t.Fatalf("Next shouldn't be called") | 			t.Fatalf("Next shouldn't be called") | ||||||
| 			return 0, nil | 			return 0, nil | ||||||
| 		}), | 		}), | ||||||
| 		Root: "./testdata", |  | ||||||
| 		Configs: []Config{ | 		Configs: []Config{ | ||||||
| 			{ | 			{ | ||||||
| 				PathScope: "/photos", | 				PathScope: "/photos", | ||||||
|  | 				Root:      http.Dir("./testdata"), | ||||||
| 				Template:  tmpl, | 				Template:  tmpl, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @ -208,16 +208,16 @@ func TestBrowseJson(t *testing.T) { | |||||||
| 			t.Fatalf("Next shouldn't be called") | 			t.Fatalf("Next shouldn't be called") | ||||||
| 			return 0, nil | 			return 0, nil | ||||||
| 		}), | 		}), | ||||||
| 		Root: "./testdata", |  | ||||||
| 		Configs: []Config{ | 		Configs: []Config{ | ||||||
| 			{ | 			{ | ||||||
| 				PathScope: "/photos/", | 				PathScope: "/photos/", | ||||||
|  | 				Root:      http.Dir("./testdata"), | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//Getting the listing from the ./testdata/photos, the listing returned will be used to validate test results | 	//Getting the listing from the ./testdata/photos, the listing returned will be used to validate test results | ||||||
| 	testDataPath := b.Root + "/photos/" | 	testDataPath := filepath.Join("./testdata", "photos") | ||||||
| 	file, err := os.Open(testDataPath) | 	file, err := os.Open(testDataPath) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if os.IsPermission(err) { | 		if os.IsPermission(err) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user