]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: make http.FileServer return 404 when a path is invalid/unsafe
authorGrégoire Lodi <pro@lodi.me>
Wed, 5 Mar 2025 09:44:46 +0000 (09:44 +0000)
committerGopher Robot <gobot@golang.org>
Wed, 5 Mar 2025 19:26:39 +0000 (11:26 -0800)
This PR adds error handling in net/http toHTTPError to return a 404
instead of a 500 when net/http fs.Dir.Open throws the error http:
invalid or unsafe file path.

Fixes #72091

Change-Id: I7941c8fca5160a4a82732dc1d05b9b95eac84fbf
GitHub-Last-Rev: 04b5019dfb629820621f3776d6f22fd754171565
GitHub-Pull-Request: golang/go#72108
Reviewed-on: https://go-review.googlesource.com/c/go/+/654975
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Damien Neil <dneil@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
src/net/http/fs.go
src/net/http/fs_test.go

index 48ba05a664dbafaefc385fecf5455e4d95268560..92bd94f72dfb685bf874d79f233c25cb91e66b48 100644 (file)
@@ -67,6 +67,11 @@ func mapOpenError(originalErr error, name string, sep rune, stat func(string) (f
        return originalErr
 }
 
+// errInvalidUnsafePath is returned by Dir.Open when the call to
+// filepath.Localize fails. filepath.Localize returns an error if the path
+// cannot be represented by the operating system.
+var errInvalidUnsafePath = errors.New("http: invalid or unsafe file path")
+
 // Open implements [FileSystem] using [os.Open], opening files for reading rooted
 // and relative to the directory d.
 func (d Dir) Open(name string) (File, error) {
@@ -76,7 +81,7 @@ func (d Dir) Open(name string) (File, error) {
        }
        path, err := filepath.Localize(path)
        if err != nil {
-               return nil, errors.New("http: invalid or unsafe file path")
+               return nil, errInvalidUnsafePath
        }
        dir := string(d)
        if dir == "" {
@@ -768,6 +773,9 @@ func toHTTPError(err error) (msg string, httpStatus int) {
        if errors.Is(err, fs.ErrPermission) {
                return "403 Forbidden", StatusForbidden
        }
+       if errors.Is(err, errInvalidUnsafePath) {
+               return "404 page not found", StatusNotFound
+       }
        // Default:
        return "500 Internal Server Error", StatusInternalServerError
 }
index 3149ca35acd2a297f323dd66f3e6dd6769d12902..9b34ad080efe1ae2b9e9692fa344ea3aea0bd3e6 100644 (file)
@@ -733,6 +733,27 @@ func testFileServerZeroByte(t *testing.T, mode testMode) {
        }
 }
 
+func TestFileServerNullByte(t *testing.T) { run(t, testFileServerNullByte) }
+func testFileServerNullByte(t *testing.T, mode testMode) {
+       ts := newClientServerTest(t, mode, FileServer(Dir("testdata"))).ts
+
+       for _, path := range []string{
+               "/file%00",
+               "/%00",
+               "/file/qwe/%00",
+       } {
+               res, err := ts.Client().Get(ts.URL + path)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               res.Body.Close()
+               if res.StatusCode != 404 {
+                       t.Errorf("Get(%q): got status %v, want 404", path, res.StatusCode)
+               }
+
+       }
+}
+
 func TestFileServerNamesEscape(t *testing.T) { run(t, testFileServerNamesEscape) }
 func testFileServerNamesEscape(t *testing.T, mode testMode) {
        ts := newClientServerTest(t, mode, FileServer(Dir("testdata"))).ts