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

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


The following commit(s) were added to refs/heads/master by this push:
     new c227d787b4 [FIX] Fix possible NPE when audit trail in DeleteProcessor 
(#2732)
c227d787b4 is described below

commit c227d787b4673e4ca98accb38d67f5b49fe3825e
Author: Trần Hồng Quân <55171818+quantranhong1...@users.noreply.github.com>
AuthorDate: Mon Jun 2 21:41:08 2025 +0700

    [FIX] Fix possible NPE when audit trail in DeleteProcessor (#2732)
    
    
{"timestamp":"2025-05-30T07:02:53.647Z","level":"WARN","thread":"lettuce-epollEventLoop-4-4","logger":"org.apache.james.util.AuditTrail","message":"Exception
 while providing AuditTrail 
parameters","context":"default","exception":"java.lang.NullPointerException: 
Cannot invoke 
\"org.apache.james.imap.api.process.SelectedMailbox.getMailboxId()\" because 
\"selected\" is null
            at 
org.apache.james.imap.processor.DeleteProcessor.lambda$auditTrail$11(DeleteProcessor.java:117)
            at 
org.apache.james.util.AuditTrail$Entry.parameters(AuditTrail.java:101)
            at 
org.apache.james.imap.processor.DeleteProcessor.auditTrail(DeleteProcessor.java:116)
            at 
org.apache.james.imap.processor.DeleteProcessor.lambda$processRequestReactive$1(DeleteProcessor.java:75)
            at 
reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onComplete(MonoPeekTerminal.java:289)
            at 
reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89)
            at 
reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89)
            at 
reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:210)
            at 
reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:239)
            at 
reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:204)
            at 
reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89)
            at 
reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:85)
            at 
reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:246)
            at 
reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305)
            at 
reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:245)
            at 
reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305)
            at 
reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:294)
            at 
reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:188)
            at 
reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:237)
            at 
reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:204)
            at 
reactor.core.publisher.SerializedSubscriber.onComplete(SerializedSubscriber.java:146)
            at 
reactor.core.publisher.FluxRetryWhen$RetryWhenMainSubscriber.onComplete(FluxRetryWhen.java:204)
            at 
reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:250)
            at 
reactor.core.publisher.MonoFlatMap$FlatMapInner.onComplete(MonoFlatMap.java:324)
            at 
reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:210)
            at 
reactor.core.publisher.MonoUsing$MonoUsingSubscriber.onComplete(MonoUsing.java:283)
            at 
reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89)
            at 
reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260)
            at 
reactor.core.publisher.FluxConcatArray$ConcatArraySubscriber.onComplete(FluxConcatArray.java:209)
            at 
reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89)
            at 
reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onComplete(FluxHide.java:147)
            at 
reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:281)
            at 
reactor.core.publisher.MonoZip$ZipInner.onComplete(MonoZip.java:527)
            at 
reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89)
            at 
reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:850)
            at 
reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:612)
            at 
reactor.core.publisher.FluxFlatMap$FlatMapMain.innerComplete(FluxFlatMap.java:898)
            at 
reactor.core.publisher.FluxFlatMap$FlatMapInner.onComplete(FluxFlatMap.java:1001)
            at 
reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89)
            at 
reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onComplete(Operators.java:2231)
            at 
reactor.core.publisher.SerializedSubscriber.onComplete(SerializedSubscriber.java:146)
            at 
reactor.core.publisher.FluxTimeout$TimeoutMainSubscriber.onComplete(FluxTimeout.java:235)
            at 
reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:850)
            at 
reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:612)
            at 
reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:592)
            at 
reactor.core.publisher.FluxFlatMap$FlatMapMain.onComplete(FluxFlatMap.java:469)
            at 
io.lettuce.core.RedisPublisher$ImmediateSubscriber.onComplete(RedisPublisher.java:900)
            at 
io.lettuce.core.RedisPublisher$State.onAllDataRead(RedisPublisher.java:702)
            at 
io.lettuce.core.RedisPublisher$State$3.read(RedisPublisher.java:612)
            at 
io.lettuce.core.RedisPublisher$State$3.onDataAvailable(RedisPublisher.java:569)
            at 
io.lettuce.core.RedisPublisher$RedisSubscription.onDataAvailable(RedisPublisher.java:326)
            at 
io.lettuce.core.RedisPublisher$RedisSubscription.onAllDataRead(RedisPublisher.java:341)
            at 
io.lettuce.core.RedisPublisher$SubscriptionCommand.doOnComplete(RedisPublisher.java:782)
            at 
io.lettuce.core.protocol.CommandWrapper.complete(CommandWrapper.java:65)
            at 
io.lettuce.core.protocol.CommandWrapper.complete(CommandWrapper.java:63)
            at 
io.lettuce.core.protocol.CommandHandler.complete(CommandHandler.java:745)
            at 
io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:680)
            at 
io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:597)
            at 
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
            at 
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
            at 
io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
            at 
io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1407)
            at 
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
            at 
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
            at 
io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:918)
            at 
io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:799)
            at 
io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:501)
            at 
io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:399)
            at 
io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:994)
            at 
io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
            at 
io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
            at java.base/java.lang.Thread.run(Unknown Source)
    "}
    
    Because SelectedMailbox could be deselected before AuditTrail, therefore 
null SelectedMailbox could be used.
    
    I propose we audit the mailboxPath instead:
    - mailboxId could be null
    - The deleted mailbox may not be the same as the selected mailbox (id)
    - After the mailbox is deleted, logging its mailboxId may not be meaningful
    - If we insist on logging the mailboxId, it may need extra DB requests
---
 .../main/java/org/apache/james/imap/processor/DeleteProcessor.java  | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteProcessor.java
index add768ee41..acfef263e2 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteProcessor.java
@@ -72,7 +72,7 @@ public class DeleteProcessor extends 
AbstractMailboxProcessor<DeleteRequest> {
             .then(unsolicitedResponses(session, responder, false))
             .then(Mono.fromRunnable(() -> okComplete(request, responder)))
             .then()
-            .doOnSuccess(any -> auditTrail(session, selected))
+            .doOnSuccess(any -> auditTrail(session, mailboxPath))
             .onErrorResume(MailboxNotFoundException.class, e -> {
                 no(request, responder, 
HumanReadableText.FAILURE_NO_SUCH_MAILBOX);
                 return ReactorUtils.logAsMono(() -> LOGGER.debug("Delete 
failed for mailbox {} as it doesn't exist", mailboxPath, e));
@@ -107,14 +107,14 @@ public class DeleteProcessor extends 
AbstractMailboxProcessor<DeleteRequest> {
             .addToContext("mailbox", request.getMailboxName());
     }
 
-    private void auditTrail(ImapSession session, SelectedMailbox selected) {
+    private void auditTrail(ImapSession session, MailboxPath mailboxPath) {
         AuditTrail.entry()
             .username(() -> session.getUserName().asString())
             .sessionId(() -> session.sessionId().asString())
             .protocol("IMAP")
             .action("DELETE")
             .parameters(() -> ImmutableMap.of("loggedInUser", 
session.getMailboxSession().getLoggedInUser().map(Username::asString).orElse(""),
-                "mailboxId", selected.getMailboxId().serialize()))
+                "mailboxPath", mailboxPath.asString()))
             .log(String.format("IMAP DELETE succeeded."));
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org
For additional commands, e-mail: notifications-h...@james.apache.org

Reply via email to