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

joerghoh pushed a commit to branch SLING-13365
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-engine.git

commit 18e2f69da6afed8e5d4d292b0f96063e4e035886
Author: Joerg Hoh <[email protected]>
AuthorDate: Fri Sep 25 13:15:47 2026 +0200

    SLING-13365 fail by default when providing more than 10'000 parameters
---
 .../engine/impl/SlingRequestProcessorImpl.java     |   8 ++
 .../sling/engine/impl/parameters/ParameterMap.java |   2 +-
 .../impl/parameters/ParameterParseException.java   |  41 +++++++
 .../RequestParameterSupportConfigurer.java         |   7 +-
 .../engine/impl/SlingRequestProcessorImplTest.java | 127 +++++++++++++++++++++
 .../engine/impl/parameters/ParameterMapTest.java   |   4 +-
 6 files changed, 183 insertions(+), 6 deletions(-)

diff --git 
a/src/main/java/org/apache/sling/engine/impl/SlingRequestProcessorImpl.java 
b/src/main/java/org/apache/sling/engine/impl/SlingRequestProcessorImpl.java
index 27451b6..cd6bb96 100644
--- a/src/main/java/org/apache/sling/engine/impl/SlingRequestProcessorImpl.java
+++ b/src/main/java/org/apache/sling/engine/impl/SlingRequestProcessorImpl.java
@@ -66,6 +66,7 @@ import 
org.apache.sling.engine.impl.filter.ServletFilterManager;
 import 
org.apache.sling.engine.impl.filter.ServletFilterManager.FilterChainType;
 import org.apache.sling.engine.impl.filter.SlingComponentFilterChain;
 import org.apache.sling.engine.impl.helper.SlingServletContext;
+import org.apache.sling.engine.impl.parameters.ParameterParseException;
 import org.apache.sling.engine.impl.parameters.ParameterSupport;
 import org.apache.sling.engine.impl.request.ContentData;
 import org.apache.sling.engine.impl.request.DispatchingInfo;
