Andrea Cosentino created CAMEL-25660:
----------------------------------------

             Summary: camel-azure-servicebus - dead-lettering builds unbounded 
reason and description from the exception, and a failure to dead-letter is not 
caught
                 Key: CAMEL-25660
                 URL: https://issues.apache.org/jira/browse/CAMEL-25660
             Project: Camel
          Issue Type: Bug
          Components: camel-azure-servicebus
            Reporter: Andrea Cosentino


h3. Summary

With {{enableDeadLettering=true}} in PEEK_LOCK mode, 
{{ServiceBusConsumer.onFailure}} fills both dead-letter fields straight from 
the route's exception, with no length limit:

{code:java}
deadLetterOptions.setDeadLetterReason(
        String.format("%s: %s", cause.getClass().getName(), 
cause.getMessage()));
deadLetterOptions.setDeadLetterErrorDescription(Arrays.stream(cause.getStackTrace())
        .map(StackTraceElement::toString)
        .collect(Collectors.joining("\n")));
messageContext.deadLetter(deadLetterOptions);
{code}

A full Camel stack trace, with the error-handler and processor frames, is 
routinely several thousand characters. Microsoft's reference for 
{{ServiceBusReceiver.DeadLetterMessageAsync}} lists 4096 as the ceiling for the 
dead-letter reason or the error description, and exceeding it is one of the 
documented error conditions for the call. So for an ordinary route failure the 
description can be over the limit.

h3. Why it matters

The call is in a {{try}} whose only companion is a {{finally}} that decrements 
{{pendingExchanges}} - there is no {{catch}}:

{code:java}
try {
    ...
    messageContext.deadLetter(deadLetterOptions);
    ...
} finally {
    pendingExchanges.decrementAndGet();
}
{code}

So if the broker rejects the dead-letter request, the exception leaves 
{{onFailure}} and the message is *not* dead-lettered. Its lock then expires and 
Service Bus redelivers it, the route fails the same way, and the same oversized 
description is sent again - until {{maxDeliveryCount}} is reached and the 
broker dead-letters it on its own terms. The net effect is that 
{{enableDeadLettering}} does not deliver the diagnostics it is there for, and 
the exchange is processed several more times than intended.

The reason field has the same shape: {{cause.getMessage()}} is unbounded, and 
some messages are long (a database error quoting the statement, a validation 
report).

h3. Suggested fix

Truncate both fields to what Service Bus accepts before calling {{deadLetter}}, 
and wrap the call so that a broker rejection is reported through the exception 
handler instead of escaping {{onFailure}}. Keeping the head of the stack trace 
is the useful part; the tail is error-handler frames.

h3. Evidence and what is NOT verified

* Verified by reading the component on main: both fields are unbounded, and 
there is no {{catch}} around {{deadLetter}}.
* Verified that {{com.azure.messaging.servicebus.models.DeadLetterOptions}} 
(azure-messaging-servicebus 7.17.16) does no client-side length validation, so 
the broker is what rejects it.
* *Not* verified against a live namespace - this has not been reproduced, and 
the 4096 figure comes from the SDK reference rather than the Service Bus quotas 
page, which also leaves it unclear whether the limit counts characters or 
bytes. Someone with a Service Bus namespace should confirm the threshold and 
the resulting behaviour before the fix is sized.

Filed unassigned.

Found while sweeping for consumers that echo exception detail. Unlike that 
class, this is not an information-disclosure concern: the text goes to the 
operator's own dead-letter queue, not to an untrusted sender.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to