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]
