Bug 1677832 [wpt PR 26556] - Test multipart/form-data form submission with controls and punctuation., a=testonly

Automatic update from web-platform-tests
Test multipart/form-data form submission with controls and punctuation

This PR adds tests for forms containing controls and punctuation, both
on multipart/form-data request bodies and on FormData objects.

This is in part a modification of #8618, which got closed downstream due
to inactivity.

Co-Authored-By: Benjamin C. Wiley Sittler <bsittler@chromium.org>
--

wpt-commits: ebb2824cabb11ff1ef3bfce1b7530980efbc916d
wpt-pr: 26556
This commit is contained in:
Andreu Botella
2020-11-20 16:04:53 +00:00
committed by moz-wptsync-bot
parent d00f4b60c2
commit ae83fb121d
13 changed files with 952 additions and 43 deletions

View File

@@ -0,0 +1,24 @@
from wptserve.utils import isomorphic_encode
# Outputs the request body, with controls and non-ASCII bytes escaped
# (b"\n" becomes b"\\x0a"), and with backslashes doubled.
# As a convenience, CRLF newlines are left as is.
def escape_byte(byte):
if b"\0" <= byte <= b"\x1F" or byte >= b"\x7F":
return b"\\x%02x" % ord(byte)
if byte == b"\\":
return b"\\\\"
return byte
def main(request, response):
headers = [(b"X-Request-Method", isomorphic_encode(request.method)),
(b"X-Request-Content-Length", request.headers.get(b"Content-Length", b"NO")),
(b"X-Request-Content-Type", request.headers.get(b"Content-Type", b"NO")),
# Avoid any kind of content sniffing on the response.
(b"Content-Type", b"text/plain; charset=UTF-8")]
content = b"".join(map(escape_byte, request.body)).replace(b"\\x0d\\x0a", b"\r\n")
return headers, content

View File

@@ -0,0 +1,117 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>Upload files named using controls (tentative)</title>
<!--
NOTE: This test is tentative because encoding for filename
control characters is not yet standardized.
-->
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data"
/>
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist"
/>
<link rel="help" href="https://w3c.github.io/FileAPI/#file-constructor" />
<link
rel="author"
title="Benjamin C. Wiley Sittler"
href="mailto:bsittler@chromium.org"
/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/send-file-form-helper.js"></script>
<script>
"use strict";
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-NUL-[\0].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-NUL-[\0].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-BS-[\b].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-BS-[\b].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-VT-[\v].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-VT-[\v].txt",
});
// These have characters that undergo processing in name=,
// filename=, and/or value; formPostFileUploadTest postprocesses
// expectedEncodedBaseName for these internally.
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LF-[\n].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-LF-[\n].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LF-CR-[\n\r].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-LF-CR-[\n\r].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CR-[\r].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-CR-[\r].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CR-LF-[\r\n].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-CR-LF-[\r\n].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-HT-[\t].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-HT-[\t].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-FF-[\f].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-FF-[\f].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-DEL-[\x7F].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-DEL-[\x7F].txt",
});
// The rest should be passed through unmodified:
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-ESC-[\x1B].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-ESC-[\x1B].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SPACE-[ ].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-SPACE-[ ].txt",
});
</script>

View File