@@ -311,6 +312,13 @@ public class SlingRequestProcessorImpl implements 
SlingRequestProcessor {
             log.debug("service: Resource {} not found", rnfe.getResource());
             handleError(HttpServletResponse.SC_NOT_FOUND, rnfe.getMessage(), 
request, response);
 
+        } catch (final ParameterParseException ppe) {
+            // the request parameters could not be processed (e.g. too many
+            // parameters); the request itself is malformed/excessive, not the
+            // server, so send this exception as a 400 status
+            log.debug("service: Failed processing request parameters", ppe);
+            handleError(HttpServletResponse.SC_BAD_REQUEST, ppe.getMessage(), 
request, response);
+
         } catch (final SlingException se) {
             // send this exception as is (albeit unwrapping and wrapped
             // exception.
diff --git 
a/src/main/java/org/apache/sling/engine/impl/parameters/ParameterMap.java 
b/src/main/java/org/apache/sling/engine/impl/parameters/ParameterMap.java
index 884fd5b..611456f 100644
--- a/src/main/java/org/apache/sling/engine/impl/parameters/ParameterMap.java
+++ b/src/main/java/org/apache/sling/engine/impl/parameters/ParameterMap.java
@@ -78,7 +78,7 @@ public class ParameterMap extends LinkedHashMap<String, 
RequestParameter[]> impl
         // check number of parameters
         if (maxParameters > -1 && this.requestParameters.size() >= 
maxParameters) {
             if (failOnParameterLimit) {
-                throw new IllegalStateException("Too many name/value pairs, 
limit is " + maxParameters);
+                throw new ParameterParseException("Too many name/value pairs, 
limit is " + maxParameters);
             }
             LoggerFactory.getLogger(Util.class)
                     .warn("Too many name/value pairs, stopped processing after 
" + maxParameters + " entries");
diff --git 
a/src/main/java/org/apache/sling/engine/impl/parameters/ParameterParseException.java
 
b/src/main/java/org/apache/sling/engine/impl/parameters/ParameterParseException.java
new file mode 100644
index 0000000..f47ae7e
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/engine/impl/parameters/ParameterParseException.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.engine.impl.parameters;
+
+/**
+ * Unchecked exception indicating that the request parameters could not be
+ * processed as-is, e.g. because the request exceeds the configured maximum
+ * number of parameters. This is a client-caused, request-fatal condition: the
+ * request must be refused (mapped to an HTTP 400 response by
+ * {@code SlingRequestProcessorImpl}) rather than processed with a silently
+ * truncated parameter map, which would let an attacker hide parameters from
+ * Sling-side consumers while other parsers of the same request (edge WAFs,
+ * the container) still see them.
+ * <p>
+ * Extends {@link IllegalStateException} to remain compatible with callers
+ * that only checked for that (more generic) exception type.
+ */
+public class ParameterParseException extends IllegalStateException {
+
+    private static final long serialVersionUID = 1L;
+
+    public ParameterParseException(final String message) {
+        super(message);
+    }
+}
diff --git 
a/src/main/java/org/apache/sling/engine/impl/parameters/RequestParameterSupportConfigurer.java
 
b/src/main/java/org/apache/sling/engine/impl/parameters/RequestParameterSupportConfigurer.java
index 7421b46..f1e6f20 100644
--- 
a/src/main/java/org/apache/sling/engine/impl/parameters/RequestParameterSupportConfigurer.java
+++ 
b/src/main/java/org/apache/sling/engine/impl/parameters/RequestParameterSupportConfigurer.java
@@ -132,9 +132,10 @@ public class RequestParameterSupportConfigurer implements 
Filter {
         @AttributeDefinition(
                 name = "Fail on Parameter Limit",
                 description = "Whether to throw an exception when the maximum 
number of parameters is exceeded. "
-                        + "If false (default), a warning is logged and 
processing continues with truncated parameters. "
-                        + "If true, an IllegalStateException is thrown.")
-        boolean sling_default_parameter_fail_on_limit() default false;
+                        + "If true (default), an exception is thrown and the 
request is rejected with a 400 Bad "
+                        + "Request response. If false, a warning is logged and 
processing continues with the "
+                        + "parameter map silently truncated after the 
configured limit.")
+        boolean sling_default_parameter_fail_on_limit() default true;
     }
 
     static final String PID = "org.apache.sling.engine.parameters";
diff --git 
a/src/test/java/org/apache/sling/engine/impl/SlingRequestProcessorImplTest.java 
b/src/test/java/org/apache/sling/engine/impl/SlingRequestProcessorImplTest.java
new file mode 100644
index 0000000..b07f3cc
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/engine/impl/SlingRequestProcessorImplTest.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.engine.impl;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+
+import jakarta.servlet.Servlet;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.sling.api.SlingJakartaHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceMetadata;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.servlets.ServletResolver;
+import org.apache.sling.engine.impl.filter.FilterHandle;
+import org.apache.sling.engine.impl.filter.ServletFilterManager;
+import 
org.apache.sling.engine.impl.filter.ServletFilterManager.FilterChainType;
+import org.apache.sling.engine.impl.parameters.ParameterParseException;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Before;
+import org.junit.Test;
+
+import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link SlingRequestProcessorImpl}, in particular the
+ * {@code ParameterParseException} to HTTP 400 mapping performed in
+ * {@code doProcessRequest} (regression test for SLING-13138/f018: a
+ * request refused for exceeding the configured parameter limit must
+ * surface as a 400 Bad Request, not an uncaught 500).
+ */
+public class SlingRequestProcessorImplTest {
+
+    private SlingRequestProcessorImpl processor;
+    private ServletFilterManager filterManager;
+
+    @Before
+    public void setup() throws Exception {
+        processor = new SlingRequestProcessorImpl();
+
+        filterManager = mock(ServletFilterManager.class);
+        when(filterManager.getFilters(FilterChainType.ERROR)).thenReturn(new 
FilterHandle[0]);
+        setField(processor, "filterManager", filterManager);
+    }
+
+    private static void setField(final Object target, final String name, final 
Object value) throws Exception {
+        Field field = target.getClass().getDeclaredField(name);
+        field.setAccessible(true);
+        field.set(target, value);
+    }
+
+    @Test
+    public void testDoProcessRequestMapsParameterParseExceptionToBadRequest() 
throws Exception {
+        final Servlet servlet = mock(Servlet.class);
+        doThrow(new ParameterParseException("Too many name/value pairs, limit 
is 10000"))
+                .when(servlet)
+                .service(any(ServletRequest.class), 
any(ServletResponse.class));
+
+        final Resource resource = getMockedResource("/content/test");
+
+        final ResourceResolver resourceResolver = mock(ResourceResolver.class);
+        when(resourceResolver.resolve(any(HttpServletRequest.class), 
anyString()))
+                .thenReturn(resource);
+
+        final ServletResolver servletResolver = mock(ServletResolver.class);
+        
when(servletResolver.resolve(any(SlingJakartaHttpServletRequest.class))).thenReturn(servlet);
+        setField(processor, "servletResolver", servletResolver);
+
+        // no request/component filters: the chain falls straight through to
+        // the resolved servlet, whose service() call raises the exception
+        when(filterManager.getFilters(FilterChainType.REQUEST)).thenReturn(new 
FilterHandle[0]);
+        
when(filterManager.getFilters(FilterChainType.COMPONENT)).thenReturn(new 
FilterHandle[0]);
+
+        final HttpServletRequest httpServletRequest = 
mock(HttpServletRequest.class);
+        when(httpServletRequest.getRequestURI()).thenReturn("/content/test");
+        when(httpServletRequest.getRequestURL()).thenReturn(new 
StringBuffer("http://localhost/content/test";));
+        when(httpServletRequest.getContextPath()).thenReturn("");
+        when(httpServletRequest.getServletPath()).thenReturn("");
+        when(httpServletRequest.getMethod()).thenReturn("GET");
+
+        final HttpServletResponse httpServletResponse = 
mock(HttpServletResponse.class);
+        final StringWriter writer = new StringWriter();
+        when(httpServletResponse.getWriter()).thenReturn(new 
PrintWriter(writer));
+
+        processor.doProcessRequest(httpServletRequest, httpServletResponse, 
resourceResolver);
+
+        verify(httpServletResponse).setStatus(SC_BAD_REQUEST);
+        writer.flush();
+        assertTrue(writer.toString().contains("Too many name/value pairs"));
+    }
+
+    private static @NotNull Resource getMockedResource(final @NotNull String 
path) {
+        final Resource resource = mock(Resource.class);
+        when(resource.getPath()).thenReturn(path);
+        final ResourceMetadata resourceMetadata = mock(ResourceMetadata.class);
+        when(resource.getResourceMetadata()).thenReturn(resourceMetadata);
+        when(resourceMetadata.getResolutionPathInfo()).thenReturn(path);
+        return resource;
+    }
+}
diff --git 
a/src/test/java/org/apache/sling/engine/impl/parameters/ParameterMapTest.java 
b/src/test/java/org/apache/sling/engine/impl/parameters/ParameterMapTest.java
index a89e279..0387db7 100644
--- 
a/src/test/java/org/apache/sling/engine/impl/parameters/ParameterMapTest.java
+++ 
b/src/test/java/org/apache/sling/engine/impl/parameters/ParameterMapTest.java
@@ -76,7 +76,7 @@ public class ParameterMapTest {
         assertEquals(2, pm.size());
 
         // Should throw exception when exceeding limit
-        exception.expect(IllegalStateException.class);
+        exception.expect(ParameterParseException.class);
         exception.expectMessage("Too many name/value pairs");
         exception.expectMessage("2");
         pm.addParameter(createTestParameter("param3", "value3"), false);
@@ -123,7 +123,7 @@ public class ParameterMapTest {
         assertEquals(5, pm.size());
 
         // Next should fail
-        exception.expect(IllegalStateException.class);
+        exception.expect(ParameterParseException.class);
         exception.expectMessage("Too many name/value pairs");
         exception.expectMessage("5");
         pm.addParameter(createTestParameter("param6", "value6"), false);

Reply via email to