author | Andreu Botella <abb@randomunok.com> |
Mon, 24 May 2021 13:17:57 +0000 | |
changeset 580517 | 6daedfe5f8fea1d6fb4b631d3aaae3d0078b355d |
parent 580516 | 3e05faaa217db6f534c4c62da4a5f32d4bcbcba3 |
child 580518 | f4e6c9f9222e429bfb4b27a056f96ac99fb2b66c |
push id | 38488 |
push user | nbeleuzu@mozilla.com |
push date | Mon, 24 May 2021 21:58:32 +0000 |
treeherder | mozilla-central@9635f4a632cb [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mixedpuppy |
bugs | 1697292, 1686765 |
milestone | 90.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/toolkit/components/extensions/test/mochitest/test_ext_webrequest_upload.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_upload.html @@ -10,17 +10,17 @@ </head> <body> <form method="post" action="file_WebRequest_page3.html?trigger=form" target="_blank" enctype="multipart/form-data" > -<input type="text" name=""special" ch�rs" value="sp�cial"> +<input type="text" name=""special" 
 ch�rs" value="sp�cial"> <input type="file" name="testFile"> <input type="file" name="emptyFile"> <input type="text" name="textInput1" value="value1"> </form> <form method="post" action="file_WebRequest_page3.html?trigger=form" target="_blank" @@ -201,13 +201,65 @@ add_task(async function test_xhr_forms() action.searchParams.set("upload", `${blob.content.size} bytes`); await post(blob.content); let byteLength = 16; action.searchParams.set("upload", `${byteLength} bytes`); await post(new ArrayBuffer(byteLength)); } + // Testing the decoding of percent escapes even in cases where the + // multipart/form-data serializer won't emit them. + { + let boundary = "-".repeat(27); + for (let i = 0; i < 3; i++) { + const randomNumber = Math.floor(Math.random() * (2 ** 32)); + boundary += String(randomNumber); + } + + const formPayload = [ + `--${boundary}`, + 'Content-Disposition: form-data; name="percent escapes other than%20quotes and newlines"', + "", + "", + `--${boundary}`, + 'Content-Disposition: form-data; name="valid UTF-8: %F0%9F%92%A9"', + "", + "", + `--${boundary}`, + 'Content-Disposition: form-data; name="broken UTF-8: %F0%9F %92%A9"', + "", + "", + `--${boundary}`, + 'Content-Disposition: form-data; name="percent escapes aren\'t decoded in filenames"; filename="%0D%0A%22"', + "Content-Type: application/octet-stream", + "", + "", + `--${boundary}--`, + "" + ].join("\r\n"); + + const action = new URL("file_WebRequest_page3.html?trigger=form", document.location.href); + action.searchParams.set("xhr", "1"); + action.searchParams.set("upload", JSON.stringify({ + "percent escapes other than quotes and newlines": [""], + "valid UTF-8: 💩": [""], + "broken UTF-8: � ��": [""], + "percent escapes aren't decoded in filenames": ["%0D%0A%22"] + })); + action.searchParams.set("enctype", "multipart/form-data"); + + await fetch( + action.href, + { + method: "POST", + headers: {"Content-Type": `multipart/form-data; boundary=${boundary}`}, + body: formPayload + }, + ); + await doneAndTabClosed(); + } + await extension.unload(); }); </script> </body> </html>
--- a/toolkit/components/extensions/webrequest/WebRequestUpload.jsm +++ b/toolkit/components/extensions/webrequest/WebRequestUpload.jsm @@ -346,19 +346,31 @@ function parseFormData(stream, channel, !name || headers.getParam("content-disposition", "") !== "form-data" ) { throw new Error( "Invalid MIME stream: No valid Content-Disposition header" ); } + // Decode the percent-escapes in the name. Unlike with decodeURIComponent, + // partial percent-escapes are passed through as is rather than throwing + // exceptions. + name = name.replace(/(%[0-9A-Fa-f]{2})+/g, match => { + const bytes = new Uint8Array(match.length / 3); + for (let i = 0; i < match.length / 3; i++) { + bytes[i] = parseInt(match.substring(i * 3 + 1, (i + 1) * 3), 16); + } + return new TextDecoder("utf-8").decode(bytes); + }); + if (headers.has("content-type")) { // For file upload fields, we return the filename, rather than the - // file data. + // file data. We're following Chrome in not percent-decoding the + // filename. let filename = headers.getParam("content-disposition", "filename"); content = filename || ""; } formData.get(name).push(content); } return formData; }