@@ -0,0 +1,230 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>Upload files named using punctuation (tentative)</title>
<!--
NOTE: This test is tentative because encoding for filename
punctuation characters is not yet standardized.
-->
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data"
/>
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist"
/>
<link rel="help" href="https://w3c.github.io/FileAPI/#file-constructor" />
<link
rel="author"
title="Benjamin C. Wiley Sittler"
href="mailto:bsittler@chromium.org"
/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/send-file-form-helper.js"></script>
<script>
"use strict";
// These have characters that undergo processing in name=,
// filename=, and/or value; formPostFileUploadTest postprocesses
// expectedEncodedBaseName for these internally.
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-QUOTATION-MARK-[\x22].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-QUOTATION-MARK-[\x22].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: '"file-for-upload-in-form-double-quoted.txt"',
formEncoding: "UTF-8",
expectedEncodedBaseName: '"file-for-upload-in-form-double-quoted.txt"',
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-REVERSE-SOLIDUS-[\\].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-REVERSE-SOLIDUS-[\\].txt",
});
// The rest should be passed through unmodified:
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-EXCLAMATION-MARK-[!].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-EXCLAMATION-MARK-[!].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-DOLLAR-SIGN-[$].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-DOLLAR-SIGN-[$].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-PERCENT-SIGN-[%].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-PERCENT-SIGN-[%].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-AMPERSAND-[&].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-AMPERSAND-[&].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-APOSTROPHE-['].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-APOSTROPHE-['].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-PARENTHESIS-[(].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-LEFT-PARENTHESIS-[(].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-PARENTHESIS-[)].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-RIGHT-PARENTHESIS-[)].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-ASTERISK-[*].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-ASTERISK-[*].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-PLUS-SIGN-[+].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-PLUS-SIGN-[+].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-COMMA-[,].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-COMMA-[,].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-FULL-STOP-[.].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-FULL-STOP-[.].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SOLIDUS-[/].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-SOLIDUS-[/].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-COLON-[:].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-COLON-[:].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SEMICOLON-[;].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-SEMICOLON-[;].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-EQUALS-SIGN-[=].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-EQUALS-SIGN-[=].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-QUESTION-MARK-[?].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-QUESTION-MARK-[?].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CIRCUMFLEX-ACCENT-[^].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-CIRCUMFLEX-ACCENT-[^].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-SQUARE-BRACKET-[[].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-LEFT-SQUARE-BRACKET-[[].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-SQUARE-BRACKET-[]].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-RIGHT-SQUARE-BRACKET-[]].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-CURLY-BRACKET-[{].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-LEFT-CURLY-BRACKET-[{].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-VERTICAL-LINE-[|].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-VERTICAL-LINE-[|].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-CURLY-BRACKET-[}].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-RIGHT-CURLY-BRACKET-[}].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-TILDE-[~].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-TILDE-[~].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "'file-for-upload-in-form-single-quoted.txt'",
formEncoding: "UTF-8",
expectedEncodedBaseName: "'file-for-upload-in-form-single-quoted.txt'",
});
</script>

View File

@@ -27,35 +27,36 @@ formPostFileUploadTest({
fileBaseName: 'file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt',
formEncoding: 'UTF-8',
expectedEncodedBaseName: (
'file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt'),
'file-for-upload-in-form-\xEF\x9F\xB0\xEF\x9E\x93\xEF\x9E\x83\xEF\x9E\xA0.txt'),
});
formPostFileUploadTest({
fileNameSource: 'windows-1252',
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
formEncoding: 'UTF-8',
expectedEncodedBaseName: 'file-for-upload-in-form-☺😂.txt',
expectedEncodedBaseName: (
'file-for-upload-in-form-\xC3\xA2\xCB\x9C\xC2\xBA\xC3\xB0\xC5\xB8\xCB\x9C\xE2\x80\x9A.txt'),
});
formPostFileUploadTest({
fileNameSource: 'JIS X 0201 and JIS X 0208',
fileBaseName: 'file-for-upload-in-form-★星★.txt',
formEncoding: 'UTF-8',
expectedEncodedBaseName: 'file-for-upload-in-form-★星★.txt',
expectedEncodedBaseName: 'file-for-upload-in-form-\xE2\x98\x85\xE6\x98\x9F\xE2\x98\x85.txt',
});
formPostFileUploadTest({
fileNameSource: 'Unicode',
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
formEncoding: 'UTF-8',
expectedEncodedBaseName: 'file-for-upload-in-form-☺😂.txt',
expectedEncodedBaseName: 'file-for-upload-in-form-\xE2\x98\xBA\xF0\x9F\x98\x82.txt',
});
formPostFileUploadTest({
fileNameSource: 'Unicode',
fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
formEncoding: 'UTF-8',
expectedEncodedBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
expectedEncodedBaseName: `file-for-upload-in-form-${kTestFallbackUtf8}.txt`,
});
</script>

