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

quantranhong1999 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 17338ae4d20ebbdb1b8d46ea765777bf4eb2e63f
Author: Quan Tran <[email protected]>
AuthorDate: Thu Aug 13 13:06:48 2026 +0700

    JAMES-4210 [FIX] ManageSieve commands after STARTTLS should work
    
    Expose ManageSieve command-detection state through the Netty session 
attribute expected by the shared STARTTLS decoder. Add a real TLS handshake 
regression test proving encrypted commands remain accepted after STARTTLS.
---
 .../james/managesieveserver/netty/NettyConstants.java  |  3 ++-
 .../apache/james/managesieveserver/CapabilityTest.java | 18 ++++++++++++++++++
 .../james/managesieveserver/ManageSieveClient.java     | 17 +++++++++++++++++
 .../managesieveserver/ManageSieveServerTestSystem.java | 17 +++++++++++++++++
 .../src/test/resources/managesieveserver-starttls.xml  | 15 +++++++++++++++
 5 files changed, 69 insertions(+), 1 deletion(-)

diff --git 
a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/NettyConstants.java
 
b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/NettyConstants.java
index d966e796e8..70acfc4f8c 100644
--- 
a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/NettyConstants.java
+++ 
b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/NettyConstants.java
@@ -27,7 +27,8 @@ import io.netty.util.AttributeKey;
  * Just some constants which are used with the Netty implementation
  */
 public interface NettyConstants {
-    AttributeKey<ChannelManageSieveResponseWriter> 
RESPONSE_WRITER_ATTRIBUTE_KEY = AttributeKey.valueOf("ResponseWriter");
+    // AllButStartTlsLineBasedChannelHandler reads this shared key to disable 
command-injection detection after STARTTLS.
+    AttributeKey<ChannelManageSieveResponseWriter> 
RESPONSE_WRITER_ATTRIBUTE_KEY = AttributeKey.valueOf("session");
     AttributeKey<ProxyInformation> PROXY_INFO = 
AttributeKey.valueOf("ProxyInfo");
     AttributeKey<Session> SESSION_ATTRIBUTE_KEY = 
AttributeKey.valueOf("Session");
 }
diff --git 
a/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/CapabilityTest.java
 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/CapabilityTest.java
index 5ea4003657..1242819ee4 100644
--- 
a/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/CapabilityTest.java
+++ 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/CapabilityTest.java
@@ -124,6 +124,24 @@ class CapabilityTest {
         
assertThatThrownBy(client::readResponse).isInstanceOf(EOFException.class);
     }
 
