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 415b505bb0ea1bd7a3e44257d6dda48cd9991e4f
Author: opencode <[email protected]>
AuthorDate: Fri Oct 16 23:37:44 2026 +0200

    Reject the permessage-deflate compressed bit on continuation frames
    
    RFC 7692 section 6.1: "An endpoint MUST NOT set the 'Per-Message
    Compressed' bit of control frames and non-first fragments of a data
    message. An endpoint receiving such a frame MUST Fail the WebSocket
    Connection."
    
    PerMessageDeflate.validateRsv() only enforced this for control frames.
    For any other opcode it stripped RSV1 and delegated to the next
    transformation, so a continuation frame carrying the bit was silently
    accepted instead of failing the connection. Route continuation frames
    (opcode 0x00, non-first fragments by definition) through the same
    reject path so WsFrameBase closes the connection with PROTOCOL_ERROR,
    as the RFC requires.
    
    Tomcat's own sender sets RSV1 only on the first compressed frame of a
    message (firstCompressedFrameWritten), so this cannot break compressed
    fragmented messages between Tomcat endpoints.
    
    Add a regression test to TestPerMessageDeflate covering RSV1 on text,
    binary, continuation and control frames, plus RSV clear on
    continuations.
---
 .../apache/tomcat/websocket/PerMessageDeflate.java |  5 +++-
 .../tomcat/websocket/TestPerMessageDeflate.java    | 35 ++++++++++++++++++++++
 2 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/java/org/apache/tomcat/websocket/PerMessageDeflate.java 
b/java/org/apache/tomcat/websocket/PerMessageDeflate.java
index c879cbe167..9b45d20cbf 100644
--- a/java/org/apache/tomcat/websocket/PerMessageDeflate.java
+++ b/java/org/apache/tomcat/websocket/PerMessageDeflate.java
@@ -461,7 +461,10 @@ public class PerMessageDeflate implements Transformation {
 
     @Override
     public boolean validateRsv(int rsv, byte opCode) {
-        if (Util.isControl(opCode)) {
+        if (Util.isControl(opCode) || Util.isContinuation(opCode)) {
+            // RFC 7692 section 6.1: An endpoint MUST NOT set the "Per-Message 
Compressed" bit of
+            // control frames and non-first fragments of a data message. An 
endpoint receiving such
+            // a frame MUST Fail the WebSocket Connection.
             if ((rsv & RSV_BITMASK) != 0) {
                 return false;
             } else {
diff --git a/test/org/apache/tomcat/websocket/TestPerMessageDeflate.java 
b/test/org/apache/tomcat/websocket/TestPerMessageDeflate.java
index 7edecd7fce..69c7927a79 100644
--- a/test/org/apache/tomcat/websocket/TestPerMessageDeflate.java
+++ b/test/org/apache/tomcat/websocket/TestPerMessageDeflate.java
@@ -799,6 +799,41 @@ public class TestPerMessageDeflate {
         return sb.toString();
     }
 
+    /*
+     * RFC 7692 section 6.1: "An endpoint MUST NOT set the 'Per-Message 
Compressed' bit of control frames and non-first
+     * fragments of a data message. An endpoint receiving such a frame MUST 
Fail the WebSocket Connection." The RSV1 bit
+     * is only meaningful on the first frame of a compressed data message. 
Continuation frames (non-first fragments) and
+     * control frames must not carry it.
+     */
+    @Test
+    public void testRsvBitMustNotBeSetOnContinuationFrames() {
+        List<List<Parameter>> preferences = new ArrayList<>();
+        preferences.add(Collections.emptyList());
+
+        // Standalone instance (no next transformation), as the terminal 
transformation would accept rsv==0.
+        PerMessageDeflate perMessageDeflate = 
PerMessageDeflate.build(preferences, true);
+
+        // First frame of a compressed data message: RSV1 set is valid.
+        Assert.assertTrue("RSV1 set on a text frame should be accepted",
+                perMessageDeflate.validateRsv(0b100, Constants.OPCODE_TEXT));
+        Assert.assertTrue("RSV1 set on a binary frame should be accepted",
+                perMessageDeflate.validateRsv(0b100, Constants.OPCODE_BINARY));
+
+        // Continuation frame (non-first fragment) with RSV1 set: must fail 
the connection.
+        Assert.assertFalse("RSV1 set on a continuation frame should be 
rejected",
+                perMessageDeflate.validateRsv(0b100, 
Constants.OPCODE_CONTINUATION));
+
+        // Control frame with RSV1 set: must fail the connection (already the 
case).
+        Assert.assertFalse("RSV1 set on a control frame should be rejected",
+                perMessageDeflate.validateRsv(0b100, Constants.OPCODE_PING));
+
+        // Continuation frame without RSV1: valid (this is the normal case for 
a
+        // fragmented compressed message).
+        Assert.assertTrue("RSV clear on a continuation frame should be 
accepted",
+                perMessageDeflate.validateRsv(0, 
Constants.OPCODE_CONTINUATION));
+    }
+
+
     /*
      * Minimal implementation to enable other transformations to be tested. It 
is NOT robust.
      */


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

Reply via email to