View File

@@ -33,15 +33,14 @@ formPostFileUploadTest({
fileNameSource: 'x-user-defined',
fileBaseName: 'file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt',
formEncoding: 'windows-1252',
expectedEncodedBaseName: (
'file-for-upload-in-form-&#63472;&#63379;&#63363;&#63392;.txt'),
expectedEncodedBaseName: 'file-for-upload-in-form-&#63472;&#63379;&#63363;&#63392;.txt',
});
formPostFileUploadTest({
fileNameSource: 'windows-1252',
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
formEncoding: 'windows-1252',
expectedEncodedBaseName: 'file-for-upload-in-form-☺😂.txt',
expectedEncodedBaseName: 'file-for-upload-in-form-\xE2\x98\xBA\xF0\x9F\x98\x82.txt',
});
formPostFileUploadTest({

View File

@@ -33,7 +33,7 @@ formPostFileUploadTest({
fileNameSource: 'x-user-defined',
fileBaseName: 'file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt',
formEncoding: 'x-user-defined',
expectedEncodedBaseName: 'file-for-upload-in-form-𓃠.txt',
expectedEncodedBaseName: 'file-for-upload-in-form-\xF0\x93\x83\xA0.txt',
});
formPostFileUploadTest({

View File

@@ -0,0 +1,93 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>FormData: Upload files named using controls (tentative)</title>
<!--
NOTE: This test is tentative because encoding for filename
control characters is not yet standardized.
-->
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data"
/>
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist"
/>
<link rel="help" href="https://w3c.github.io/FileAPI/#file-constructor" />
<link
rel="author"
title="Benjamin C. Wiley Sittler"
href="mailto:bsittler@chromium.org"
/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/send-file-formdata-helper.js"></script>
<script>
"use strict";
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-NUL-[\0].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-BS-[\b].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-VT-[\v].txt",
});
// These have characters that undergo processing in name=,
// filename=, and/or value; formDataPostFileUploadTest postprocesses
// expectedEncodedBaseName for these internally.
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LF-[\n].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LF-CR-[\n\r].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CR-[\r].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CR-LF-[\r\n].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-HT-[\t].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-FF-[\f].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-DEL-[\x7F].txt",
});
// The rest should be passed through unmodified:
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-ESC-[\x1B].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SPACE-[ ].txt",
});
</script>

View File

@@ -0,0 +1,168 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>FormData: Upload files named using punctuation (tentative)</title>
<!--
NOTE: This test is tentative because encoding for filename
punctuation characters is not yet standardized.
-->
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data"
/>
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist"
/>
<link rel="help" href="https://w3c.github.io/FileAPI/#file-constructor" />
<link
rel="author"
title="Benjamin C. Wiley Sittler"
href="mailto:bsittler@chromium.org"
/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/send-file-formdata-helper.js"></script>
<script>
"use strict";
// These have characters that undergo processing in name=,
// filename=, and/or value; formDataPostFileUploadTest postprocesses
// expectedEncodedBaseName for these internally.
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-QUOTATION-MARK-[\x22].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: '"file-for-upload-in-form-double-quoted.txt"',
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-REVERSE-SOLIDUS-[\\].txt",
});
// The rest should be passed through unmodified:
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-EXCLAMATION-MARK-[!].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-DOLLAR-SIGN-[$].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-PERCENT-SIGN-[%].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-AMPERSAND-[&].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-APOSTROPHE-['].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-PARENTHESIS-[(].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-PARENTHESIS-[)].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-ASTERISK-[*].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-PLUS-SIGN-[+].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-COMMA-[,].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-FULL-STOP-[.].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SOLIDUS-[/].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-COLON-[:].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SEMICOLON-[;].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-EQUALS-SIGN-[=].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-QUESTION-MARK-[?].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CIRCUMFLEX-ACCENT-[^].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-SQUARE-BRACKET-[[].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-SQUARE-BRACKET-[]].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-CURLY-BRACKET-[{].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-VERTICAL-LINE-[|].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-CURLY-BRACKET-[}].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-TILDE-[~].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "'file-for-upload-in-form-single-quoted.txt'",
});
</script>

