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

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

commit 64659e9447488e8814c6a5027db3d18b1db0c016
Author: Andriy Redko <[email protected]>
AuthorDate: Tue Sep 8 06:16:55 2026 -0400

    Add formParamsMaxSize to FormEncodingProvider and estimate form params 
count before decoding / parsing (#3444)
    
    (cherry picked from commit 3fd6143353b911f050460d845277de375078d990)
---
 .../cxf/jaxrs/provider/FormEncodingProvider.java   |  9 +++++--
 .../java/org/apache/cxf/jaxrs/utils/FormUtils.java | 28 +++++++++++++++++++++-
 .../systest/jaxrs/JAXRSClientServerBookTest.java   | 26 ++++++++++++++++++++
 3 files changed, 60 insertions(+), 3 deletions(-)

diff --git 
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java
 
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java
index 7be9751d70c..d4c26ca1073 100644
--- 
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java
+++ 
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java
@@ -61,6 +61,7 @@ public class FormEncodingProvider<T> extends 
AbstractConfigurableProvider
     private String attachmentDir;
     private String attachmentThreshold;
     private String attachmentMaxSize;
+    private int formParamsMaxSize = FormUtils.DEFAULT_FORM_PARAMS_MAX_SIZE;
 
     private boolean expectEncoded;
 
@@ -87,6 +88,10 @@ public class FormEncodingProvider<T> extends 
AbstractConfigurableProvider
     public void setAttachmentMaxSize(String maxSize) {
         attachmentMaxSize = maxSize;
     }
+    
+    public void setFormParamsMaxSize(int maxSize) {
+        this.formParamsMaxSize = maxSize;
+    }
 
     public void setValidator(FormValidator formValidator) {
         validator = formValidator;
@@ -172,13 +177,13 @@ public class FormEncodingProvider<T> extends 
AbstractConfigurableProvider
             if (servletRequest == null) {
                 FormUtils.populateMapFromString(params,
                                                 
PhaseInterceptorChain.getCurrentMessage(),
-                                                FormUtils.readBody(is, enc),
+                                                FormUtils.readBody(is, enc, 
formParamsMaxSize),
                                                 enc,
                                                 decode);
             } else {
                 FormUtils.populateMapFromString(params,
                                                 
PhaseInterceptorChain.getCurrentMessage(),
-                                                FormUtils.readBody(is, enc),
+                                                FormUtils.readBody(is, enc, 
formParamsMaxSize),
                                                 enc,
                                                 decode,
                                                 
(javax.servlet.http.HttpServletRequest)servletRequest);
diff --git 
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java 
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java
index 5d51005b154..a360717c664 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java
@@ -41,6 +41,7 @@ import javax.ws.rs.core.MultivaluedMap;
 
 import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.common.util.SystemPropertyAction;
 import org.apache.cxf.helpers.IOUtils;
 import org.apache.cxf.io.CachedOutputStream;
 import org.apache.cxf.jaxrs.ext.multipart.Attachment;
@@ -54,6 +55,9 @@ import org.apache.cxf.phase.PhaseInterceptorChain;
 import org.apache.cxf.transport.http.AbstractHTTPDestination;
 
 public final class FormUtils {
+    public static final int DEFAULT_FORM_PARAMS_MAX_SIZE =
+        SystemPropertyAction.getInteger("org.apache.cxf.form-params-max-size", 
104857600 /* 100Mb */);
+
     public static final int DEFAULT_MAX_FORM_PARAM_COUNT = 500;
 
     public static final String FORM_PARAMS_FROM_HTTP_PARAMS = 
"set.form.parameters.from.http.parameters";
@@ -116,10 +120,18 @@ public final class FormUtils {
         }
     }
 
+    /**
+     * @deprecated please use {@code readBody(InputStream is, String encoding, 
int maxSize)}
+     */
+    @Deprecated
     public static String readBody(InputStream is, String encoding) {
+        return readBody(is, encoding, -1);
+    }
+
+    public static String readBody(InputStream is, String encoding, int 
maxSize) {
         try {
             ByteArrayOutputStream bos = new ByteArrayOutputStream();
-            IOUtils.copy(is, bos, 1024);
+            IOUtils.copy(is, bos, 1024, maxSize);
             return new String(bos.toByteArray(), encoding);
         } catch (Exception ex) {
             throw ExceptionUtils.toInternalServerErrorException(ex, null);
@@ -134,6 +146,8 @@ public final class FormUtils {
         if (StringUtils.isEmpty(postBody)) {
             return;
         }
+        final int numberOfParts = estimateNumberOfParts(postBody);
+        checkNumberOfParts(m, numberOfParts);
         String[] parts = postBody.split("&");
         checkNumberOfParts(m, parts.length);
         for (String part : parts) {
@@ -270,6 +284,18 @@ public final class FormUtils {
         }
     }
 
+    /**
+     * Estimates how many parts we should expect by checking on & separator
+     */
+    private static int estimateNumberOfParts(String postBody) {
+        int count = 0;
+        int index = -1;
+        while ((index = postBody.indexOf('&', index + 1)) >= 0) {
+            ++count;
+        }
+        return count;
+    }
+
     private static void checkNumberOfParts(Message m, int numberOfParts) {
         if (m == null || m.getExchange() == null || 
m.getExchange().getInMessage() == null) {
             return;
diff --git 
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
index 9dfe95f55e6..11c9d35c11e 100644
--- 
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
+++ 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
@@ -36,6 +36,8 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 import java.util.zip.GZIPInputStream;
 
 import javax.ws.rs.InternalServerErrorException;
@@ -339,6 +341,30 @@ public class JAXRSClientServerBookTest extends 
AbstractBusClientServerTestBase {
         assertEquals("empty form", r.readEntity(String.class));
     }
 
+    @Test
+    public void testTooManyFormParams() throws Exception {
+        // Exceeding 500 limit
+        String params = IntStream.range(0, 501).mapToObj(i -> "param" + i + 
"=" + i).collect(Collectors.joining("&"));
+        String address = "http://localhost:"; + PORT + "/bookstore/form";
+        WebClient wc = WebClient.create(address);
+        wc.type(MediaType.APPLICATION_FORM_URLENCODED);
+        Response r = wc.post(new 
ByteArrayInputStream(params.getBytes(StandardCharsets.UTF_8)));
+        assertThat("max form params limit reached",  r.getStatus(), 
equalTo(413));
+    }
+
+    @Test
+    public void testTooLargeFormParams() throws Exception {
+        // Exceeding 100Mb limit
+        String params = IntStream.range(0, 110)
+            .mapToObj(i -> "param" + i + "="
+                + new 
String(Integer.toString(i)).repeat(524800)).collect(Collectors.joining("&"));
+        String address = "http://localhost:"; + PORT + "/bookstore/form";
+        WebClient wc = WebClient.create(address);
+        wc.type(MediaType.APPLICATION_FORM_URLENCODED);
+        Response r = wc.post(new 
ByteArrayInputStream(params.getBytes(StandardCharsets.UTF_8)));
+        assertThat("max form params limit reached",  r.getStatus(), 
equalTo(500));
+    }
+
     @Test
     public void testGetBookDescriptionHttpResponse() throws Exception {
         String address = "http://localhost:"; + PORT + 
"/bookstore/httpresponse";

Reply via email to