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 d061d648cda9e7fa000a861bab1d20ca2ece8717 Author: Andriy Redko <[email protected]> AuthorDate: Thu Jun 4 11:40:57 2026 -0400 Introduce default value for maxFormParameterCount (#3177) --- .../java/org/apache/cxf/jaxrs/utils/FormUtils.java | 9 +- .../org/apache/cxf/jaxrs/utils/FormUtilsTest.java | 103 +++++++++++++++++---- 2 files changed, 88 insertions(+), 24 deletions(-) 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 0c14eb0b29c..5d51005b154 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 @@ -54,6 +54,8 @@ import org.apache.cxf.phase.PhaseInterceptorChain; import org.apache.cxf.transport.http.AbstractHTTPDestination; public final class FormUtils { + 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"; public static final String FORM_PARAM_MAP = "org.apache.cxf.form_data"; public static final String FORM_PARAM_MAP_DECODED = "org.apache.cxf.form_data.decoded"; @@ -272,11 +274,8 @@ public final class FormUtils { if (m == null || m.getExchange() == null || m.getExchange().getInMessage() == null) { return; } - String maxPartsCountProp = (String)m.getExchange() - .getInMessage().getContextualProperty(MAX_FORM_PARAM_COUNT); - if (maxPartsCountProp == null) { - return; - } + final String maxPartsCountProp = MessageUtils.getContextualString(m.getExchange().getInMessage(), + MAX_FORM_PARAM_COUNT, Integer.toString(DEFAULT_MAX_FORM_PARAM_COUNT)); try { int maxPartsCount = Integer.parseInt(maxPartsCountProp); if (maxPartsCount != -1 && numberOfParts >= maxPartsCount) { diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/FormUtilsTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/FormUtilsTest.java index 4e55527ea05..3076a291d4f 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/FormUtilsTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/FormUtilsTest.java @@ -19,41 +19,55 @@ package org.apache.cxf.jaxrs.utils; +import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MultivaluedMap; +import org.apache.cxf.jaxrs.ext.multipart.Attachment; +import org.apache.cxf.jaxrs.ext.multipart.MultipartBody; import org.apache.cxf.jaxrs.impl.MetadataMap; +import org.apache.cxf.message.ExchangeImpl; import org.apache.cxf.message.Message; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class FormUtilsTest { - - private static final String HTTP_PARAM1 = "httpParam1"; - private static final String HTTP_PARAM2 = "httpParam2"; - private static final String HTTP_PARAM_VALUE1 = "httpValue1"; - private static final String HTTP_PARAM_VALUE2 = "httpValue2"; - - private static final String FORM_PARAM1 = "formParam1"; - private static final String FORM_PARAM2 = "formParam2"; - private static final String FORM_PARAM_VALUE1 = "formValue1"; - private static final String FORM_PARAM_VALUE2 = "formValue2"; + private static final String HTTP_PARAM = "httpParam"; + private static final String HTTP_PARAM1 = HTTP_PARAM + "1"; + private static final String HTTP_PARAM2 = HTTP_PARAM + "2"; + private static final String HTTP_PARAM_VALUE = "httpValue"; + private static final String HTTP_PARAM_VALUE1 = HTTP_PARAM_VALUE + "1"; + private static final String HTTP_PARAM_VALUE2 = HTTP_PARAM_VALUE + "2"; + + private static final String FORM_PARAM = "formParam"; + private static final String FORM_PARAM1 = FORM_PARAM + "1"; + private static final String FORM_PARAM2 = FORM_PARAM + "2"; + private static final String FORM_PARAM_VALUE = "formValue"; + private static final String FORM_PARAM_VALUE1 = FORM_PARAM_VALUE + "1"; + private static final String FORM_PARAM_VALUE2 = FORM_PARAM_VALUE + "2"; private Message mockMessage; private HttpServletRequest mockRequest; @Test public void populateMapFromStringFromHTTP() { - mockObjects(null); + mockObjects(null, 2); MultivaluedMap<String, String> params = new MetadataMap<>(); FormUtils.populateMapFromString(params, mockMessage, null, StandardCharsets.UTF_8.name(), @@ -66,7 +80,7 @@ public class FormUtilsTest { @Test public void populateMapFromStringFromHTTPWithProp() { - mockObjects("false"); + mockObjects("false", 2); MultivaluedMap<String, String> params = new MetadataMap<>(); FormUtils.populateMapFromString(params, mockMessage, null, StandardCharsets.UTF_8.name(), @@ -77,7 +91,7 @@ public class FormUtilsTest { @Test public void populateMapFromStringFromBody() { - mockObjects(null); + mockObjects(null, 2); MultivaluedMap<String, String> params = new MetadataMap<>(); String postBody = FORM_PARAM1 + "=" + FORM_PARAM_VALUE1 + "&" + FORM_PARAM2 + "=" + FORM_PARAM_VALUE2; @@ -90,19 +104,70 @@ public class FormUtilsTest { } - private void mockObjects(String formPropertyValue) { + @Test + public void populateMapFromBodyExceedsDefaultMaxFormParams() { + mockObjects(null, FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT); + + final MultivaluedMap<String, String> params = new MetadataMap<>(); + final String postBody = IntStream + .range(1, FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT + 1) + .mapToObj(i -> FORM_PARAM + i + "=" + FORM_PARAM_VALUE + i) + .collect(Collectors.joining("&")); + final WebApplicationException ex = assertThrows(WebApplicationException.class, + () -> FormUtils.populateMapFromString(params, mockMessage, postBody, + StandardCharsets.UTF_8.name(), false)); + assertThat(ex.getResponse().getStatus(), equalTo(413)); /* Request Entity Too Large */ + + // Increase the limit and try again + when(mockMessage.getContextualProperty("maxFormParameterCount")) + .thenReturn(Integer.toString(FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT + 1)); + FormUtils.populateMapFromString(params, mockMessage, null, StandardCharsets.UTF_8.name(), false, mockRequest); + assertEquals(500, params.size()); + } + + @Test + public void populateMapFromMultiPartExceedsDefaultMaxFormParams() { + mockObjects(null, FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT); + + final MultivaluedMap<String, String> params = new MetadataMap<>(); + final MultipartBody body = new MultipartBody(IntStream + .range(1, FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT + 1) + .mapToObj(i -> { + final MultivaluedMap<String, String> headers = new MetadataMap<>(); + headers.putSingle("Content-ID", UUID.randomUUID().toString()); + return new Attachment(InputStream.nullInputStream(), headers); + }) + .collect(Collectors.toList())); + final WebApplicationException ex = assertThrows(WebApplicationException.class, + () -> FormUtils.populateMapFromMultipart(params, body, mockMessage, false)); + assertThat(ex.getResponse().getStatus(), equalTo(413)); /* Request Entity Too Large */ + + // Increase the limit and try again + when(mockMessage.getContextualProperty("maxFormParameterCount")) + .thenReturn(Integer.toString(FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT + 1)); + FormUtils.populateMapFromMultipart(params, body, mockMessage, false); + assertEquals(500, params.size()); + } + + private void mockObjects(String formPropertyValue, int params) { + final ExchangeImpl exchange = new ExchangeImpl(); + mockMessage = mock(Message.class); when(mockMessage.getContextualProperty(FormUtils.FORM_PARAMS_FROM_HTTP_PARAMS)) .thenReturn(formPropertyValue); - when(mockMessage.getExchange()).thenReturn(null); + when(mockMessage.getExchange()).thenReturn(exchange); when(mockMessage.put(FormUtils.FORM_PARAM_MAP_DECODED, true)) .thenReturn(null); - + exchange.setInMessage(mockMessage); + mockRequest = mock(HttpServletRequest.class); - String[] httpParamNames = {HTTP_PARAM1, HTTP_PARAM2}; + String[] httpParamNames = IntStream.range(1, params + 1) + .mapToObj(i -> "httpParam" + i) + .toArray(String[]::new); Enumeration<String> httpParamsEnum = Collections.enumeration(Arrays.asList(httpParamNames)); when(mockRequest.getParameterNames()).thenReturn(httpParamsEnum); - when(mockRequest.getParameterValues(HTTP_PARAM1)).thenReturn(new String[] {HTTP_PARAM_VALUE1}); - when(mockRequest.getParameterValues(HTTP_PARAM2)).thenReturn(new String[] {HTTP_PARAM_VALUE2}); + for (int i = 1; i <= httpParamNames.length; ++i) { + when(mockRequest.getParameterValues(HTTP_PARAM + i)).thenReturn(new String[] {HTTP_PARAM_VALUE + i}); + } } } \ No newline at end of file
