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

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

commit 2a8394e721ffeaeef90403d241ca951123b0bcac
Author: opencode <[email protected]>
AuthorDate: Wed Oct 14 14:29:42 2026 +0200

    Validate the cookie max-age value at deployment time
    
    SessionConfig.setCookieMaxAge(String) stored the raw string and
    getCookieMaxAge() parsed it lazily. The value is copied verbatim by
    ContextConfig into the runtime SessionCookieConfig, where
    ApplicationSessionCookieConfig.getMaxAge() parses it again on every
    session cookie creation. Web.xml XSD validation is off by default, so an
    invalid value such as "abc" passed parsing and then threw a raw
    NumberFormatException on every session creation and session id change
    for the application, instead of failing the deployment.
    
    The setter now validates the value with Integer.valueOf() before
    storing it, consistently with setSessionTimeout() in the same class. The
    digester wraps the resulting NumberFormatException into a SAX parse
    error that reports the location in the descriptor and fails the
    deployment cleanly. Valid values, including negative ones used to
    disable cookie expiry, are stored unchanged.
    
    The generic cookie attribute path (SessionConfig.setCookieAttribute, the
    descriptor mirror of SessionCookieConfig.setAttribute) accepts arbitrary
    strings and remains unvalidated, which is pre-existing and mirrors the
    programmatic API.
    
    Adds TestSessionConfig for the setter and parse level tests in
    TestWebXmlParser covering valid, invalid and empty max-age elements.
---
 .../tomcat/util/descriptor/web/SessionConfig.java  |  5 ++
 .../util/descriptor/web/TestSessionConfig.java     | 82 ++++++++++++++++++++++
 .../util/descriptor/web/TestWebXmlParser.java      | 44 ++++++++++++
 3 files changed, 131 insertions(+)

