Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mime/multipart: add field Reader.MaxMIMEHeaderSize #70933

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions src/mime/multipart/multipart.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,11 @@ func (p *Part) Close() error {
// Reader's underlying parser consumes its input as needed. Seeking
// isn't supported.
type Reader struct {
// MaxMIMEHeaderSize is the maximum size of a MIME header we will parse,
// including header keys, values, and map overhead. If not set, then the
// default value of 10 << 20 is used.
MaxMIMEHeaderSize int64

bufReader *bufio.Reader
tempDir string // used in tests

Expand Down Expand Up @@ -362,14 +367,25 @@ func maxMIMEHeaders() int64 {
return 10000
}

// maxMIMEHeaderSize returns the maximum size of a MIME header we will parse.
// It uses the value of Reader.MaxMIMEHeaderSize if it is not 0, otherwise the
// value of maxMIMEHeaderSize constant is used.
func (r *Reader) maxMIMEHeaderSize() int64 {
if r.MaxMIMEHeaderSize != 0 {
return r.MaxMIMEHeaderSize
} else {
return maxMIMEHeaderSize
}
}

// NextPart returns the next part in the multipart or an error.
// When there are no more parts, the error [io.EOF] is returned.
//
// As a special case, if the "Content-Transfer-Encoding" header
// has a value of "quoted-printable", that header is instead
// hidden and the body is transparently decoded during Read calls.
func (r *Reader) NextPart() (*Part, error) {
return r.nextPart(false, maxMIMEHeaderSize, maxMIMEHeaders())
return r.nextPart(false, r.maxMIMEHeaderSize(), maxMIMEHeaders())
}

// NextRawPart returns the next part in the multipart or an error.
Expand All @@ -378,7 +394,7 @@ func (r *Reader) NextPart() (*Part, error) {
// Unlike [Reader.NextPart], it does not have special handling for
// "Content-Transfer-Encoding: quoted-printable".
func (r *Reader) NextRawPart() (*Part, error) {
return r.nextPart(true, maxMIMEHeaderSize, maxMIMEHeaders())
return r.nextPart(true, r.maxMIMEHeaderSize(), maxMIMEHeaders())
}

func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize, maxMIMEHeaders int64) (*Part, error) {
Expand Down
35 changes: 35 additions & 0 deletions src/mime/multipart/multipart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,41 @@ Cases:
}
}

func TestParseMaxMIMEHeaderSize(t *testing.T) {
sep := "MyBoundary"
in := `This is a multi-part message. This line is ignored.
--MyBoundary
Header1: value1
Header2: value2
Header3: value3
Header4: value4
Header5: value5
Header6: value6
Header7: value7
Header8: value8

My value
The end.
--MyBoundary--`

in = strings.Replace(in, "\n", "\r\n", -1)

// Control.
r := NewReader(strings.NewReader(in), sep)
_, err := r.NextPart()
if err != nil {
t.Fatalf("control failed: %v", err)
}

// Test MaxMIMEHeaderSize.
r = NewReader(strings.NewReader(in), sep)
r.MaxMIMEHeaderSize = 100
_, err = r.NextPart()
if err != ErrMessageTooLarge {
t.Fatalf("test failed: %v", err)
}
}

func partsFromReader(r *Reader) ([]headerBody, error) {
got := []headerBody{}
for {
Expand Down