diff --git a/src/mime/multipart/writer.go b/src/mime/multipart/writer.go index 8806ab960b8..5d140693699 100644 --- a/src/mime/multipart/writer.go +++ b/src/mime/multipart/writer.go @@ -125,8 +125,20 @@ func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, error) { return p, nil } -var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") +var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"", "\r", "%0D", "\n", "%0A") +// escapeQuotes escapes special characters in field parameter values. +// +// For historical reasons, this uses \ escaping for " and \ characters, +// and percent encoding for CR and LF. +// +// The WhatWG specification for form data encoding suggests that we should +// use percent encoding for " (%22), and should not escape \. +// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart/form-data-encoding-algorithm +// +// Empirically, as of the time this comment was written, it is necessary +// to escape \ characters or else Chrome (and possibly other browsers) will +// interpet the unescaped \ as an escape. func escapeQuotes(s string) string { return quoteEscaper.Replace(s) } diff --git a/src/mime/multipart/writer_test.go b/src/mime/multipart/writer_test.go index 4af6d8c597d..c234b961080 100644 --- a/src/mime/multipart/writer_test.go +++ b/src/mime/multipart/writer_test.go @@ -184,6 +184,7 @@ func TestFileContentDisposition(t *testing.T) { {`somefield`, `somefile"withquotes".txt`, `form-data; name="somefield"; filename="somefile\"withquotes\".txt"`}, {`somefield\withbackslash`, "somefile.txt", `form-data; name="somefield\\withbackslash"; filename="somefile.txt"`}, {"somefield", `somefile\withbackslash.txt`, `form-data; name="somefield"; filename="somefile\\withbackslash.txt"`}, + {"a\rb\nc", "e\rf\ng", `form-data; name="a%0Db%0Ac"; filename="e%0Df%0Ag"`}, } for i, tt := range tests { if found := FileContentDisposition(tt.fieldname, tt.filename); found != tt.want {