diff --git a/java/org/apache/tomcat/util/descriptor/web/SessionConfig.java 
b/java/org/apache/tomcat/util/descriptor/web/SessionConfig.java
index 6525e6a481..898a059a56 100644
--- a/java/org/apache/tomcat/util/descriptor/web/SessionConfig.java
+++ b/java/org/apache/tomcat/util/descriptor/web/SessionConfig.java
@@ -176,6 +176,11 @@ public class SessionConfig {
      * @param cookieMaxAge the max age
      */
     public void setCookieMaxAge(String cookieMaxAge) {
+        // Validate the value at deployment time, consistently with
+        // setSessionTimeout(), so an invalid value fails the deployment with a
+        // parse error instead of causing a NumberFormatException at run time
+        // when the value is parsed to create each session cookie
+        Integer.valueOf(cookieMaxAge);
         setCookieAttribute(Constants.COOKIE_MAX_AGE_ATTR, cookieMaxAge);
     }
 
diff --git a/test/org/apache/tomcat/util/descriptor/web/TestSessionConfig.java 
b/test/org/apache/tomcat/util/descriptor/web/TestSessionConfig.java
new file mode 100644
index 0000000000..0ab55d5589
--- /dev/null
+++ b/test/org/apache/tomcat/util/descriptor/web/TestSessionConfig.java
@@ -0,0 +1,82 @@
+/*
+ * 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.tomcat.util.descriptor.web;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestSessionConfig {
+
+    @Test
+    public void testCookieMaxAgeUnset() {
+        SessionConfig sessionConfig = new SessionConfig();
+        Assert.assertNull(sessionConfig.getCookieMaxAge());
+    }
+
+    @Test
+    public void testCookieMaxAgeValid() {
+        SessionConfig sessionConfig = new SessionConfig();
+
+        sessionConfig.setCookieMaxAge("60");
+        Assert.assertEquals(Integer.valueOf(60), 
sessionConfig.getCookieMaxAge());
+        Assert.assertEquals("60", 
sessionConfig.getCookieAttribute(Constants.COOKIE_MAX_AGE_ATTR));
+
+        sessionConfig.setCookieMaxAge("0");
+        Assert.assertEquals(Integer.valueOf(0), 
sessionConfig.getCookieMaxAge());
+
+        // Negative values disable cookie expiry and must be accepted
+        sessionConfig.setCookieMaxAge("-1");
+        Assert.assertEquals(Integer.valueOf(-1), 
sessionConfig.getCookieMaxAge());
+    }
+
+    @Test
+    public void testCookieMaxAgeInvalid() {
+        SessionConfig sessionConfig = new SessionConfig();
+
+        // An invalid value must be rejected at deployment time rather than
+        // causing a NumberFormatException whenever a session cookie is created
+        try {
+            sessionConfig.setCookieMaxAge("abc");
+            Assert.fail("NumberFormatException expected");
+        } catch (NumberFormatException e) {
+            // Expected
+        }
+        
Assert.assertNull(sessionConfig.getCookieAttribute(Constants.COOKIE_MAX_AGE_ATTR));
+        Assert.assertNull(sessionConfig.getCookieMaxAge());
+
+        try {
+            sessionConfig.setCookieMaxAge("");
+            Assert.fail("NumberFormatException expected");
+        } catch (NumberFormatException e) {
+            // Expected
+        }
+        
Assert.assertNull(sessionConfig.getCookieAttribute(Constants.COOKIE_MAX_AGE_ATTR));
+    }
+
+    @Test
+    public void testCookieMaxAgeOverflow() {
+        SessionConfig sessionConfig = new SessionConfig();
+
+        try {
+            sessionConfig.setCookieMaxAge("2147483648");
+            Assert.fail("NumberFormatException expected");
+        } catch (NumberFormatException e) {
+            // Expected
+        }
+        
Assert.assertNull(sessionConfig.getCookieAttribute(Constants.COOKIE_MAX_AGE_ATTR));
+    }
+}
diff --git a/test/org/apache/tomcat/util/descriptor/web/TestWebXmlParser.java 
b/test/org/apache/tomcat/util/descriptor/web/TestWebXmlParser.java
index 5bcf23a10a..3158e6368a 100644
--- a/test/org/apache/tomcat/util/descriptor/web/TestWebXmlParser.java
+++ b/test/org/apache/tomcat/util/descriptor/web/TestWebXmlParser.java
@@ -16,12 +16,15 @@
  */
 package org.apache.tomcat.util.descriptor.web;
 
+import java.io.StringReader;
 import java.net.URL;
 import java.util.Set;
 
 import org.junit.Assert;
 import org.junit.Test;
 
+import org.xml.sax.InputSource;
+
 public class TestWebXmlParser {
 
     private static final String WEB_XML = "TestWebXmlParser-web.xml";
@@ -64,4 +67,45 @@ public class TestWebXmlParser {
         SecurityCollection securityCollection = 
securityConstraint.findCollection("resource");
         Assert.assertArrayEquals(new String[] { "/secure" + expectedSuffix }, 
securityCollection.findPatterns());
     }
+
+    @Test
+    public void testCookieMaxAgeInvalidFailsParse() {
+        // An invalid max-age value must fail the parse so the deployment 
fails,
+        // rather than breaking session cookie creation at run time
+        String webXml = "<web-app><session-config><cookie-config>" +
+                "<max-age>abc</max-age>" +
+                "</cookie-config></session-config></web-app>";
+
+        WebXml result = new WebXml();
+        WebXmlParser parser = new WebXmlParser(false, false, true);
+
+        Assert.assertFalse(parser.parseWebXml(new InputSource(new 
StringReader(webXml)), result, false));
+    }
+
+    @Test
+    public void testCookieMaxAgeEmptyFailsParse() {
+        // The digester passes an empty body text to the setter, so an empty
+        // element must also fail the parse
+        String webXml = "<web-app><session-config><cookie-config>" +
+                "<max-age></max-age>" +
+                "</cookie-config></session-config></web-app>";
+
+        WebXml result = new WebXml();
+        WebXmlParser parser = new WebXmlParser(false, false, true);
+
+        Assert.assertFalse(parser.parseWebXml(new InputSource(new 
StringReader(webXml)), result, false));
+    }
+
+    @Test
+    public void testCookieMaxAgeValidParses() {
+        String webXml = "<web-app><session-config><cookie-config>" +
+                "<max-age>60</max-age>" +
+                "</cookie-config></session-config></web-app>";
+
+        WebXml result = new WebXml();
+        WebXmlParser parser = new WebXmlParser(false, false, true);
+
+        Assert.assertTrue(parser.parseWebXml(new InputSource(new 
StringReader(webXml)), result, false));
+        Assert.assertEquals(Integer.valueOf(60), 
result.getSessionConfig().getCookieMaxAge());
+    }
 }


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

Reply via email to