+    @Test
+    void shouldAcceptCommandsAfterSuccessfulStartTls() throws Exception {
+        this.testSystem.setUp("managesieveserver-starttls.xml");
+
+        ManageSieveClient client = new ManageSieveClient();
+        client.connect(this.testSystem.getBindedIP(), 
this.testSystem.getBindedPort());
+        client.readResponse();
+
+        client.sendCommand("STARTTLS");
+        
assertThat(client.readResponse().responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+        client.execTLS();
+        
assertThat(client.readResponse().responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+
+        client.sendCommand("CAPABILITY");
+
+        
assertThat(client.readResponse().responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+    }
+
     private String[] getSASLMechanisms(ManageSieveClient.ServerResponse 
response) {
         String saslLine = assertThat(response.responseLines())
             .filteredOn(line -> line.startsWith("\"SASL\""))
diff --git 
a/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveClient.java
 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveClient.java
index 104a546b28..770f971cac 100644
--- 
a/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveClient.java
+++ 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveClient.java
@@ -30,9 +30,12 @@ import java.util.ArrayList;
 import java.util.Base64;
 import java.util.Optional;
 
+import javax.net.ssl.SSLSocket;
+
 import org.apache.commons.lang3.EnumUtils;
 import org.apache.commons.net.SocketClient;
 import org.apache.commons.net.io.CRLFLineReader;
+import org.apache.james.protocols.api.utils.BogusSslContextFactory;
 
 public class ManageSieveClient extends SocketClient {
     private static final String ENCODING = StandardCharsets.UTF_8.name();
@@ -57,6 +60,20 @@ public class ManageSieveClient extends SocketClient {
     @Override
     protected void _connectAction_() throws IOException {
         super._connectAction_();
+        resetStreams();
+    }
+
+    public void execTLS() throws IOException {
+        SSLSocket sslSocket = (SSLSocket) 
BogusSslContextFactory.getClientContext().getSocketFactory()
+            .createSocket(_socket_, 
_socket_.getInetAddress().getHostAddress(), _socket_.getPort(), true);
+        sslSocket.startHandshake();
+        _socket_ = sslSocket;
+        _input_ = sslSocket.getInputStream();
+        _output_ = sslSocket.getOutputStream();
+        resetStreams();
+    }
+
+    private void resetStreams() throws IOException {
         this.reader = new CRLFLineReader(new InputStreamReader(_input_, 
ENCODING));
         this.writer = new BufferedWriter(new OutputStreamWriter(_output_, 
ENCODING));
     }
diff --git 
a/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveServerTestSystem.java
 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveServerTestSystem.java
index f3b17d7456..926348ffaf 100644
--- 
a/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveServerTestSystem.java
+++ 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveServerTestSystem.java
@@ -19,7 +19,11 @@
 
 package org.apache.james.managesieveserver;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.net.InetAddress;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
 
 import org.apache.commons.configuration2.HierarchicalConfiguration;
 import org.apache.commons.configuration2.tree.ImmutableNode;
@@ -74,6 +78,9 @@ class ManageSieveServerTestSystem {
 
     private void setUp(HierarchicalConfiguration<ImmutableNode> configuration, 
ImmutableList<SaslMechanism> saslMechanisms) throws Exception {
         this.fileSystem.clear();
+        if (configuration.containsKey("tls.keystore")) {
+            prepareKeystore();
+        }
         Authenticator authenticator = (username, password) -> {
             try {
                 return usersRepository.test(username, password.toString());
@@ -95,6 +102,16 @@ class ManageSieveServerTestSystem {
         this.manageSieveServer.init();
     }
 
+    private void prepareKeystore() throws IOException {
+        Files.createDirectories(this.fileSystem.getBasedir().toPath());
+        try (InputStream keystore = 
ClassLoader.getSystemResourceAsStream("keystore")) {
+            if (keystore == null) {
+                throw new IOException("ManageSieve test keystore is missing");
+            }
+            Files.copy(keystore, 
this.fileSystem.getFile("file://keystore").toPath(), 
StandardCopyOption.REPLACE_EXISTING);
+        }
+    }
+
     public void setUp(String configFilePath) throws Exception {
         HierarchicalConfiguration<ImmutableNode> configuration = 
FileConfigurationProvider.getConfig(ClassLoader.getSystemResourceAsStream(configFilePath));
         setUp(configuration);
diff --git 
a/server/protocols/protocols-managesieve/src/test/resources/managesieveserver-starttls.xml
 
b/server/protocols/protocols-managesieve/src/test/resources/managesieveserver-starttls.xml
new file mode 100644
index 0000000000..3ff3e550ef
--- /dev/null
+++ 
b/server/protocols/protocols-managesieve/src/test/resources/managesieveserver-starttls.xml
@@ -0,0 +1,15 @@
+<managesieveserver enabled="true">
+    <jmxName>managesieveserver</jmxName>
+    <bind>0.0.0.0:4190</bind>
+
+    <tls socketTLS="false" startTLS="true">
+        <keystore>file://keystore</keystore>
+        <secret>james72laBalle</secret>
+        <provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider>
+    </tls>
+
+    <connectionBacklog>200</connectionBacklog>
+    <connectiontimeout>360</connectiontimeout>
+    <connectionLimit>0</connectionLimit>
+    <connectionLimitPerIP>0</connectionLimitPerIP>
+</managesieveserver>


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

Reply via email to