From 9189756080c181414aa6855cee02b6f897fe8b8b Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Wed, 9 Sep 2026 08:01:53 -0400 Subject: [PATCH] Add maxFormParameterSize contextual message parameter and bound form params processing to it --- .../jaxrs/impl/HttpServletRequestFilter.java | 3 ++- .../org/apache/cxf/jaxrs/utils/FormUtils.java | 5 ++++ .../apache/cxf/jaxrs/utils/JAXRSUtils.java | 2 +- .../client/ClientCodeRequestFilter.java | 7 ++++-- .../oidc/rp/OidcIdTokenRequestFilter.java | 7 ++++-- .../oidc/rp/OidcRpAuthenticationFilter.java | 7 ++++-- .../jaxrs/JAXRSClientServerBookTest.java | 24 +++++++++++++++++++ 7 files changed, 47 insertions(+), 8 deletions(-) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpServletRequestFilter.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpServletRequestFilter.java index 36285ca3c71..dba00c4b156 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpServletRequestFilter.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpServletRequestFilter.java @@ -119,7 +119,8 @@ private void readFromParamsIfNeeded() { formParams = new MetadataMap<>(); MediaType mt = JAXRSUtils.toMediaType((String)m.get(Message.CONTENT_TYPE)); String enc = HttpUtils.getEncoding(mt, StandardCharsets.UTF_8.name()); - String body = FormUtils.readBody(m.getContent(InputStream.class), enc); + String body = FormUtils.readBody(m.getContent(InputStream.class), enc, + FormUtils.getMaxFormParamsSize(m)); FormUtils.populateMapFromString(formParams, m, body, enc, true); } } 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 3479f737e95..fd8c5cd1fb6 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 @@ -67,6 +67,7 @@ public final class FormUtils { private static final Logger LOG = LogUtils.getL7dLogger(FormUtils.class); private static final String MULTIPART_FORM_DATA_TYPE = "form-data"; private static final String MAX_FORM_PARAM_COUNT = "maxFormParameterCount"; + private static final String MAX_FORM_PARAM_SIZE = "maxFormParameterSize"; private static final String CONTENT_DISPOSITION_FILES_PARAM = "files"; private FormUtils() { @@ -334,4 +335,8 @@ public static boolean isFormPostRequest(Message m) { return MediaType.APPLICATION_FORM_URLENCODED.equals(m.get(Message.CONTENT_TYPE)) && HttpMethod.POST.equals(m.get(Message.HTTP_REQUEST_METHOD)); } + + public static int getMaxFormParamsSize(Message m) { + return MessageUtils.getContextualInteger(m, MAX_FORM_PARAM_SIZE, DEFAULT_FORM_PARAMS_MAX_SIZE); + } } diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java index d1beb0c25c8..1bcb78e7eaf 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java @@ -1198,7 +1198,7 @@ private static Object processFormParam(Message m, String key, if (mt == null || mt.isCompatible(MediaType.APPLICATION_FORM_URLENCODED_TYPE)) { InputStream entityStream = copyAndGetEntityStream(m); - String body = FormUtils.readBody(entityStream, enc); + String body = FormUtils.readBody(entityStream, enc, FormUtils.getMaxFormParamsSize(m)); // Do not decode unless the key is empty value, fe @FormParam("") FormUtils.populateMapFromStringOrHttpRequest(params, m, body, enc, StringUtils.isEmpty(key) && decode); } else { diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index ffdf95c48cb..cbfc8bcb1e1 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -44,6 +44,7 @@ import org.apache.cxf.jaxrs.utils.ExceptionUtils; import org.apache.cxf.jaxrs.utils.FormUtils; import org.apache.cxf.jaxrs.utils.JAXRSUtils; +import org.apache.cxf.message.Message; import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken; import org.apache.cxf.rs.security.oauth2.grants.code.AuthorizationCodeGrant; import org.apache.cxf.rs.security.oauth2.grants.code.CodeVerifierTransformer; @@ -311,8 +312,10 @@ protected MultivaluedMap toRequestState(ContainerRequestContext MultivaluedMap requestState = new MetadataMap<>(); requestState.putAll(ui.getQueryParameters(decodeRequestParameters)); if (MediaType.APPLICATION_FORM_URLENCODED_TYPE.isCompatible(rc.getMediaType())) { - String body = FormUtils.readBody(rc.getEntityStream(), StandardCharsets.UTF_8.name()); - FormUtils.populateMapFromString(requestState, JAXRSUtils.getCurrentMessage(), body, + final Message currentMessage = JAXRSUtils.getCurrentMessage(); + String body = FormUtils.readBody(rc.getEntityStream(), StandardCharsets.UTF_8.name(), + FormUtils.getMaxFormParamsSize(currentMessage)); + FormUtils.populateMapFromString(requestState, currentMessage, body, StandardCharsets.UTF_8.name(), decodeRequestParameters); } return requestState; diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcIdTokenRequestFilter.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcIdTokenRequestFilter.java index 88b6ce62991..4d6258e581c 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcIdTokenRequestFilter.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcIdTokenRequestFilter.java @@ -31,6 +31,7 @@ import org.apache.cxf.jaxrs.impl.MetadataMap; import org.apache.cxf.jaxrs.utils.FormUtils; import org.apache.cxf.jaxrs.utils.JAXRSUtils; +import org.apache.cxf.message.Message; import org.apache.cxf.rs.security.oauth2.client.Consumer; import org.apache.cxf.rs.security.oidc.common.IdToken; @@ -60,8 +61,10 @@ public void filter(ContainerRequestContext requestContext) throws IOException { private MultivaluedMap toFormData(ContainerRequestContext rc) { MultivaluedMap requestState = new MetadataMap<>(); if (MediaType.APPLICATION_FORM_URLENCODED_TYPE.isCompatible(rc.getMediaType())) { - String body = FormUtils.readBody(rc.getEntityStream(), StandardCharsets.UTF_8.name()); - FormUtils.populateMapFromString(requestState, JAXRSUtils.getCurrentMessage(), body, + final Message currentMessage = JAXRSUtils.getCurrentMessage(); + String body = FormUtils.readBody(rc.getEntityStream(), StandardCharsets.UTF_8.name(), + FormUtils.getMaxFormParamsSize(currentMessage)); + FormUtils.populateMapFromString(requestState, currentMessage, body, StandardCharsets.UTF_8.name(), false); rc.setEntityStream(new ByteArrayInputStream(StringUtils.toBytesUTF8(body))); } diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationFilter.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationFilter.java index 48aae736602..d8731639099 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationFilter.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationFilter.java @@ -39,6 +39,7 @@ import org.apache.cxf.jaxrs.impl.MetadataMap; import org.apache.cxf.jaxrs.utils.FormUtils; import org.apache.cxf.jaxrs.utils.JAXRSUtils; +import org.apache.cxf.message.Message; import org.apache.cxf.rs.security.jose.jwt.JwtException; import org.apache.cxf.rs.security.jose.jwt.JwtUtils; import org.apache.cxf.rs.security.oauth2.client.ClientTokenContext; @@ -114,8 +115,10 @@ private MultivaluedMap toRequestState(ContainerRequestContext rc MultivaluedMap requestState = new MetadataMap<>(); requestState.putAll(rc.getUriInfo().getQueryParameters(true)); if (MediaType.APPLICATION_FORM_URLENCODED_TYPE.isCompatible(rc.getMediaType())) { - String body = FormUtils.readBody(rc.getEntityStream(), StandardCharsets.UTF_8.name()); - FormUtils.populateMapFromString(requestState, JAXRSUtils.getCurrentMessage(), body, + final Message currentMessage = JAXRSUtils.getCurrentMessage(); + String body = FormUtils.readBody(rc.getEntityStream(), StandardCharsets.UTF_8.name(), + FormUtils.getMaxFormParamsSize(currentMessage)); + FormUtils.populateMapFromString(requestState, currentMessage, body, StandardCharsets.UTF_8.name(), true); rc.setEntityStream(new ByteArrayInputStream(StringUtils.toBytesUTF8(body))); 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 4b0b1d9d498..8fb2e885b62 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 @@ -355,6 +355,17 @@ public void testTooManyFormParams() throws Exception { assertThat("max form params limit reached", r.getStatus(), equalTo(413)); } + @Test + public void testTooManyFormParamsUsingForm() throws Exception { + // Exceeding 500 limit + String params = IntStream.range(0, 501).mapToObj(i -> "id" + i + "=" + i).collect(Collectors.joining("&")); + String address = "http://localhost:" + PORT + "/bookstore/formParams/1"; + 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 @@ -368,6 +379,19 @@ public void testTooLargeFormParams() throws Exception { assertThat("max form params limit reached", r.getStatus(), equalTo(500)); } + @Test + public void testTooLargeFormParamsUsingForm() throws Exception { + // Exceeding 100Mb limit + String params = IntStream.range(0, 150) + .mapToObj(i -> "id" + i + "=" + + new String(Integer.toString(i)).repeat(524800)).collect(Collectors.joining("&")); + String address = "http://localhost:" + PORT + "/bookstore/formParams/1"; + 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";