зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1686765 - Update newline normalization in form payloads. r=smaug
This commit also changes the way escapes work in multipart/form-data names and filenames. Differential Revision: https://phabricator.services.mozilla.com/D108000
This commit is contained in:
Родитель
1af3773c48
Коммит
b1453df956
|
@ -320,18 +320,11 @@ nsresult FSURLEncoded::GetEncodedSubmission(nsIURI* aURI,
|
|||
|
||||
// i18n helper routines
|
||||
nsresult FSURLEncoded::URLEncode(const nsAString& aStr, nsACString& aEncoded) {
|
||||
// convert to CRLF breaks
|
||||
int32_t convertedBufLength = 0;
|
||||
char16_t* convertedBuf = nsLinebreakConverter::ConvertUnicharLineBreaks(
|
||||
aStr.BeginReading(), nsLinebreakConverter::eLinebreakAny,
|
||||
nsLinebreakConverter::eLinebreakNet, aStr.Length(), &convertedBufLength);
|
||||
NS_ENSURE_TRUE(convertedBuf, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsAutoString convertedString;
|
||||
convertedString.Adopt(convertedBuf, convertedBufLength);
|
||||
|
||||
nsAutoCString encodedBuf;
|
||||
nsresult rv = EncodeVal(convertedString, encodedBuf, false);
|
||||
// We encode with eValueEncode because the urlencoded format needs the newline
|
||||
// normalizations but percent-escapes characters that eNameEncode doesn't,
|
||||
// so calling NS_Escape would still be needed.
|
||||
nsresult rv = EncodeVal(aStr, encodedBuf, EncodeType::eValueEncode);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (NS_WARN_IF(!NS_Escape(encodedBuf, aEncoded, url_XPAlphas))) {
|
||||
|
@ -382,30 +375,19 @@ nsIInputStream* FSMultipartFormData::GetSubmissionBody(
|
|||
|
||||
nsresult FSMultipartFormData::AddNameValuePair(const nsAString& aName,
|
||||
const nsAString& aValue) {
|
||||
nsCString valueStr;
|
||||
nsAutoCString encodedVal;
|
||||
nsresult rv = EncodeVal(aValue, encodedVal, false);
|
||||
nsresult rv = EncodeVal(aValue, encodedVal, EncodeType::eValueEncode);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
int32_t convertedBufLength = 0;
|
||||
char* convertedBuf = nsLinebreakConverter::ConvertLineBreaks(
|
||||
encodedVal.get(), nsLinebreakConverter::eLinebreakAny,
|
||||
nsLinebreakConverter::eLinebreakNet, encodedVal.Length(),
|
||||
&convertedBufLength);
|
||||
valueStr.Adopt(convertedBuf, convertedBufLength);
|
||||
|
||||
nsAutoCString nameStr;
|
||||
rv = EncodeVal(aName, nameStr, true);
|
||||
rv = EncodeVal(aName, nameStr, EncodeType::eNameEncode);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Make MIME block for name/value pair
|
||||
|
||||
// XXX: name parameter should be encoded per RFC 2231
|
||||
// RFC 2388 specifies that RFC 2047 be used, but I think it's not
|
||||
// consistent with MIME standard.
|
||||
mPostDataChunk += "--"_ns + mBoundary + nsLiteralCString(CRLF) +
|
||||
"Content-Disposition: form-data; name=\""_ns + nameStr +
|
||||
nsLiteralCString("\"" CRLF CRLF) + valueStr +
|
||||
nsLiteralCString("\"" CRLF CRLF) + encodedVal +
|
||||
nsLiteralCString(CRLF);
|
||||
|
||||
return NS_OK;
|
||||
|
@ -417,7 +399,7 @@ nsresult FSMultipartFormData::AddNameBlobPair(const nsAString& aName,
|
|||
|
||||
// Encode the control name
|
||||
nsAutoCString nameStr;
|
||||
nsresult rv = EncodeVal(aName, nameStr, true);
|
||||
nsresult rv = EncodeVal(aName, nameStr, EncodeType::eNameEncode);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ErrorResult error;
|
||||
|
@ -442,7 +424,7 @@ nsresult FSMultipartFormData::AddNameBlobPair(const nsAString& aName,
|
|||
}
|
||||
}
|
||||
|
||||
rv = EncodeVal(filename16, filename, true);
|
||||
rv = EncodeVal(filename16, filename, EncodeType::eFilenameEncode);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Get content type
|
||||
|
@ -495,7 +477,7 @@ nsresult FSMultipartFormData::AddNameDirectoryPair(const nsAString& aName,
|
|||
|
||||
// Encode the control name
|
||||
nsAutoCString nameStr;
|
||||
nsresult rv = EncodeVal(aName, nameStr, true);
|
||||
nsresult rv = EncodeVal(aName, nameStr, EncodeType::eNameEncode);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString dirname;
|
||||
|
@ -514,7 +496,7 @@ nsresult FSMultipartFormData::AddNameDirectoryPair(const nsAString& aName,
|
|||
RetrieveDirectoryName(aDirectory, dirname16);
|
||||
}
|
||||
|
||||
rv = EncodeVal(dirname16, dirname, true);
|
||||
rv = EncodeVal(dirname16, dirname, EncodeType::eFilenameEncode);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
AddDataChunk(nameStr, dirname, "application/octet-stream"_ns, nullptr, 0);
|
||||
|
@ -531,9 +513,6 @@ void FSMultipartFormData::AddDataChunk(const nsACString& aName,
|
|||
//
|
||||
// more appropriate than always using binary?
|
||||
mPostDataChunk += "--"_ns + mBoundary + nsLiteralCString(CRLF);
|
||||
// XXX: name/filename parameter should be encoded per RFC 2231
|
||||
// RFC 2388 specifies that RFC 2047 be used, but I think it's not
|
||||
// consistent with the MIME standard.
|
||||
mPostDataChunk += "Content-Disposition: form-data; name=\""_ns + aName +
|
||||
"\"; filename=\""_ns + aFilename +
|
||||
nsLiteralCString("\"" CRLF) + "Content-Type: "_ns +
|
||||
|
@ -675,20 +654,13 @@ nsresult FSTextPlain::GetEncodedSubmission(nsIURI* aURI,
|
|||
rv = NS_MutateURI(aURI).SetPathQueryRef(path).Finalize(aOutURI);
|
||||
} else {
|
||||
// Create data stream.
|
||||
// We do want to send the data through the charset encoder and we want to
|
||||
// normalize linebreaks to use the "standard net" format (\r\n), but we
|
||||
// don't want to perform any other encoding. This means that names and
|
||||
// values which contains '=' or newlines are potentially ambigiously
|
||||
// encoded, but that how text/plain is specced.
|
||||
// We use eValueEncode to send the data through the charset encoder and to
|
||||
// normalize linebreaks to use the "standard net" format (\r\n), but not
|
||||
// perform any other escaping. This means that names and values which
|
||||
// contain '=' or newlines are potentially ambiguously encoded, but that is
|
||||
// how text/plain is specced.
|
||||
nsCString cbody;
|
||||
EncodeVal(mBody, cbody, false);
|
||||
|
||||
int32_t convertedBufLength = 0;
|
||||
char* convertedBuf = nsLinebreakConverter::ConvertLineBreaks(
|
||||
cbody.get(), nsLinebreakConverter::eLinebreakAny,
|
||||
nsLinebreakConverter::eLinebreakNet, cbody.Length(),
|
||||
&convertedBufLength);
|
||||
cbody.Adopt(convertedBuf, convertedBufLength);
|
||||
EncodeVal(mBody, cbody, EncodeType::eValueEncode);
|
||||
|
||||
nsCOMPtr<nsIInputStream> bodyStream;
|
||||
rv = NS_NewCStringInputStream(getter_AddRefs(bodyStream), std::move(cbody));
|
||||
|
@ -743,7 +715,7 @@ EncodingFormSubmission::~EncodingFormSubmission() = default;
|
|||
// i18n helper routines
|
||||
nsresult EncodingFormSubmission::EncodeVal(const nsAString& aStr,
|
||||
nsCString& aOut,
|
||||
bool aHeaderEncode) {
|
||||
EncodeType aEncodeType) {
|
||||
nsresult rv;
|
||||
const Encoding* ignored;
|
||||
Tie(rv, ignored) = mEncoding->Encode(aStr, aOut);
|
||||
|
@ -751,14 +723,32 @@ nsresult EncodingFormSubmission::EncodeVal(const nsAString& aStr,
|
|||
return rv;
|
||||
}
|
||||
|
||||
if (aHeaderEncode) {
|
||||
if (aEncodeType != EncodeType::eFilenameEncode) {
|
||||
// Normalize newlines
|
||||
int32_t convertedBufLength = 0;
|
||||
char* convertedBuf = nsLinebreakConverter::ConvertLineBreaks(
|
||||
aOut.get(), nsLinebreakConverter::eLinebreakAny,
|
||||
nsLinebreakConverter::eLinebreakSpace, aOut.Length(),
|
||||
nsLinebreakConverter::eLinebreakNet, (int32_t)aOut.Length(),
|
||||
&convertedBufLength);
|
||||
aOut.Adopt(convertedBuf, convertedBufLength);
|
||||
aOut.ReplaceSubstring("\""_ns, "\\\""_ns);
|
||||
}
|
||||
|
||||
if (aEncodeType != EncodeType::eValueEncode) {
|
||||
// Percent-escape LF, CR and double quotes.
|
||||
int32_t offset = 0;
|
||||
while ((offset = aOut.FindCharInSet("\n\r\"", offset)) != kNotFound) {
|
||||
if (aOut[offset] == '\n') {
|
||||
aOut.ReplaceLiteral(offset, 1, "%0A");
|
||||
} else if (aOut[offset] == '\r') {
|
||||
aOut.ReplaceLiteral(offset, 1, "%0D");
|
||||
} else if (aOut[offset] == '"') {
|
||||
aOut.ReplaceLiteral(offset, 1, "%22");
|
||||
} else {
|
||||
MOZ_ASSERT(false);
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -147,17 +147,30 @@ class EncodingFormSubmission : public HTMLFormSubmission {
|
|||
|
||||
virtual ~EncodingFormSubmission();
|
||||
|
||||
// Indicates the type of newline normalization and escaping to perform in
|
||||
// `EncodeVal`, in addition to encoding the string into bytes.
|
||||
enum EncodeType {
|
||||
// Normalizes newlines to CRLF and then escapes for use in
|
||||
// `Content-Disposition`. (Useful for `multipart/form-data` entry names.)
|
||||
eNameEncode,
|
||||
// Escapes for use in `Content-Disposition`. (Useful for
|
||||
// `multipart/form-data` filenames.)
|
||||
eFilenameEncode,
|
||||
// Normalizes newlines to CRLF.
|
||||
eValueEncode,
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode a Unicode string to bytes using the encoder (or just copy the input
|
||||
* if there is no encoder).
|
||||
* Encode a Unicode string to bytes, additionally performing escapes or
|
||||
* normalizations.
|
||||
* @param aStr the string to encode
|
||||
* @param aResult the encoded string [OUT]
|
||||
* @param aHeaderEncode If true, turns all linebreaks into spaces and escapes
|
||||
* all quotes
|
||||
* @param aOut the encoded string [OUT]
|
||||
* @param aEncodeType The type of escapes or normalizations to perform on the
|
||||
* encoded string.
|
||||
* @throws an error if UnicodeToNewBytes fails
|
||||
*/
|
||||
nsresult EncodeVal(const nsAString& aStr, nsCString& aResult,
|
||||
bool aHeaderEncode);
|
||||
nsresult EncodeVal(const nsAString& aStr, nsCString& aOut,
|
||||
EncodeType aEncodeType);
|
||||
};
|
||||
|
||||
class DialogFormSubmission final : public HTMLFormSubmission {
|
||||
|
|
|
@ -609,11 +609,14 @@ function checkMPSubmission(sub, expected, test) {
|
|||
for (x in o) ++l;
|
||||
return l;
|
||||
}
|
||||
function mpquote(s) {
|
||||
return s.replace(/\r\n/g, " ")
|
||||
.replace(/\r/g, " ")
|
||||
.replace(/\n/g, " ")
|
||||
.replace(/\"/g, "\\\"");
|
||||
function mpquote_name(s) {
|
||||
return s.replace(/\r?\n|\r/g, "%0D%0A")
|
||||
.replace(/\"/g, "%22");
|
||||
}
|
||||
function mpquote_filename(s) {
|
||||
return s.replace(/\r/g, "%0D")
|
||||
.replace(/\n/g, "%0A")
|
||||
.replace(/\"/g, "%22");
|
||||
}
|
||||
|
||||
is(sub.length, expected.length,
|
||||
|
@ -627,7 +630,7 @@ function checkMPSubmission(sub, expected, test) {
|
|||
for (i = 0; i < expected.length; ++i) {
|
||||
if (!("fileName" in expected[i])) {
|
||||
is(sub[i].headers["Content-Disposition"],
|
||||
"form-data; name=\"" + mpquote(expected[i].name) + "\"",
|
||||
"form-data; name=\"" + mpquote_name(expected[i].name) + "\"",
|
||||
"Correct name in " + test);
|
||||
is (getPropCount(sub[i].headers), 1,
|
||||
"Wrong number of headers in " + test);
|
||||
|
@ -637,8 +640,8 @@ function checkMPSubmission(sub, expected, test) {
|
|||
}
|
||||
else {
|
||||
is(sub[i].headers["Content-Disposition"],
|
||||
"form-data; name=\"" + mpquote(expected[i].name) + "\"; filename=\"" +
|
||||
mpquote(expected[i].fileName) + "\"",
|
||||
"form-data; name=\"" + mpquote_name(expected[i].name) + "\"; filename=\"" +
|
||||
mpquote_filename(expected[i].fileName) + "\"",
|
||||
"Correct name in " + test);
|
||||
is(sub[i].headers["Content-Type"],
|
||||
expected[i].contentType,
|
||||
|
|
|
@ -2,18 +2,6 @@
|
|||
expected:
|
||||
if (processor == "x86") and (os == "win") and debug: ["OK", "TIMEOUT"]
|
||||
if (processor == "x86") and (os == "win") and not debug: ["OK", "TIMEOUT"]
|
||||
[Upload file-for-upload-in-form-LF-[\n\].txt (ASCII) in UTF-8 form]
|
||||
expected: FAIL
|
||||
|
||||
[Upload file-for-upload-in-form-LF-CR-[\n\r\].txt (ASCII) in UTF-8 form]
|
||||
expected: FAIL
|
||||
|
||||
[Upload file-for-upload-in-form-CR-[\r\].txt (ASCII) in UTF-8 form]
|
||||
expected: FAIL
|
||||
|
||||
[Upload file-for-upload-in-form-CR-LF-[\r\n\].txt (ASCII) in UTF-8 form]
|
||||
expected: FAIL
|
||||
|
||||
[Upload file-for-upload-in-form-ESC-[\x1b\].txt (ASCII) in UTF-8 form]
|
||||
expected:
|
||||
if (processor == "x86") and (os == "win"): ["PASS", "TIMEOUT"]
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
[send-file-form-punctuation.html]
|
||||
expected:
|
||||
if (os == "linux") and not webrender and not debug and not fission and (processor == "x86_64"): ["OK", "TIMEOUT"]
|
||||
[Upload file-for-upload-in-form-QUOTATION-MARK-["\].txt (ASCII) in UTF-8 form]
|
||||
expected: FAIL
|
||||
|
||||
[Upload "file-for-upload-in-form-double-quoted.txt" (ASCII) in UTF-8 form]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
[send-file-formdata-controls.html]
|
||||
[Upload file-for-upload-in-form-LF-[\n\].txt (ASCII) in fetch with FormData]
|
||||
expected: FAIL
|
||||
|
||||
[Upload file-for-upload-in-form-LF-CR-[\n\r\].txt (ASCII) in fetch with FormData]
|
||||
expected: FAIL
|
||||
|
||||
[Upload file-for-upload-in-form-CR-[\r\].txt (ASCII) in fetch with FormData]
|
||||
expected: FAIL
|
||||
|
||||
[Upload file-for-upload-in-form-CR-LF-[\r\n\].txt (ASCII) in fetch with FormData]
|
||||
expected: FAIL
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
[send-file-formdata-punctuation.html]
|
||||
[Upload file-for-upload-in-form-QUOTATION-MARK-["\].txt (ASCII) in fetch with FormData]
|
||||
expected: FAIL
|
||||
|
||||
[Upload "file-for-upload-in-form-double-quoted.txt" (ASCII) in fetch with FormData]
|
||||
expected: FAIL
|
||||
|
|
@ -7,43 +7,35 @@
|
|||
|
||||
[multipart/form-data: \\n in name (normal form)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\n in name (formdata event)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "TIMEOUT", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "TIMEOUT", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\r in name (normal form)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\r in name (formdata event)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\r\\n in name (normal form)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\r\\n in name (formdata event)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "TIMEOUT", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "TIMEOUT", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\n\\r in name (normal form)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\n\\r in name (formdata event)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\n in value (normal form)]
|
||||
expected:
|
||||
|
@ -79,53 +71,43 @@
|
|||
|
||||
[multipart/form-data: \\n in filename (normal form)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\n in filename (formdata event)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\r in filename (normal form)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\r in filename (formdata event)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\r\\n in filename (normal form)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\r\\n in filename (formdata event)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\n\\r in filename (normal form)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: \\n\\r in filename (formdata event)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: double quote in name (normal form)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: double quote in name (formdata event)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: double quote in value (normal form)]
|
||||
expected:
|
||||
|
@ -137,13 +119,11 @@
|
|||
|
||||
[multipart/form-data: double quote in filename (normal form)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: double quote in filename (formdata event)]
|
||||
expected:
|
||||
if (processor == "x86") and not debug: ["FAIL", "NOTRUN"]
|
||||
FAIL
|
||||
if (processor == "x86") and not debug: ["PASS", "NOTRUN"]
|
||||
|
||||
[multipart/form-data: single quote in name (normal form)]
|
||||
expected:
|
||||
|
|
Загрузка…
Ссылка в новой задаче