This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/main by this push:
new e164fb47a86 strip cr/lf from attachment part headers in writeHeaders
(#3212)
e164fb47a86 is described below
commit e164fb47a86cc7abf273baa90395283c52d089c4
Author: Javid Khan <[email protected]>
AuthorDate: Mon Jun 15 15:25:28 2026 +0530
strip cr/lf from attachment part headers in writeHeaders (#3212)
---
.../cxf/attachment/AttachmentSerializer.java | 21 +++++++++--
.../cxf/attachment/AttachmentSerializerTest.java | 42 ++++++++++++++++++++++
2 files changed, 61 insertions(+), 2 deletions(-)
diff --git
a/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java
b/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java
index d32c5a33a52..cd0eb4c194f 100644
--- a/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java
+++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java
@@ -266,11 +266,11 @@ public class AttachmentSerializer {
|| "Content-Transfer-Encoding".equalsIgnoreCase(name)) {
continue;
}
- writer.write(name);
+ writer.write(stripLineBreaks(name));
writer.write(": ");
List<String> values = entry.getValue();
for (int i = 0; i < values.size(); i++) {
- writer.write(values.get(i));
+ writer.write(stripLineBreaks(values.get(i)));
if (i + 1 < values.size()) {
writer.write(",");
}
@@ -281,6 +281,23 @@ public class AttachmentSerializer {
writer.write("\r\n");
}
+ // A part header value such as the Content-Disposition filename can carry
an
+ // attacker-supplied name. Bare CR or LF in it would otherwise terminate
the
+ // header line and let extra part headers be injected into the MIME stream.
+ private static String stripLineBreaks(String value) {
+ if (value == null || (value.indexOf('\r') < 0 && value.indexOf('\n') <
0)) {
+ return value;
+ }
+ StringBuilder sb = new StringBuilder(value.length());
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ if (c != '\r' && c != '\n') {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
private static String checkAngleBrackets(String value) {
if (value.charAt(0) == '<' && value.charAt(value.length() - 1) == '>')
{
return value.substring(1, value.length() - 1);
diff --git
a/core/src/test/java/org/apache/cxf/attachment/AttachmentSerializerTest.java
b/core/src/test/java/org/apache/cxf/attachment/AttachmentSerializerTest.java
index 13f4793dee8..166e085cfac 100644
--- a/core/src/test/java/org/apache/cxf/attachment/AttachmentSerializerTest.java
+++ b/core/src/test/java/org/apache/cxf/attachment/AttachmentSerializerTest.java
@@ -263,6 +263,48 @@ public class AttachmentSerializerTest {
}
+ @Test
+ public void testHeaderValueLineBreaksAreStripped() throws Exception {
+ MessageImpl msg = new MessageImpl();
+
+ Collection<Attachment> atts = new ArrayList<>();
+ AttachmentImpl a = new AttachmentImpl("test.xml");
+ InputStream is = getClass().getResourceAsStream("my.wav");
+ ByteArrayDataSource ds = new ByteArrayDataSource(is,
"application/octet-stream");
+ a.setDataHandler(new DataHandler(ds));
+ // a filename carrying CR/LF, e.g. taken from an uploaded part name
+ a.setHeader("Content-Disposition",
+ "attachment; filename=\"evil\r\nX-Injected: yes\"");
+ atts.add(a);
+ msg.setAttachments(atts);
+
+ msg.put(Message.CONTENT_TYPE, "application/soap+xml");
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ msg.setContent(OutputStream.class, out);
+
+ AttachmentSerializer serializer = new AttachmentSerializer(msg);
+ serializer.writeProlog();
+ out.write("<soap:Body/>".getBytes());
+ serializer.writeAttachments();
+ out.flush();
+
+ String wire = out.toString();
+ assertTrue("the line break must not survive into the MIME stream",
+ wire.contains("filename=\"evilX-Injected: yes\""));
+
+ String ct = (String) msg.get(Message.CONTENT_TYPE);
+ DataSource source = new ByteArrayDataSource(new
ByteArrayInputStream(out.toByteArray()), ct);
+ MimeMultipart mpart = new MimeMultipart(source);
+ Session session = Session.getDefaultInstance(new Properties());
+ MimeMessage inMsg = new MimeMessage(session);
+ inMsg.setContent(mpart);
+ inMsg.addHeaderLine("Content-Type: " + ct);
+ MimeMultipart multipart = (MimeMultipart) inMsg.getContent();
+ MimeBodyPart part = (MimeBodyPart) multipart.getBodyPart(1);
+ assertEquals(null, part.getHeader("X-Injected"));
+ }
+
private static String escapeQuotes(String s) {
return s.indexOf('"') != 0 ? s.replace("\"", "\\\"") : s;
}