From 1fea204a588eb2045ef4e845301d60c5210ab591 Mon Sep 17 00:00:00 2001 From: anjeongkyun Date: Fri, 11 Sep 2026 15:33:23 +0900 Subject: [PATCH] Fix NullPointerException when publishing an SNS batch without a topic ARN --- .../instrumentation/aws/v1/sns/SnsInterceptor.java | 3 +++ .../src/test/groovy/SnsClientTest.groovy | 12 ++++++++++++ .../instrumentation/aws/v2/sns/SnsInterceptor.java | 3 +++ .../src/test/groovy/SnsClientTest.groovy | 12 ++++++++++++ 4 files changed, 30 insertions(+) diff --git a/dd-java-agent/instrumentation/aws-java/aws-java-sns-1.0/src/main/java/datadog/trace/instrumentation/aws/v1/sns/SnsInterceptor.java b/dd-java-agent/instrumentation/aws-java/aws-java-sns-1.0/src/main/java/datadog/trace/instrumentation/aws/v1/sns/SnsInterceptor.java index 25158ae8255..d02512fb61e 100644 --- a/dd-java-agent/instrumentation/aws-java/aws-java-sns-1.0/src/main/java/datadog/trace/instrumentation/aws/v1/sns/SnsInterceptor.java +++ b/dd-java-agent/instrumentation/aws-java/aws-java-sns-1.0/src/main/java/datadog/trace/instrumentation/aws/v1/sns/SnsInterceptor.java @@ -90,6 +90,9 @@ public AmazonWebServiceRequest beforeMarshalling(AmazonWebServiceRequest request PublishBatchRequest pmbRequest = (PublishBatchRequest) request; // Extract the topic name from the ARN for DSM String topicName = pmbRequest.getTopicArn(); + if (null == topicName) { + return request; // no topic to attribute the batch to, leave it untouched + } topicName = topicName.substring(topicName.lastIndexOf(':') + 1); final ByteBuffer bytebuffer = this.getMessageAttributeValueToInject(request, topicName); diff --git a/dd-java-agent/instrumentation/aws-java/aws-java-sns-1.0/src/test/groovy/SnsClientTest.groovy b/dd-java-agent/instrumentation/aws-java/aws-java-sns-1.0/src/test/groovy/SnsClientTest.groovy index 4aa1e6e2ddc..55df2ee754e 100644 --- a/dd-java-agent/instrumentation/aws-java/aws-java-sns-1.0/src/test/groovy/SnsClientTest.groovy +++ b/dd-java-agent/instrumentation/aws-java/aws-java-sns-1.0/src/test/groovy/SnsClientTest.groovy @@ -1,9 +1,12 @@ +import com.amazonaws.AmazonClientException import com.amazonaws.auth.AWSStaticCredentialsProvider import com.amazonaws.auth.BasicAWSCredentials import com.amazonaws.client.builder.AwsClientBuilder import com.amazonaws.services.sns.AmazonSNSClient import com.amazonaws.services.sns.AmazonSNSClientBuilder import com.amazonaws.services.sns.model.MessageAttributeValue +import com.amazonaws.services.sns.model.PublishBatchRequest +import com.amazonaws.services.sns.model.PublishBatchRequestEntry import com.amazonaws.services.sns.model.PublishRequest import datadog.trace.agent.test.naming.VersionedNamingTestBase import datadog.trace.agent.test.utils.TraceUtils @@ -235,6 +238,15 @@ abstract class SnsClientTest extends VersionedNamingTestBase { then: noExceptionThrown() } + + def "SNS batch without topic ARN doesn't leak exception"() { + when: + snsClient.publishBatch(new PublishBatchRequest() + .withPublishBatchRequestEntries(new PublishBatchRequestEntry().withId("1").withMessage('sometext'))) + + then: + thrown(AmazonClientException) + } } class SnsClientV0Test extends SnsClientTest { diff --git a/dd-java-agent/instrumentation/aws-java/aws-java-sns-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/sns/SnsInterceptor.java b/dd-java-agent/instrumentation/aws-java/aws-java-sns-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/sns/SnsInterceptor.java index 6f84107b7dd..97cf2c32420 100644 --- a/dd-java-agent/instrumentation/aws-java/aws-java-sns-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/sns/SnsInterceptor.java +++ b/dd-java-agent/instrumentation/aws-java/aws-java-sns-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/sns/SnsInterceptor.java @@ -81,6 +81,9 @@ public SdkRequest modifyRequest(ModifyRequest context, ExecutionAttributes execu PublishBatchRequest request = (PublishBatchRequest) context.request(); // Get topic name for DSM String snsTopicArn = request.topicArn(); + if (null == snsTopicArn) { + return request; // no topic to attribute the batch to, leave it untouched + } String snsTopicName = snsTopicArn.substring(snsTopicArn.lastIndexOf(':') + 1); ArrayList entries = new ArrayList<>(); SdkBytes value = this.getMessageAttributeValueToInject(executionAttributes, snsTopicName); diff --git a/dd-java-agent/instrumentation/aws-java/aws-java-sns-2.0/src/test/groovy/SnsClientTest.groovy b/dd-java-agent/instrumentation/aws-java/aws-java-sns-2.0/src/test/groovy/SnsClientTest.groovy index d340515d7a1..859bf20d0b9 100644 --- a/dd-java-agent/instrumentation/aws-java/aws-java-sns-2.0/src/test/groovy/SnsClientTest.groovy +++ b/dd-java-agent/instrumentation/aws-java/aws-java-sns-2.0/src/test/groovy/SnsClientTest.groovy @@ -13,6 +13,8 @@ import software.amazon.awssdk.auth.credentials.AwsBasicCredentials import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider import software.amazon.awssdk.regions.Region import software.amazon.awssdk.services.sns.SnsClient +import software.amazon.awssdk.core.exception.SdkException +import software.amazon.awssdk.services.sns.model.PublishBatchRequestEntry import software.amazon.awssdk.services.sns.model.PublishResponse import software.amazon.awssdk.services.sqs.SqsClient import software.amazon.awssdk.services.sqs.model.QueueAttributeName @@ -184,6 +186,16 @@ abstract class SnsClientTest extends VersionedNamingTestBase { noExceptionThrown() } + def "SNS batch without topic ARN doesn't leak exception"() { + when: + snsClient.publishBatch { + it.publishBatchRequestEntries(PublishBatchRequestEntry.builder().id("1").message("sometext").build()) + } + + then: + thrown(SdkException) + } + def "test propagation styles"() { when: TEST_WRITER.clear()