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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]