View File

@@ -0,0 +1,53 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>FormData: Upload files in UTF-8 fetch()</title>
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data"
/>
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist"
/>
<link rel="help" href="https://w3c.github.io/FileAPI/#file-constructor" />
<link
rel="author"
title="Benjamin C. Wiley Sittler"
href="mailto:bsittler@chromium.org"
/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/send-file-formdata-helper.js"></script>
<script>
"use strict";
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form.txt",
});
formDataPostFileUploadTest({
fileNameSource: "x-user-defined",
fileBaseName: "file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt",
});
formDataPostFileUploadTest({
fileNameSource: "windows-1252",
fileBaseName: "file-for-upload-in-form-☺😂.txt",
});
formDataPostFileUploadTest({
fileNameSource: "JIS X 0201 and JIS X 0208",
fileBaseName: "file-for-upload-in-form-★星★.txt",
});
formDataPostFileUploadTest({
fileNameSource: "Unicode",
fileBaseName: "file-for-upload-in-form-☺😂.txt",
});
formDataPostFileUploadTest({
fileNameSource: "Unicode",
fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
});
</script>

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>FormData: Upload ASCII-named file in UTF-8 form</title>
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data"
/>
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist"
/>
<link rel="help" href="https://w3c.github.io/FileAPI/#file-constructor" />
<link
rel="author"
title="Benjamin C. Wiley Sittler"
href="mailto:bsittler@chromium.org"
/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/send-file-formdata-helper.js"></script>
<script>
"use strict";
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form.txt",
});
</script>

View File

