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

rmaucher pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 7bacd86a631f858a6ff3c83f08fb5a2a6367735d
Author: opencode <[email protected]>
AuthorDate: Mon Oct 12 11:54:11 2026 +0200

    Do not apply partial ParameterLimitValve configuration when parsing fails
    
    setUrlPatternLimits() parsed the configuration line by line directly
    into the shared map of enforced limits. An invalid line, an invalid
    number of limits or a non-numeric limit propagated an exception after
    the lines parsed so far had already been applied, leaving the valve
    enforcing a partial, inconsistent limit set. The String overload made
    this worse by clearing the previously configured limits before
    parsing, so a failed runtime update through the management operation
    both lost the old configuration and enforced a partial one. An IO error
    mid-read was logged but likewise left partial state in effect.
    
    Parse into a temporary map and only merge it into the enforced limits
    once the complete input has been parsed successfully. On a parse
    exception the previously configured limits are now left untouched, and
    on an IO error the input read so far is discarded. The append semantics
    of the BufferedReader overload and the replace semantics of the String
    overload are unchanged.
    
    Also use the parameterLimitValve.invalidLimits message key that is
    actually defined in the bundles for the invalid limit count error,
    instead of the undefined parameterLimitValve.invalidLimitsString, so
    the message is no longer rendered as a missing-resource placeholder.
---
 .../catalina/valves/ParameterLimitValve.java       | 36 +++++++++++++++++----
 .../catalina/valves/TestParameterLimitValve.java   | 37 ++++++++++++++++++++++
 2 files changed, 67 insertions(+), 6 deletions(-)

