This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/4.1.x-fixes by this push:
     new fdc32064f2c strip cr/lf from attachment part headers in writeHeaders 
(#3212)
fdc32064f2c is described below

commit fdc32064f2c6c295de9b0754f1a01ed17bd1969c
Author: Javid Khan <[email protected]>
AuthorDate: Mon Jun 15 15:25:28 2026 +0530

    strip cr/lf from attachment part headers in writeHeaders (#3212)
    
    (cherry picked from commit e164fb47a86cc7abf273baa90395283c52d089c4)
---
 .../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 9b6922c347e..59e87c2bf98 100644
--- a/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java
+++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java
@@ -258,11 +258,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(",");
                 }
@@ -273,6 +273,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;
     }

Reply via email to