Skip to content

Commit da6f1b2

Browse files
dxbjavidok2c
authored andcommitted
escape quotes and strip line breaks in rfc7578 multipart parameters
1 parent 06aac6d commit da6f1b2

3 files changed

Lines changed: 41 additions & 1 deletion

File tree

httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/AbstractMultipartFormat.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,21 @@ static CharSequence stripLineBreaks(final CharSequence s) {
114114
return buf.toString();
115115
}
116116

117+
static CharSequence escapeQuoted(final CharSequence s) {
118+
if (s == null) {
119+
return null;
120+
}
121+
final StringBuilder buf = new StringBuilder(s.length());
122+
for (int n = 0; n < s.length(); n++) {
123+
final char ch = s.charAt(n);
124+
if (ch == '"' || ch == '\\') {
125+
buf.append('\\');
126+
}
127+
buf.append(ch);
128+
}
129+
return buf.toString();
130+
}
131+
117132
static void writeField(
118133
final MimeField field, final OutputStream out) throws IOException {
119134
writeBytes(stripLineBreaks(field.getName()), out);

httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/HttpRFC7578Multipart.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ protected void formatMultipartHeader(final MultipartPart part, final OutputStrea
119119
writeBytes(value, StandardCharsets.ISO_8859_1, out);
120120
}
121121
} else {
122-
writeBytes(value, out);
122+
writeBytes(stripLineBreaks(escapeQuoted(value)), out);
123123
}
124124
}
125125
writeBytes("\"", out);

httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/HttpRFC7578MultipartTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,37 @@
2828
package org.apache.hc.client5.http.entity.mime;
2929

3030
import static org.junit.jupiter.api.Assertions.assertEquals;
31+
import static org.junit.jupiter.api.Assertions.assertFalse;
32+
import static org.junit.jupiter.api.Assertions.assertTrue;
3133

34+
import java.io.ByteArrayOutputStream;
35+
import java.nio.charset.StandardCharsets;
36+
import java.util.Collections;
37+
38+
import org.apache.hc.core5.http.ContentType;
3239
import org.apache.hc.core5.net.PercentCodec;
3340
import org.junit.jupiter.api.Test;
3441

3542
class HttpRFC7578MultipartTest {
3643

44+
@Test
45+
void testFieldNameLineBreaksAndQuotesAreNeutralised() throws Exception {
46+
final FormBodyPart part = FormBodyPartBuilder.create(
47+
"evil\"\r\nX-Injected: yes",
48+
new StringBody("value", ContentType.DEFAULT_TEXT),
49+
HttpMultipartMode.EXTENDED).build();
50+
final HttpRFC7578Multipart multipart = new HttpRFC7578Multipart(
51+
StandardCharsets.UTF_8, "BOUNDARY", Collections.singletonList(part),
52+
HttpMultipartMode.EXTENDED);
53+
54+
final ByteArrayOutputStream out = new ByteArrayOutputStream();
55+
multipart.writeTo(out);
56+
final String s = out.toString("ISO-8859-1");
57+
58+
assertFalse(s.contains("\r\nX-Injected: yes"), s);
59+
assertTrue(s.contains("name=\"evil\\\" X-Injected: yes\""), s);
60+
}
61+
3762
@Test
3863
void testPercentDecodingWithValidMessages() {
3964
final String[][] tests = new String[][] {

0 commit comments

Comments
 (0)