This is an automated email from the ASF dual-hosted git repository.
reta 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 3fd6143353b Add formParamsMaxSize to FormEncodingProvider and estimate
form params count before decoding / parsing (#3444)
3fd6143353b is described below
commit 3fd6143353b911f050460d845277de375078d990
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)
---
.../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 bb798c42a54..76ff7d59cc3 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
@@ -60,6 +60,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;
@@ -86,6 +87,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;
@@ -171,13 +176,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,
(jakarta.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 44f3a6d0b7e..3479f737e95 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 jakarta.ws.rs.core.MediaType;
import jakarta.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) {
@@ -288,6 +302,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 64cc125d07f..4b0b1d9d498 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.xml.namespace.QName;
@@ -342,6 +344,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";