@@ -1,5 +1,17 @@
'use strict';
// See /FileAPI/file/resources/echo-content-escaped.py
function escapeString(string) {
return string.replace(/\\/g, "\\\\").replace(
/[^\x20-\x7E]/g,
(x) => {
let hex = x.charCodeAt(0).toString(16);
if (hex.length < 2) hex = "0" + hex;
return `\\x${hex}`;
},
).replace(/\\x0d\\x0a/g, "\r\n");
}
// Rationale for this particular test character sequence, which is
// used in filenames and also in file contents:
//
@@ -72,36 +84,46 @@
// are also allowed in Windows Unicode filenames.
const kTestChars = 'ABC~‾¥≈¤・・•∙·☼★星🌟星★☼·∙•・・¤≈¥‾~XYZ';
// NOTE: The expected interpretation of ISO-2022-JP according to
// https://encoding.spec.whatwg.org/#iso-2022-jp-encoder unifies
// single-byte and double-byte katakana.
const kTestFallbackIso2022jp =
('ABC~\x1B(J~\\≈¤\x1B$B!&!&\x1B(B•∙·☼\x1B$B!z@1\x1B(B🌟' +
'\x1B$B@1!z\x1B(B☼·∙•\x1B$B!&!&\x1B(B¤≈\x1B(J\\~\x1B(B~XYZ').replace(
/[^\0-\x7F]/gu,
x => `&#${x.codePointAt(0)};`);
// The kTestFallback* strings represent the expected byte sequence from
// encoding kTestChars with the given encoding with "html" replacement
// mode, isomorphic-decoded. That means, characters that can't be
// encoded in that encoding get HTML-escaped, but no further
// `escapeString`-like escapes are needed.
const kTestFallbackUtf8 = (
"ABC~\xE2\x80\xBE\xC2\xA5\xE2\x89\x88\xC2\xA4\xEF\xBD\xA5\xE3\x83\xBB\xE2" +
"\x80\xA2\xE2\x88\x99\xC2\xB7\xE2\x98\xBC\xE2\x98\x85\xE6\x98\x9F\xF0\x9F" +
"\x8C\x9F\xE6\x98\x9F\xE2\x98\x85\xE2\x98\xBC\xC2\xB7\xE2\x88\x99\xE2\x80" +
"\xA2\xE3\x83\xBB\xEF\xBD\xA5\xC2\xA4\xE2\x89\x88\xC2\xA5\xE2\x80\xBE~XYZ"
);
// NOTE: \uFFFD is used here to replace Windows-1252 bytes to match
// how we will see them in the reflected POST bytes in a frame using
// UTF-8 byte interpretation. The bytes will actually be intact, but
// this code cannot tell and does not really care.
const kTestFallbackWindows1252 =
'ABC~‾\xA5≈\xA4・・\x95∙\xB7☼★星🌟星★☼\xB7∙\x95・・\xA4≈\xA5‾~XYZ'.replace(
/[^\0-\xFF]/gu,
x => `&#${x.codePointAt(0)};`).replace(/[\x80-\xFF]/g, '\uFFFD');
const kTestFallbackIso2022jp = (
("ABC~\x1B(J~\\≈¤\x1B$B!&!&\x1B(B•∙·☼\x1B$B!z@1\x1B(B🌟" +
"\x1B$B@1!z\x1B(B☼·∙•\x1B$B!&!&\x1B(B¤≈\x1B(J\\~\x1B(B~XYZ")
.replace(/[^\0-\x7F]/gu, (x) => `&#${x.codePointAt(0)};`)
);
const kTestFallbackXUserDefined =
kTestChars.replace(/[^\0-\x7F]/gu, x => `&#${x.codePointAt(0)};`);
const kTestFallbackWindows1252 = (
"ABC~‾\xA5≈\xA4・・\x95∙\xB7☼★星🌟星★☼\xB7∙\x95・・\xA4≈\xA5‾~XYZ".replace(
/[^\0-\xFF]/gu,
(x) => `&#${x.codePointAt(0)};`,
)
);
const kTestFallbackXUserDefined = kTestChars.replace(
/[^\0-\x7F]/gu,
(x) => `&#${x.codePointAt(0)};`,
);
// formPostFileUploadTest - verifies multipart upload structure and
// numeric character reference replacement for filenames, field names,
// and field values.
// and field values using form submission.
//
// Uses /fetch/api/resources/echo-content.py to echo the upload
// POST with UTF-8 byte interpretation, leading to the "UTF-8 goggles"
// behavior documented below for expectedEncodedBaseName when non-
// UTF-8-compatible byte sequences appear in the formEncoding-encoded
// uploaded data.
// Uses /FileAPI/file/resources/echo-content-escaped.py to echo the
// upload POST with controls and non-ASCII bytes escaped. This is done
// because navigations whose response body contains [\0\b\v] may get
// treated as a download, which is not what we want. Use the
// `escapeString` function to replicate that kind of escape (note that
// it takes an isomorphic-decoded string, not a byte sequence).
//
// Fields in the parameter object:
//
@@ -114,10 +136,9 @@ const kTestFallbackXUserDefined =
// - formEncoding: the acceptCharset of the form used to submit the
// test file. Used in the test name.
// - expectedEncodedBaseName: the expected formEncoding-encoded
// version of fileBaseName with unencodable characters replaced by
// numeric character references and non-7-bit-ASCII bytes seen
// through UTF-8 goggles; subsequences not interpretable as UTF-8
// have each byte represented here by \uFFFD REPLACEMENT CHARACTER.
// version of fileBaseName, isomorphic-decoded. That means, characters
// that can't be encoded in that encoding get HTML-escaped, but no
// further `escapeString`-like escapes are needed.
const formPostFileUploadTest = ({
fileNameSource,
fileBaseName,
@@ -140,7 +161,7 @@ const formPostFileUploadTest = ({
const form = Object.assign(document.createElement('form'), {
acceptCharset: formEncoding,
action: '/fetch/api/resources/echo-content.py',
action: '/FileAPI/file/resources/echo-content-escaped.py',
method: 'POST',
enctype: 'multipart/form-data',
target: formTargetFrame.name,
@@ -194,7 +215,7 @@ const formPostFileUploadTest = ({
// exposed through the newer .files[0].name API. This check
// verifies that assumption.
assert_equals(
fileInput.files[0].name,
baseNameOfFilePath(fileInput.files[0].name),
baseNameOfFilePath(fileInput.value),
`The basename of the field's value should match its files[0].name`);
form.submit();
@@ -219,6 +240,15 @@ const formPostFileUploadTest = ({
`${fileBaseName}: multipart form data must end with ${boundary}--: ${
JSON.stringify(formDataText)
}`);
const asValue = expectedEncodedBaseName.replace(/\r\n?|\n/g, "\r\n");
const asName = asValue.replace(/[\r\n"]/g, encodeURIComponent);
const asFilename = expectedEncodedBaseName.replace(/[\r\n"]/g, encodeURIComponent);
// The response body from echo-content-escaped.py has controls and non-ASCII
// bytes escaped, so any caller-provided field that might contain such bytes
// must be passed to `escapeString`, after any other expected
// transformations.
const expectedText = [
boundary,
'Content-Disposition: form-data; name="_charset_"',
@@ -227,19 +257,22 @@ const formPostFileUploadTest = ({
boundary,
'Content-Disposition: form-data; name="filename"',
'',
expectedEncodedBaseName,
// Unlike for names and filenames, multipart/form-data values don't escape
// \r\n linebreaks, and when they're read from an iframe they become \n.
escapeString(asValue).replace(/\r\n/g, "\n"),
boundary,
`Content-Disposition: form-data; name="${expectedEncodedBaseName}"`,
`Content-Disposition: form-data; name="${escapeString(asName)}"`,
'',
'filename',
boundary,
`Content-Disposition: form-data; name="file"; ` +
`filename="${expectedEncodedBaseName}"`,
`filename="${escapeString(asFilename)}"`,
'Content-Type: text/plain',
'',
kTestChars,
escapeString(kTestFallbackUtf8),
boundary + '--',
].join('\n');
assert_true(
formDataText.startsWith(expectedText),
`Unexpected multipart-shaped form data received:\n${

View File

@@ -0,0 +1,97 @@
"use strict";
const kTestChars = "ABC~‾¥≈¤・・•∙·☼★星🌟星★☼·∙•・・¤≈¥‾~XYZ";
// formDataPostFileUploadTest - verifies multipart upload structure and
// numeric character reference replacement for filenames, field names,
// and field values using FormData and fetch().
//
// Uses /fetch/api/resources/echo-content.py to echo the upload
// POST (unlike in send-file-form-helper.js, here we expect all
// multipart/form-data request bodies to be UTF-8, so we don't need to
// escape controls and non-ASCII bytes).
//
// Fields in the parameter object:
//
// - fileNameSource: purely explanatory and gives a clue about which
// character encoding is the source for the non-7-bit-ASCII parts of
// the fileBaseName, or Unicode if no smaller-than-Unicode source
// contains all the characters. Used in the test name.
// - fileBaseName: the not-necessarily-just-7-bit-ASCII file basename
// used for the constructed test file. Used in the test name.
const formDataPostFileUploadTest = ({
fileNameSource,
fileBaseName,
}) => {
promise_test(async (testCase) => {
const formData = new FormData();
let file = new Blob([kTestChars], { type: "text/plain" });
try {
// Switch to File in browsers that allow this
file = new File([file], fileBaseName, { type: file.type });
} catch (ignoredException) {
}
// Used to verify that the browser agrees with the test about
// field value replacement and encoding independently of file system
// idiosyncracies.
formData.append("filename", fileBaseName);
// Same, but with name and value reversed to ensure field names
// get the same treatment.
formData.append(fileBaseName, "filename");
formData.append("file", file, fileBaseName);
const formDataText = await (await fetch(
`/fetch/api/resources/echo-content.py`,
{
method: "POST",
body: formData,
},
)).text();
const formDataLines = formDataText.split("\r\n");
if (formDataLines.length && !formDataLines[formDataLines.length - 1]) {
--formDataLines.length;
}
assert_greater_than(
formDataLines.length,
2,
`${fileBaseName}: multipart form data must have at least 3 lines: ${
JSON.stringify(formDataText)
}`,
);
const boundary = formDataLines[0];
assert_equals(
formDataLines[formDataLines.length - 1],
boundary + "--",
`${fileBaseName}: multipart form data must end with ${boundary}--: ${
JSON.stringify(formDataText)
}`,
);
const asName = fileBaseName.replace(/[\r\n"]/g, encodeURIComponent);
const expectedText = [
boundary,
'Content-Disposition: form-data; name="filename"',
"",
fileBaseName,
boundary,
`Content-Disposition: form-data; name="${asName}"`,
"",
"filename",
boundary,
`Content-Disposition: form-data; name="file"; ` +
`filename="${asName}"`,
"Content-Type: text/plain",
"",
kTestChars,
boundary + "--",
].join("\r\n");
assert_true(
formDataText.startsWith(expectedText),
`Unexpected multipart-shaped form data received:\n${formDataText}\nExpected:\n${expectedText}`,
);
}, `Upload ${fileBaseName} (${fileNameSource}) in fetch with FormData`);
};

View File

@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Form newline normalization</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
function createForm(testCase, name, value) {
const form = document.createElement("form");
const input = document.createElement("input");
input.type = "hidden";
input.name = name;
input.value = value;
form.appendChild(input);
document.body.appendChild(form);
testCase.add_cleanup(() => {
document.body.removeChild(form);
});
return form;
}
test((testCase) => {
const formData = new FormData(createForm(testCase, "a", "b\nc"));
assert_equals(formData.get("a"), "b\r\nc");
}, document.title + ": \\n in the value becomes \\r\\n");
test((testCase) => {
const formData = new FormData(createForm(testCase, "a", "b\rc"));
assert_equals(formData.get("a"), "b\r\nc");
}, document.title + ": \\r in the value becomes \\r\\n");
test((testCase) => {
const formData = new FormData(createForm(testCase, "a", "b\r\nc"));
assert_equals(formData.get("a"), "b\r\nc");
}, document.title + ": \\r\\n in the value stays unchanged");
test((testCase) => {
const formData = new FormData(createForm(testCase, "a", "b\n\rc"));
assert_equals(formData.get("a"), "b\r\n\r\nc");
}, document.title + ": \\n\\r in the value becomes \\r\\n\\r\\n");
test((testCase) => {
const formData = new FormData(createForm(testCase, "a\nb", "c"));
assert_equals([...formData][0][0], "a\r\nb");
}, document.title + ": \\n in the name becomes \\r\\n");
test((testCase) => {
const formData = new FormData(createForm(testCase, "a\rb", "c"));
assert_equals([...formData][0][0], "a\r\nb");
}, document.title + ": \\r in the name becomes \\r\\n");
test((testCase) => {
const formData = new FormData(createForm(testCase, "a\r\nb", "c"));
assert_equals([...formData][0][0], "a\r\nb");
}, document.title + ": \\r\\n in the name stays unchanged");
test((testCase) => {
const formData = new FormData(createForm(testCase, "a\n\rb", "c"));
assert_equals([...formData][0][0], "a\r\n\r\nb");
}, document.title + ": \\n\\r in the name becomes \\r\\n\\r\\n");
</script>
</body>
</html>