diff options
author | Andreu Botella <abb@randomunok.com> | 2021-06-03 20:48:09 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-03 20:48:09 +0200 |
commit | 03184aeabb3e5f70aa9e66fd279b0285b810a830 (patch) | |
tree | 8a7a9701f4b0d69ae3717654dd46af742b5b9a85 | |
parent | 55e962b6886ad5b3d39ecb7b146f646f3081b659 (diff) |
fix(fetch): implement newline normalization and escapes in the multipart/form-data serializer (#10832)
-rw-r--r-- | extensions/fetch/21_formdata.js | 37 | ||||
-rw-r--r-- | tools/wpt/expectation.json | 12 |
2 files changed, 36 insertions, 13 deletions
diff --git a/extensions/fetch/21_formdata.js b/extensions/fetch/21_formdata.js index db4cfafa3..7b519ddc2 100644 --- a/extensions/fetch/21_formdata.js +++ b/extensions/fetch/21_formdata.js @@ -323,11 +323,13 @@ filename, type, ) { + const escapedField = this.#headerEscape(field); + const escapedFilename = this.#headerEscape(filename, true); /** @type {[string, string][]} */ const headers = [ [ "Content-Disposition", - `form-data; name="${field}"; filename="${filename}"`, + `form-data; name="${escapedField}"; filename="${escapedFilename}"`, ], ["Content-Type", type || "application/octet-stream"], ]; @@ -340,7 +342,10 @@ */ #writeFieldHeaders(field) { /** @type {[string, string][]} */ - const headers = [["Content-Disposition", `form-data; name="${field}"`]]; + const headers = [[ + "Content-Disposition", + `form-data; name="${this.#headerEscape(field)}"`, + ]]; return this.#writeHeaders(headers); } @@ -351,7 +356,7 @@ */ #writeField(field, value) { this.#writeFieldHeaders(field); - this.chunks.push(encoder.encode(value)); + this.chunks.push(encoder.encode(this.#normalizeNewlines(value))); } /** @@ -363,6 +368,32 @@ this.#writeFileHeaders(field, value.name, value.type); this.chunks.push(value[_byteSequence]); } + + /** + * @param {string} string + * @returns {string} + */ + #normalizeNewlines(string) { + return string.replace(/\r(?!\n)|(?<!\r)\n/g, "\r\n"); + } + + /** + * Performs the percent-escaping and the normalization required for field + * names and filenames in Content-Disposition headers. + * @param {string} name + * @param {boolean} isFilename Whether we are encoding a filename. This + * skips the newline normalization that takes place for field names. + * @returns {string} + */ + #headerEscape(name, isFilename = false) { + if (!isFilename) { + name = this.#normalizeNewlines(name); + } + return name + .replaceAll("\n", "%0A") + .replaceAll("\r", "%0D") + .replaceAll('"', "%22"); + } } /** diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json index a18e16b8f..f12aa16fd 100644 --- a/tools/wpt/expectation.json +++ b/tools/wpt/expectation.json @@ -990,16 +990,8 @@ }, "file": { "File-constructor.any.html": true, - "send-file-formdata-controls.any.html": [ - "Upload file-for-upload-in-form-LF-[\n].txt (ASCII) in fetch with FormData", - "Upload file-for-upload-in-form-LF-CR-[\n\r].txt (ASCII) in fetch with FormData", - "Upload file-for-upload-in-form-CR-[\r].txt (ASCII) in fetch with FormData", - "Upload file-for-upload-in-form-CR-LF-[\r\n].txt (ASCII) in fetch with FormData" - ], - "send-file-formdata-punctuation.any.html": [ - "Upload file-for-upload-in-form-QUOTATION-MARK-[\"].txt (ASCII) in fetch with FormData", - "Upload \"file-for-upload-in-form-double-quoted.txt\" (ASCII) in fetch with FormData" - ], + "send-file-formdata-controls.any.html": true, + "send-file-formdata-punctuation.any.html": true, "send-file-formdata-utf-8.any.html": true, "send-file-formdata.any.html": true }, |