From b15b7af0f3f812fb2fdee7fce36d5e7f889c9be1 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Wed, 5 Nov 2025 17:54:15 +0100 Subject: [PATCH] FIX --- .../appsec/gateway/AppSecRequestContext.java | 12 ++++-- .../AppSecRequestContextSpecification.groovy | 40 +++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/AppSecRequestContext.java b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/AppSecRequestContext.java index 1bf6dfab1c3..b1a7988e42f 100644 --- a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/AppSecRequestContext.java +++ b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/AppSecRequestContext.java @@ -646,9 +646,15 @@ public void close() { closeWafContext(); } collectedCookies = null; - requestHeaders.clear(); - responseHeaders.clear(); - persistentData.clear(); + if (requestHeaders != null) { + requestHeaders.clear(); + } + if (responseHeaders != null) { + responseHeaders.clear(); + } + if (persistentData != null) { + persistentData.clear(); + } if (derivatives != null) { derivatives.clear(); derivatives = null; diff --git a/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/gateway/AppSecRequestContextSpecification.groovy b/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/gateway/AppSecRequestContextSpecification.groovy index 0d249961c5b..1db808f3ee5 100644 --- a/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/gateway/AppSecRequestContextSpecification.groovy +++ b/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/gateway/AppSecRequestContextSpecification.groovy @@ -451,4 +451,44 @@ class AppSecRequestContextSpecification extends DDSpecification { assert context.isHttpClientRequestSampled(requestId) == sampled } } + + void 'close is idempotent and can be called multiple times'() { + given: + def ctx = new AppSecRequestContext() + ctx.addRequestHeader('test', 'value') + ctx.addResponseHeader('content-type', 'text/plain') + + when: + ctx.close() + ctx.close() + ctx.close() + + then: + notThrown(Exception) + } + + void 'close handles null internal fields gracefully'() { + given: + def ctx = new AppSecRequestContext() + + // Use reflection to simulate the scenario where fields might be null + // (e.g., due to bytecode instrumentation or unusual initialization) + def requestHeadersField = AppSecRequestContext.getDeclaredField('requestHeaders') + requestHeadersField.setAccessible(true) + requestHeadersField.set(ctx, null) + + def responseHeadersField = AppSecRequestContext.getDeclaredField('responseHeaders') + responseHeadersField.setAccessible(true) + responseHeadersField.set(ctx, null) + + def persistentDataField = AppSecRequestContext.getDeclaredField('persistentData') + persistentDataField.setAccessible(true) + persistentDataField.set(ctx, null) + + when: + ctx.close() + + then: + notThrown(Exception) + } }