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

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


The following commit(s) were added to refs/heads/11.0.x by this push:
     new d5cf2c9723 Decline permessage-deflate offers with invalid parameter 
values (#1110)
d5cf2c9723 is described below

commit d5cf2c9723e48982b58868a6622ac37baef37e8d
Author: Arturo Bernal <[email protected]>
AuthorDate: Wed Nov 4 20:08:36 2026 +0100

    Decline permessage-deflate offers with invalid parameter values (#1110)
    
    RFC 7692, section 7.1 requires an offer that contains a parameter with
    an invalid value to be declined. The no_context_takeover parameters have
    no value, but a value was ignored, and the max_window_bits parameters
    are integers without leading zeroes, but values such as "+15" or "015"
    were accepted. Decline the offer in these cases.
---
 .../tomcat/websocket/LocalStrings.properties       |  1 +
 .../apache/tomcat/websocket/PerMessageDeflate.java | 31 ++++++++++++++++--
 .../tomcat/websocket/TestPerMessageDeflate.java    | 38 ++++++++++++++++++++++
 3 files changed, 68 insertions(+), 2 deletions(-)

diff --git a/java/org/apache/tomcat/websocket/LocalStrings.properties 
b/java/org/apache/tomcat/websocket/LocalStrings.properties
index 3a862bdaa5..fd12229b4e 100644
--- a/java/org/apache/tomcat/websocket/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/LocalStrings.properties
@@ -55,6 +55,7 @@ futureToSendHandler.timeout=Operation timed out after waiting 
[{0}] [{1}] to com
 perMessageDeflate.alreadyClosed=The transformer has been closed and may no 
longer be used
 perMessageDeflate.deflateFailed=Failed to decompress a compressed WebSocket 
frame
 perMessageDeflate.duplicateParameter=Duplicate definition of the [{0}] 
extension parameter
+perMessageDeflate.invalidParameterValue=The [{0}] extension parameter has an 
invalid value [{1}]
 perMessageDeflate.invalidState=Invalid state
 perMessageDeflate.invalidWindowSize=An invalid window size of [{1}]  was 
specified for [{0}]. Valid values are whole numbers from 8 to 15 inclusive.
 perMessageDeflate.next.ise=The next transformation of type [{0}] returned 
unexpected result
diff --git a/java/org/apache/tomcat/websocket/PerMessageDeflate.java 
b/java/org/apache/tomcat/websocket/PerMessageDeflate.java
index 1c74e1919a..1d71f6860d 100644
--- a/java/org/apache/tomcat/websocket/PerMessageDeflate.java
+++ b/java/org/apache/tomcat/websocket/PerMessageDeflate.java
@@ -122,6 +122,7 @@ public class PerMessageDeflate implements Transformation {
             try {
                 for (Parameter param : preference) {
                     if (SERVER_NO_CONTEXT_TAKEOVER.equals(param.getName())) {
+                        checkNoValue(param);
                         if (serverContextTakeover) {
                             serverContextTakeover = false;
                         } else {
@@ -130,6 +131,7 @@ public class PerMessageDeflate implements Transformation {
                                     
sm.getString("perMessageDeflate.duplicateParameter", 
SERVER_NO_CONTEXT_TAKEOVER));
                         }
                     } else if 
(CLIENT_NO_CONTEXT_TAKEOVER.equals(param.getName())) {
+                        checkNoValue(param);
                         if (clientContextTakeover) {
                             clientContextTakeover = false;
                         } else {
@@ -139,7 +141,7 @@ public class PerMessageDeflate implements Transformation {
                         }
                     } else if (SERVER_MAX_WINDOW_BITS.equals(param.getName())) 
{
                         if (serverMaxWindowBits == -1) {
-                            serverMaxWindowBits = 
Integer.parseInt(param.getValue());
+                            serverMaxWindowBits = parseWindowBits(param);
                             if (serverMaxWindowBits < 8 || serverMaxWindowBits 
> 15) {
                                 throw new 
IllegalArgumentException(sm.getString("perMessageDeflate.invalidWindowSize",
                                         SERVER_MAX_WINDOW_BITS, 
Integer.valueOf(serverMaxWindowBits)));
@@ -169,7 +171,7 @@ public class PerMessageDeflate implements Transformation {
                                 // effectively hard-coded to 15
                                 clientMaxWindowBits = 15;
                             } else {
-                                clientMaxWindowBits = 
Integer.parseInt(param.getValue());
+                                clientMaxWindowBits = parseWindowBits(param);
                                 if (clientMaxWindowBits < 8 || 
clientMaxWindowBits > 15) {
                                     throw new IllegalArgumentException(
                                             
sm.getString("perMessageDeflate.invalidWindowSize", CLIENT_MAX_WINDOW_BITS,
@@ -215,6 +217,31 @@ public class PerMessageDeflate implements Transformation {
     }
 
 
+    /*
+     * RFC 7692, section 7.1.1: the no_context_takeover parameters have no 
value.
+     */
+    private static void checkNoValue(Parameter param) {
+        if (param.getValue() != null) {
+            throw new IllegalArgumentException(
+                    sm.getString("perMessageDeflate.invalidParameterValue", 
param.getName(), param.getValue()));
+        }
+    }
+
+
+    /*
+     * RFC 7692, section 7.1.2: the value is a decimal integer without leading 
zeroes (1*DIGIT).
+     */
+    private static int parseWindowBits(Parameter param) {
+        String value = param.getValue();
+        if (value == null || value.isEmpty() || value.charAt(0) == '0' ||
+                !value.chars().allMatch(c -> c >= '0' && c <= '9')) {
+            throw new IllegalArgumentException(
+                    sm.getString("perMessageDeflate.invalidParameterValue", 
param.getName(), value));
+        }
+        return Integer.parseInt(value);
+    }
+
+
     private PerMessageDeflate(boolean serverContextTakeover, int 
serverMaxWindowBits, boolean clientContextTakeover,
             int clientMaxWindowBits, boolean isServer) {
         this.serverContextTakeover = serverContextTakeover;
diff --git a/test/org/apache/tomcat/websocket/TestPerMessageDeflate.java 
b/test/org/apache/tomcat/websocket/TestPerMessageDeflate.java
index 893905b760..16c440c6fe 100644
--- a/test/org/apache/tomcat/websocket/TestPerMessageDeflate.java
+++ b/test/org/apache/tomcat/websocket/TestPerMessageDeflate.java
@@ -781,6 +781,44 @@ public class TestPerMessageDeflate {
     }
 
 
+    /*
+     * RFC 7692 section 7.1: the no_context_takeover parameters have no value 
and the max_window_bits parameters have a
+     * decimal integer value without leading zeroes (1*DIGIT). Section 7.1 
requires an offer that contains a parameter
+     * with an invalid value to be declined. The values used for the window 
bits are all 15, which is accepted when it
+     * is written as specified, so the offer is only declined because of how 
the value is written.
+     */
+    @Test
+    public void testInvalidParameterValueDeclinesOffer() {
+        assertDeclined("server_no_context_takeover", "1");
+        assertDeclined("client_no_context_takeover", "1");
+        assertDeclined("server_max_window_bits", "015");
+        assertDeclined("server_max_window_bits", "+15");
+        assertDeclined("client_max_window_bits", "015");
+        assertDeclined("client_max_window_bits", "+15");
+        assertDeclined("client_max_window_bits", "");
+    }
+
+
+    @Test
+    public void testValidParameterValueAcceptsOffer() {
+        assertAccepted("server_no_context_takeover", null);
+        assertAccepted("client_no_context_takeover", null);
+        assertAccepted("server_max_window_bits", "15");
+        assertAccepted("client_max_window_bits", "15");
+        assertAccepted("client_max_window_bits", null);
+    }
+
+
+    private static void assertAccepted(String name, String value) {
+        List<Parameter> parameters = new ArrayList<>();
+        parameters.add(new WsExtensionParameter(name, value));
+        List<List<Parameter>> preferences = new ArrayList<>();
+        preferences.add(parameters);
+
+        Assert.assertNotNull(name + "=" + value, 
PerMessageDeflate.build(preferences, true));
+    }
+
+
     private static void assertDeclined(String name, String value) {
         List<Parameter> parameters = new ArrayList<>();
         parameters.add(new WsExtensionParameter(name, value));


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

Reply via email to