diff --git a/java/org/apache/catalina/valves/ParameterLimitValve.java 
b/java/org/apache/catalina/valves/ParameterLimitValve.java
index b0f2748bae..2c54f2a6f8 100644
--- a/java/org/apache/catalina/valves/ParameterLimitValve.java
+++ b/java/org/apache/catalina/valves/ParameterLimitValve.java
@@ -187,18 +187,25 @@ public class ParameterLimitValve extends ValveBase {
 
     /**
      * Set the mapping of URL patterns to their corresponding parameter limits 
from a string containing the
-     * configuration.
+     * configuration. The new limits replace any previously configured limits. 
The limits are only applied if the
+     * complete configuration is parsed successfully. If parsing fails, the 
exception is propagated and any previously
+     * configured limits are left unchanged.
      *
      * @param urlPatternConfig The URL pattern to parameter limit mappings
      */
     public void setUrlPatternLimits(String urlPatternConfig) {
+        Map<Pattern,Integer[]> newLimits =
+                parseUrlPatternLimits(new BufferedReader(new 
StringReader(urlPatternConfig)));
         urlPatternLimits.clear();
-        setUrlPatternLimits(new BufferedReader(new 
StringReader(urlPatternConfig)));
+        urlPatternLimits.putAll(newLimits);
     }
 
     /**
      * Set the mapping of URL patterns to their corresponding parameter 
limits. The input should be provided line by
-     * line, where each line contains a pattern and a limit, separated by the 
last '='.
+     * line, where each line contains a pattern and a limit, separated by the 
last '='. The parsed limits are only
+     * applied if the complete input is parsed successfully. If parsing fails, 
the exception is propagated and any
+     * previously configured limits are left unchanged. If a read error 
occurs, it is logged and the input read so far
+     * is discarded without modifying the configured limits.
      * <p>
      * Example:
      *
@@ -213,9 +220,23 @@ public class ParameterLimitValve extends ValveBase {
      *                   line.
      */
     public void setUrlPatternLimits(BufferedReader reader) {
+        urlPatternLimits.putAll(parseUrlPatternLimits(reader));
+    }
+
+    /**
+     * Parse URL pattern to parameter limit mappings without modifying the 
limits that are currently in effect. This
+     * avoids enforcing a partial, inconsistent limit set when parsing fails 
part way through.
+     *
+     * @param reader A BufferedReader containing URL pattern to parameter 
limit mappings, with each pair on a separate
+     *                   line.
+     *
+     * @return The parsed mappings
+     */
+    private Map<Pattern,Integer[]> parseUrlPatternLimits(BufferedReader 
reader) {
         if (containerLog == null && getContainer() != null) {
             containerLog = LogFactory.getLog(getContainer().getLogName() + 
".parameterLimit");
         }
+        Map<Pattern,Integer[]> newLimits = new ConcurrentHashMap<>();
         try {
             String line;
             while ((line = reader.readLine()) != null) {
@@ -237,13 +258,13 @@ public class ParameterLimitValve extends ValveBase {
                 Pattern pattern = 
Pattern.compile(UDecoder.URLDecode(patternString, StandardCharsets.UTF_8));
                 String[] limits = limitsString.split(",");
                 if (limits.length == 1) {
-                    urlPatternLimits.put(pattern, new Integer[] { 
Integer.valueOf(limits[0]), null, null });
+                    newLimits.put(pattern, new Integer[] { 
Integer.valueOf(limits[0]), null, null });
                 } else if (limits.length == 3) {
-                    urlPatternLimits.put(pattern, new Integer[] { 
Integer.valueOf(limits[0]),
+                    newLimits.put(pattern, new Integer[] { 
Integer.valueOf(limits[0]),
                             Integer.valueOf(limits[1]), 
Integer.valueOf(limits[2]) });
                 } else {
                     throw new IllegalArgumentException(
-                            
sm.getString("parameterLimitValve.invalidLimitsString", limitsString));
+                            sm.getString("parameterLimitValve.invalidLimits", 
limitsString));
                 }
                 if (containerLog != null && containerLog.isTraceEnabled()) {
                     containerLog.trace("Add pattern " + pattern + " and 
limit(s) " + limitsString);
@@ -253,7 +274,10 @@ public class ParameterLimitValve extends ValveBase {
             if (containerLog != null) {
                 
containerLog.error(sm.getString("parameterLimitValve.readError"), ioe);
             }
+            // Do not apply the limits parsed before the error
+            return Map.of();
         }
+        return newLimits;
     }
 
     @Override
diff --git a/test/org/apache/catalina/valves/TestParameterLimitValve.java 
b/test/org/apache/catalina/valves/TestParameterLimitValve.java
index 5f5aab6eeb..5c34d94cb5 100644
--- a/test/org/apache/catalina/valves/TestParameterLimitValve.java
+++ b/test/org/apache/catalina/valves/TestParameterLimitValve.java
@@ -592,6 +592,43 @@ public class TestParameterLimitValve extends 
TomcatBaseTest {
     }
 
 
+    @Test
+    public void testInvalidConfigLeavesLimitsUnchanged() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        StandardContext ctx = (StandardContext) getProgrammaticRootContext();
+
+        ParameterLimitValve parameterLimitValve = new ParameterLimitValve();
+        ctx.getPipeline().addValve(parameterLimitValve);
+        parameterLimitValve.setUrlPatternLimits("/special/.*=2");
+
+        // An invalid configuration update must fail and must leave the
+        // previously configured limits in effect. Previously, the limits were
+        // cleared first and the lines parsed before the failure remained.
+        try {
+            parameterLimitValve.setUrlPatternLimits(
+                    "/other/.*=5" + CRLF + "invalid-line-without-equals");
+            Assert.fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            // Expected
+        }
+
+        Tomcat.addServlet(ctx, "snoop", new SnoopServlet());
+        ctx.addServletMapping("/special/endpoint", "snoop");
+
+        tomcat.start();
+
+        int rc = getUrl("http://localhost:"; + getPort() +
+                "/special/endpoint?param1=value1&param2=value2&param3=value3",
+            new ByteChunk(), null);
+        Assert.assertEquals(400, rc);
+
+        rc = getUrl("http://localhost:"; + getPort() + 
"/special/endpoint?param1=value1&param2=value2",
+            new ByteChunk(), null);
+        Assert.assertEquals(200, rc);
+    }
+
+
     private void doTestMaxParameterCountLimitExceeded(int maxParameterCount, 
int textPartCount, int filePartCount,
             int queryStringCount, boolean okExpected) throws Exception {
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to