From 784ef4c53135644d70f3476a4bd90010b9acff66 Mon Sep 17 00:00:00 2001 From: Katie Hockman Date: Mon, 26 Apr 2021 18:46:22 -0400 Subject: [PATCH] net/http: ignore directory path when parsing multipart forms Fixes #45789 Change-Id: Id588f5dbbecf5fbfb54e957c53903aaa900171f2 Reviewed-on: https://go-review.googlesource.com/c/go/+/313809 Trust: Katie Hockman Run-TryBot: Katie Hockman TryBot-Result: Go Bot Reviewed-by: Roland Shoemaker --- src/mime/multipart/multipart.go | 14 +++++++++++--- src/net/http/request_test.go | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go index cb8bf393383..81bf722d4ee 100644 --- a/src/mime/multipart/multipart.go +++ b/src/mime/multipart/multipart.go @@ -20,6 +20,7 @@ import ( "mime" "mime/quotedprintable" "net/textproto" + "path/filepath" "strings" ) @@ -67,13 +68,20 @@ func (p *Part) FormName() string { return p.dispositionParams["name"] } -// FileName returns the filename parameter of the Part's -// Content-Disposition header. +// FileName returns the filename parameter of the Part's Content-Disposition +// header. If not empty, the filename is passed through filepath.Base (which is +// platform dependent) before being returned. func (p *Part) FileName() string { if p.dispositionParams == nil { p.parseContentDisposition() } - return p.dispositionParams["filename"] + filename := p.dispositionParams["filename"] + if filename == "" { + return "" + } + // RFC 7578, Section 4.2 requires that if a filename is provided, the + // directory path information must not be used. + return filepath.Base(filename) } func (p *Part) parseContentDisposition() { diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go index 07b3d6a1c76..952828b395e 100644 --- a/src/net/http/request_test.go +++ b/src/net/http/request_test.go @@ -245,6 +245,29 @@ func TestParseMultipartForm(t *testing.T) { } } +// Issue 45789: multipart form should not include directory path in filename +func TestParseMultipartFormFilename(t *testing.T) { + postData := + `--xxx +Content-Disposition: form-data; name="file"; filename="../usr/foobar.txt/" +Content-Type: text/plain + +--xxx-- +` + req := &Request{ + Method: "POST", + Header: Header{"Content-Type": {`multipart/form-data; boundary=xxx`}}, + Body: io.NopCloser(strings.NewReader(postData)), + } + _, hdr, err := req.FormFile("file") + if err != nil { + t.Fatal(err) + } + if hdr.Filename != "foobar.txt" { + t.Errorf("expected only the last element of the path, got %q", hdr.Filename) + } +} + // Issue #40430: Test that if maxMemory for ParseMultipartForm when combined with // the payload size and the internal leeway buffer size of 10MiB overflows, that we // correctly return an error.