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

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

commit 78f0eaf339313607c09da654242d781a7592f18d
Author: Benoit TELLIER <[email protected]>
AuthorDate: Tue Jun 23 16:09:17 2026 +0200

    [FIX] Quote correctly username
    
    duplicated predicate (inlined/bound) exposed a flaw in the protocol layer 
resulting in null in the answer
---
 .../mailbox/postgres/mail/dao/PostgresMailboxDAO.java |  6 ++++--
 .../postgres/mail/PostgresMailboxMapperACLTest.java   | 19 +++++++++++++++++++
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git 
a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxDAO.java
 
b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxDAO.java
index 910f5e8675..2e56b6373e 100644
--- 
a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxDAO.java
+++ 
b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxDAO.java
@@ -60,6 +60,7 @@ import 
org.apache.james.mailbox.postgres.mail.PostgresACLUpsertException;
 import org.apache.james.mailbox.postgres.mail.PostgresMailbox;
 import org.apache.james.mailbox.store.MailboxExpressionBackwardCompatibility;
 import org.jooq.Condition;
+import org.jooq.Param;
 import org.jooq.Record;
 import org.jooq.impl.DSL;
 import org.jooq.impl.DefaultDataType;
@@ -181,6 +182,7 @@ public class PostgresMailboxDAO {
     }
 
     public Flux<PostgresMailbox> findMailboxesByUsername(Username userName) {
+        Param<String> userNameParam = DSL.val(userName.asString());
         return postgresExecutor.executeRows(dslContext -> 
Flux.from(dslContext.select(MAILBOX_ID,
                     MAILBOX_NAME,
                     MAILBOX_UID_VALIDITY,
@@ -191,9 +193,9 @@ public class PostgresMailboxDAO {
                     DSL.function("slice",
                         DefaultDataType.getDefaultDataType("hstore"),
                         MAILBOX_ACL,
-                        
DSL.array(DSL.val(userName.asString()))).as(MAILBOX_ACL)
+                        DSL.array(userNameParam)).as(MAILBOX_ACL)
                 ).from(TABLE_NAME)
-                .where(DSL.sql(MAILBOX_ACL.getName() + " ? '" + 
userName.asString() + "'"))), PostgresExecutor.EAGER_FETCH)       //TODO fix 
security vulnerability
+                .where(DSL.condition("defined({0}, {1})", MAILBOX_ACL, 
userNameParam))), PostgresExecutor.EAGER_FETCH)
             .map(RECORD_TO_POSTGRES_MAILBOX_FUNCTION);
     }
 
diff --git 
a/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapperACLTest.java
 
b/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapperACLTest.java
index b91bf40218..78a528dae9 100644
--- 
a/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapperACLTest.java
+++ 
b/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapperACLTest.java
@@ -19,10 +19,15 @@
 
 package org.apache.james.mailbox.postgres.mail;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 import org.apache.james.backends.postgres.PostgresExtension;
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.model.MailboxACL;
 import org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxDAO;
 import org.apache.james.mailbox.store.mail.MailboxMapper;
 import org.apache.james.mailbox.store.mail.model.MailboxMapperACLTest;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 class PostgresMailboxMapperACLTest extends MailboxMapperACLTest {
@@ -36,4 +41,18 @@ class PostgresMailboxMapperACLTest extends 
MailboxMapperACLTest {
         mailboxMapper = new PostgresMailboxMapper(new 
PostgresMailboxDAO(postgresExtension.getDefaultPostgresExecutor()));
         return mailboxMapper;
     }
+
+    @Test
+    void findNonPersonalMailboxesShouldSupportUsernamesContainingSingleQuote() 
{
+        // findMailboxesByUsername used to inline the username into the SQL 
text. A username
+        // containing a single quote then broke the query (SQL injection / 
syntax error). The
+        // username is now a bind parameter, so such usernames must be handled 
correctly.
+        Username trickyUser = Username.of("o'[email protected]");
+        MailboxACL.EntryKey key = 
MailboxACL.EntryKey.createUserEntryKey(trickyUser);
+        MailboxACL.Rfc4314Rights rights = new 
MailboxACL.Rfc4314Rights(MailboxACL.Right.Lookup);
+        mailboxMapper.updateACL(benwaInboxMailbox, 
MailboxACL.command().key(key).rights(rights).asReplacement()).block();
+
+        assertThat(mailboxMapper.findNonPersonalMailboxes(trickyUser, 
MailboxACL.Right.Lookup).collectList().block())
+            .containsOnly(benwaInboxMailbox);
+    }
 }


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

Reply via email to