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

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

commit a3510eb5ae6194c5d6518a97ae924341961d4220
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Tue Nov 10 15:42:15 2026 +0000

    Limit MIME boundary length to bound boundary scan cost
---
 .../attachment/AttachmentBoundaryDeserializer.java |  1 +
 .../cxf/attachment/AttachmentDeserializer.java     |  8 +++++
 .../cxf/attachment/AttachmentDeserializerUtil.java | 16 +++++++++
 .../cxf/attachment/MimeBodyPartInputStream.java    |  5 +++
 .../cxf/attachment/AttachmentDeserializerTest.java | 42 ++++++++++++++++++++++
 5 files changed, 72 insertions(+)

diff --git 
a/core/src/main/java/org/apache/cxf/attachment/AttachmentBoundaryDeserializer.java
 
b/core/src/main/java/org/apache/cxf/attachment/AttachmentBoundaryDeserializer.java
index e7bdc7d616d..ed1d49f85ef 100644
--- 
a/core/src/main/java/org/apache/cxf/attachment/AttachmentBoundaryDeserializer.java
+++ 
b/core/src/main/java/org/apache/cxf/attachment/AttachmentBoundaryDeserializer.java
@@ -61,6 +61,7 @@ public class AttachmentBoundaryDeserializer {
         }
 
         final byte[] boundary = 
boundaryString.getBytes(StandardCharsets.UTF_8);
+        AttachmentDeserializerUtil.checkBoundaryLength(boundary);
         if (!AttachmentDeserializerUtil.readTillFirstBoundary(stream, 
boundary)) {
             throw new IOException("Couldn't find MIME boundary: " + 
boundaryString);
         }
diff --git 
a/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java 
b/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java
index e182e878f17..457a81800b0 100644
--- a/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java
+++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java
@@ -84,6 +84,13 @@ public class AttachmentDeserializer {
     public static final int DEFAULT_MAX_HEADER_SIZE =
         
SystemPropertyAction.getInteger("org.apache.cxf.attachment-max-header-size", 
300);
 
+    /**
+     * The maximum length of a MIME boundary (excluding the leading "--"). RFC 
2046 limits
+     * boundaries to 70 characters. The default is 70.
+     */
+    public static final int DEFAULT_MAX_BOUNDARY_LENGTH =
+        
SystemPropertyAction.getInteger("org.apache.cxf.attachment-max-boundary-length",
 70);
+
     public static final int THRESHOLD = 1024 * 100; //100K (byte unit)
 
     private static final Pattern CONTENT_TYPE_BOUNDARY_PATTERN = 
Pattern.compile("boundary=\"?([^\";]*)");
@@ -168,6 +175,7 @@ public class AttachmentDeserializer {
                 throw new IOException("Couldn't determine the boundary from 
the message!");
             }
             boundary = boundaryString.getBytes(StandardCharsets.UTF_8);
+            AttachmentDeserializerUtil.checkBoundaryLength(boundary);
 
             stream = new 
PushbackInputStream(message.getContent(InputStream.class), PUSHBACK_AMOUNT);
             if (!AttachmentDeserializerUtil.readTillFirstBoundary(stream, 
boundary)) {
diff --git 
a/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializerUtil.java 
b/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializerUtil.java
index c2d4d2bffed..9f9f2e2451a 100644
--- 
a/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializerUtil.java
+++ 
b/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializerUtil.java
@@ -36,6 +36,22 @@ final class AttachmentDeserializerUtil {
 
     private AttachmentDeserializerUtil() {
     }
+
+    /**
+     * Reject boundaries that are longer than permitted (RFC 2046 limits a 
boundary to 70
+     * characters). The boundary scan cost is proportional to the boundary 
length, so an
+     * overly long attacker-supplied boundary can be used to burn CPU.
+     *
+     * @param boundary the boundary, including the leading "--"
+     * @throws IOException if the boundary is empty or too long
+     */
+    static void checkBoundaryLength(byte[] boundary) throws IOException {
+        // +2 for the leading "--"
+        if (boundary == null || boundary.length <= 2
+            || boundary.length > 
AttachmentDeserializer.DEFAULT_MAX_BOUNDARY_LENGTH + 2) {
+            throw new IOException("Invalid MIME boundary length");
+        }
+    }
     
     /**
      * Move the read pointer to the begining of the first part read till the 
end
diff --git 
a/core/src/main/java/org/apache/cxf/attachment/MimeBodyPartInputStream.java 
b/core/src/main/java/org/apache/cxf/attachment/MimeBodyPartInputStream.java
index bdabf056111..cb157576557 100644
--- a/core/src/main/java/org/apache/cxf/attachment/MimeBodyPartInputStream.java
+++ b/core/src/main/java/org/apache/cxf/attachment/MimeBodyPartInputStream.java
@@ -38,6 +38,11 @@ public class MimeBodyPartInputStream extends InputStream {
                                    byte[] boundaryParam,
                                    int pbsize) {
         super();
+        if (boundaryParam == null || boundaryParam.length == 0
+            || boundaryParam.length > 
AttachmentDeserializer.DEFAULT_MAX_BOUNDARY_LENGTH + 2) {
+            // +2 for the leading "--"
+            throw new IllegalArgumentException("Invalid MIME boundary length");
+        }
         this.inStream = inStreamParam;
         this.boundary = boundaryParam;
         this.pbAmount = pbsize;
diff --git 
a/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java 
b/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java
index 60572e06b88..b1eca80681f 100644
--- 
a/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java
+++ 
b/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java
@@ -716,6 +716,48 @@ public class AttachmentDeserializerTest {
             () -> ad.initializeAttachments());
     }
 
+    @Test
+    public void testBoundaryTooLong() throws Exception {
+        StringBuilder boundary = new StringBuilder();
+        IntStream.range(0, 2040).forEach(i -> boundary.append('A'));
+        boundary.append('B');
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("--").append(boundary).append("\r\n")
+            .append("Content-Type: text/xml; charset=UTF-8\r\n")
+            .append("\r\n")
+            .append("<envelope/>\r\n")
+            .append("--").append(boundary).append("--\r\n");
+
+        msg = new MessageImpl();
+        msg.setContent(InputStream.class, new 
ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8)));
+        msg.put(Message.CONTENT_TYPE, "multipart/related; boundary=\"" + 
boundary + "\"");
+        AttachmentDeserializer ad = new AttachmentDeserializer(msg);
+
+        assertThrows("Failure expected on too long boundary", 
IOException.class,
+            () -> ad.initializeAttachments());
+    }
+
+    @Test
+    public void testBoundaryMaxLength() throws Exception {
+        StringBuilder boundary = new StringBuilder();
+        IntStream.range(0, 70).forEach(i -> boundary.append('A'));
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("--").append(boundary).append("\r\n")
+            .append("Content-Type: text/xml; charset=UTF-8\r\n")
+            .append("\r\n")
+            .append("<envelope/>\r\n")
+            .append("--").append(boundary).append("--\r\n");
+
+        msg = new MessageImpl();
+        msg.setContent(InputStream.class, new 
ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8)));
+        msg.put(Message.CONTENT_TYPE, "multipart/related; boundary=\"" + 
boundary + "\"");
+        AttachmentDeserializer ad = new AttachmentDeserializer(msg);
+        ad.initializeAttachments();
+        assertEquals("<envelope/>", 
IOUtils.toString(msg.getContent(InputStream.class)));
+    }
+
     @Test
     public void testAttachmentHeaderSize() throws Exception {
         final Random random = new Random();

Reply via email to