From 96f1c444c84014a314609205f79a2b49452f9590 Mon Sep 17 00:00:00 2001 From: Bruce Bujon Date: Thu, 18 Sep 2025 12:31:56 +0200 Subject: [PATCH 1/9] chore: Clean up unused UnsafeUtils --- .../trace/agent/test/utils/UnsafeUtils.java | 44 ------------------- 1 file changed, 44 deletions(-) delete mode 100644 dd-java-agent/testing/src/main/java/datadog/trace/agent/test/utils/UnsafeUtils.java diff --git a/dd-java-agent/testing/src/main/java/datadog/trace/agent/test/utils/UnsafeUtils.java b/dd-java-agent/testing/src/main/java/datadog/trace/agent/test/utils/UnsafeUtils.java deleted file mode 100644 index e029a970533..00000000000 --- a/dd-java-agent/testing/src/main/java/datadog/trace/agent/test/utils/UnsafeUtils.java +++ /dev/null @@ -1,44 +0,0 @@ -package datadog.trace.agent.test.utils; - -import static datadog.environment.JavaVirtualMachine.isJavaVersionAtLeast; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - -public class UnsafeUtils { - private UnsafeUtils() {} - - private static void setStaticBooleanFieldViaUnsafe(final Field field, final boolean newValue) - throws Exception { - final Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); - unsafeField.setAccessible(true); - final sun.misc.Unsafe unsafe = (sun.misc.Unsafe) unsafeField.get(null); - - unsafe.ensureClassInitialized(field.getDeclaringClass()); - - final Object staticFieldBase = unsafe.staticFieldBase(field); - final long staticFieldOffset = unsafe.staticFieldOffset(field); - unsafe.putBooleanVolatile(staticFieldBase, staticFieldOffset, newValue); - } - - private static void setStaticFieldViaReflection(final Field field, final Object newValue) - throws Exception { - field.setAccessible(true); - final Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - final int origModifiers = field.getModifiers(); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); - field.set(null, newValue); - modifiersField.setInt(field, origModifiers); - } - - public static void setStaticBooleanField(final Field field, final boolean newValue) - throws Exception { - if (isJavaVersionAtLeast(12)) { - // since 12 we can't use reflection: http://hg.openjdk.java.net/jdk/jdk/rev/f55a4bc91ef4 - setStaticBooleanFieldViaUnsafe(field, newValue); - } else { - setStaticFieldViaReflection(field, newValue); - } - } -} From e73688ff5af3b1876ca58476b74b6817d008eae6 Mon Sep 17 00:00:00 2001 From: Bruce Bujon Date: Thu, 18 Sep 2025 12:37:55 +0200 Subject: [PATCH 2/9] feat: Add future prohibited methods --- gradle/forbiddenApiFilters/main.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/gradle/forbiddenApiFilters/main.txt b/gradle/forbiddenApiFilters/main.txt index 4993b965ed8..5db4fee5db5 100644 --- a/gradle/forbiddenApiFilters/main.txt +++ b/gradle/forbiddenApiFilters/main.txt @@ -28,3 +28,16 @@ net.bytebuddy.matcher.ElementMatchers#isAbstract() @defaultMessage Avoid using System.out/err to prevent excess logging. To override, add @SuppressForbidden. java.lang.System#out java.lang.System#err + +# prepare for JEP 500 (https://openjdk.org/jeps/500) +@defaultMessage Prepare to Make Final Mean Final - JEP 500 +java.lang.reflect.Field#set(java.lang.Object,java.lang.Object) +java.lang.reflect.Field#setBoolean(java.lang.Object,boolean) +java.lang.reflect.Field#setByte(java.lang.Object,byte) +java.lang.reflect.Field#setChar(java.lang.Object,char) +java.lang.reflect.Field#setShort(java.lang.Object,short) +java.lang.reflect.Field#setInt(java.lang.Object,int) +java.lang.reflect.Field#setLong(java.lang.Object,long) +java.lang.reflect.Field#setFloat(java.lang.Object,float) +java.lang.reflect.Field#setDouble(java.lang.Object,double) +java.lang.invoke.MethodHandles.Lookup#unreflectSetter(java.lang.reflect.Field) From 5ad4f5ee26f6d82cc8250a135d3354b4b1dc4674 Mon Sep 17 00:00:00 2001 From: Bruce Bujon Date: Thu, 18 Sep 2025 14:13:02 +0200 Subject: [PATCH 3/9] feat: Allow current usages --- .../akkahttp/appsec/Bug4304Instrumentation.java | 8 +++++++- .../instrumentation/weaver/WeaverInstrumentation.java | 3 +++ .../datadog/trace/api/iast/InstrumentationBridge.java | 3 +++ .../src/main/java/datadog/trace/util/UnsafeUtils.java | 3 +++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java index b185117f882..2dadc6a71ec 100644 --- a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java +++ b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java @@ -16,13 +16,17 @@ import datadog.trace.api.gateway.RequestContext; import datadog.trace.api.gateway.RequestContextSlot; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import de.thetaphi.forbiddenapis.SuppressForbidden; import java.lang.reflect.Field; import java.util.regex.Pattern; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; -/** See https://github.com/akka/akka-http/issues/4304 */ +/** + * See Duplicated 100 responses if there is + * an exception thrown by the unmarshaller + */ @AutoService(InstrumenterModule.class) public class Bug4304Instrumentation extends InstrumenterModule.AppSec implements Instrumenter.ForTypeHierarchy, @@ -91,6 +95,8 @@ public void methodAdvice(MethodTransformer transformer) { } static class GraphStageLogicAdvice { + // Using Field.set() will be blocked in later Java versions + @SuppressForbidden @Advice.OnMethodExit(suppress = Throwable.class) static void after(@Advice.This GraphStageLogic thiz) throws NoSuchFieldException, IllegalAccessException { diff --git a/dd-java-agent/instrumentation/weaver/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java b/dd-java-agent/instrumentation/weaver/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java index f28a45f03c5..eca7c4b2dd4 100644 --- a/dd-java-agent/instrumentation/weaver/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java +++ b/dd-java-agent/instrumentation/weaver/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java @@ -5,6 +5,7 @@ import com.google.auto.service.AutoService; import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.InstrumenterModule; +import de.thetaphi.forbiddenapis.SuppressForbidden; import java.lang.reflect.Field; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -42,6 +43,8 @@ public void methodAdvice(MethodTransformer transformer) { } public static class SbtTaskCreationAdvice { + // Using Field.set() will be blocked in later Java versions + @SuppressForbidden @Advice.OnMethodExit(suppress = Throwable.class) public static void onTaskCreation( @Advice.This Object sbtTask, @Advice.FieldValue("taskDef") TaskDef taskDef) { diff --git a/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java b/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java index 7c5c9d1ae3f..84cf554ff17 100644 --- a/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java +++ b/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java @@ -29,6 +29,7 @@ import datadog.trace.api.iast.sink.XContentTypeModule; import datadog.trace.api.iast.sink.XPathInjectionModule; import datadog.trace.api.iast.sink.XssModule; +import de.thetaphi.forbiddenapis.SuppressForbidden; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -124,6 +125,8 @@ private static M get(final Field field) { } } + // Using Field.set() will be blocked in later Java versions + @SuppressForbidden private static void set(final Field field, final IastModule module) { try { field.set(null, module); diff --git a/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java b/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java index cc0830b68c8..c062bb8fb7c 100644 --- a/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java +++ b/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java @@ -1,5 +1,6 @@ package datadog.trace.util; +import de.thetaphi.forbiddenapis.SuppressForbidden; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import org.slf4j.Logger; @@ -57,6 +58,8 @@ public static T tryShallowClone(T original) { } } + // Using Field.set() will be blocked in later Java versions + @SuppressForbidden private static void cloneFields(Class clazz, Object original, Object clone) throws Exception { for (Field field : clazz.getDeclaredFields()) { if ((field.getModifiers() & Modifier.STATIC) != 0) { From b698f4ffe95238ad1410d0398c07bd14522d497b Mon Sep 17 00:00:00 2001 From: Sarah Chen Date: Wed, 21 Jan 2026 13:21:49 -0500 Subject: [PATCH 4/9] Update forbiddenAPIs comments --- gradle/forbiddenApiFilters/main.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle/forbiddenApiFilters/main.txt b/gradle/forbiddenApiFilters/main.txt index 85532e84583..334e0d79822 100644 --- a/gradle/forbiddenApiFilters/main.txt +++ b/gradle/forbiddenApiFilters/main.txt @@ -1,7 +1,7 @@ # loads and instantiates a class which may be inefficient depending on context java.lang.Class#forName(java.lang.String) -# String methods which uses regexes for matching +# String methods which use regexes for matching java.lang.String#split(java.lang.String) java.lang.String#split(java.lang.String,int) java.lang.String#replaceAll(java.lang.String,java.lang.String) @@ -34,12 +34,12 @@ java.lang.System#err java.lang.System#getenv() java.lang.System#getenv(java.lang.String) -#Use jdk LongAdder +# use jdk LongAdder @defaultMessage use LongAdder instead of the legacy jctools FixedSizeStripedLongCounter org.jctools.counters.FixedSizeStripedLongCounter -# prepare for JEP 500 (https://openjdk.org/jeps/500) -@defaultMessage Prepare to Make Final Mean Final - JEP 500 +# avoid methods that mutate final fields, as this will soon be disallowed. For more details: https://openjdk.org/jeps/500. +@defaultMessage Avoid mutating final fields (e.g. with methods such as Field::set and MethodHandles.Lookup::unreflectSetter). If the field is not final, override with @SuppressForbidden. java.lang.reflect.Field#set(java.lang.Object,java.lang.Object) java.lang.reflect.Field#setBoolean(java.lang.Object,boolean) java.lang.reflect.Field#setByte(java.lang.Object,byte) From 968946d2573c11fd729386d4d97cdca10d08c388 Mon Sep 17 00:00:00 2001 From: Sarah Chen Date: Wed, 21 Jan 2026 16:03:04 -0500 Subject: [PATCH 5/9] Add comments where fields may be mutated --- .../java/com/datadog/debugger/agent/CapturedSnapshotTest.java | 1 + .../instrumentation/akkahttp/appsec/Bug4304Instrumentation.java | 2 +- .../src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy | 1 + .../src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy | 1 + .../java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy | 1 + .../trace/instrumentation/weaver/WeaverInstrumentation.java | 2 +- .../smoketest/appsec/springboot/SpringbootApplication.java | 1 + .../main/java/datadog/trace/api/iast/InstrumentationBridge.java | 2 +- .../src/main/java/datadog/trace/util/MethodHandles.java | 1 + internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java | 2 +- 10 files changed, 10 insertions(+), 4 deletions(-) diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java index e291ba8d8aa..57741a9583a 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java @@ -2845,6 +2845,7 @@ private TestSnapshotListener setupInstrumentTheWorldTransformer( return listener; } + // TODO: determine whether or not the field is final private void setCorrelationSingleton(Object instance) { Class singletonClass = CorrelationAccess.class.getDeclaredClasses()[0]; try { diff --git a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java index 2dadc6a71ec..a6b3a893aec 100644 --- a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java +++ b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java @@ -95,7 +95,7 @@ public void methodAdvice(MethodTransformer transformer) { } static class GraphStageLogicAdvice { - // Using Field.set() will be blocked in later Java versions + // TODO: determine whether or not the field is final @SuppressForbidden @Advice.OnMethodExit(suppress = Throwable.class) static void after(@Advice.This GraphStageLogic thiz) diff --git a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy index c9abe1cd3fa..c7057ff6214 100644 --- a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy @@ -32,6 +32,7 @@ class GrizzlyByteBodyInstrumentationTest extends InstrumentationSpecification { def supplier boolean bodyDone + // TODO: determine whether or not the field is final def prepare(String encoding = null) { _ * mockHttpHeader.attributes >> attributeHolder if (encoding) { diff --git a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy index 8ab0d014c35..64f452381f4 100644 --- a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy @@ -29,6 +29,7 @@ class GrizzlyCharBodyInstrumentationTest extends InstrumentationSpecification { def supplier boolean bodyDone + // TODO: determine whether or not the field is final def setup() { _ * mockHttpHeader.attributes >> attributeHolder 1 * attributeHolder.setAttribute('datadog.intercepted_request_body', Boolean.TRUE) diff --git a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy index 7a652ad5927..7c082a2b068 100644 --- a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy +++ b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy @@ -31,6 +31,7 @@ class SslSocketTest extends InstrumentationSpecification { injectSysConfig("dd.usm.enabled", "true") } + // TODO: determine whether or not the fields are final def "simple HTTPS request"() { setup: HttpsURLConnection.setDefaultSSLSocketFactory(server.sslContext.getSocketFactory()) diff --git a/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java b/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java index eca7c4b2dd4..71a6d343eb2 100644 --- a/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java +++ b/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java @@ -43,7 +43,7 @@ public void methodAdvice(MethodTransformer transformer) { } public static class SbtTaskCreationAdvice { - // Using Field.set() will be blocked in later Java versions + // TODO: determine whether or not the field is final @SuppressForbidden @Advice.OnMethodExit(suppress = Throwable.class) public static void onTaskCreation( diff --git a/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java b/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java index 7c115bd857a..b2f8872189f 100644 --- a/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java +++ b/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java @@ -19,6 +19,7 @@ public static void main(final String[] args) { System.out.println("Started in " + ManagementFactory.getRuntimeMXBean().getUptime() + "ms"); } + // TODO: determine whether or not the field is final private static void activateAppSec() throws Exception { Class agentClass = ClassLoader.getSystemClassLoader().loadClass("datadog.trace.bootstrap.Agent"); diff --git a/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java b/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java index 84cf554ff17..3837bd8e009 100644 --- a/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java +++ b/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java @@ -125,7 +125,7 @@ private static M get(final Field field) { } } - // Using Field.set() will be blocked in later Java versions + // TODO: determine whether or not the field is final @SuppressForbidden private static void set(final Field field, final IastModule module) { try { diff --git a/internal-api/src/main/java/datadog/trace/util/MethodHandles.java b/internal-api/src/main/java/datadog/trace/util/MethodHandles.java index bb0f756bf6f..4d2b82d3675 100644 --- a/internal-api/src/main/java/datadog/trace/util/MethodHandles.java +++ b/internal-api/src/main/java/datadog/trace/util/MethodHandles.java @@ -65,6 +65,7 @@ public MethodHandle privateFieldSetter(String className, String fieldName) { return clazz != null ? privateFieldSetter(clazz, fieldName) : null; } + // TODO: determine whether or not the field is final public MethodHandle privateFieldSetter(Class clazz, String fieldName) { return AccessController.doPrivileged( (PrivilegedAction) diff --git a/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java b/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java index c062bb8fb7c..c6ae4ae4ba8 100644 --- a/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java +++ b/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java @@ -58,7 +58,7 @@ public static T tryShallowClone(T original) { } } - // Using Field.set() will be blocked in later Java versions + // TODO: determine whether or not the fields are final @SuppressForbidden private static void cloneFields(Class clazz, Object original, Object clone) throws Exception { for (Field field : clazz.getDeclaredFields()) { From 1860f68fb727d141bd9154525b5b8a8f853361b9 Mon Sep 17 00:00:00 2001 From: Sarah Chen Date: Thu, 22 Jan 2026 12:13:32 -0500 Subject: [PATCH 6/9] Mark which usages need changing --- .../java/com/datadog/debugger/agent/CapturedSnapshotTest.java | 2 +- .../akkahttp/appsec/Bug4304Instrumentation.java | 2 +- .../src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy | 1 - .../src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy | 1 - .../java-net-1.8/src/test/groovy/SslSocketTest.groovy | 1 - .../trace/instrumentation/weaver/WeaverInstrumentation.java | 2 +- .../smoketest/appsec/springboot/SpringbootApplication.java | 1 - .../java/datadog/trace/api/iast/InstrumentationBridge.java | 4 +++- .../src/main/java/datadog/trace/util/MethodHandles.java | 1 - .../src/main/java/datadog/trace/util/UnsafeUtils.java | 2 +- 10 files changed, 7 insertions(+), 10 deletions(-) diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java index 57741a9583a..62dff6442d9 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java @@ -2845,7 +2845,7 @@ private TestSnapshotListener setupInstrumentTheWorldTransformer( return listener; } - // TODO: determine whether or not the field is final + // TODO: JEP 500 - avoid mutating final fields private void setCorrelationSingleton(Object instance) { Class singletonClass = CorrelationAccess.class.getDeclaredClasses()[0]; try { diff --git a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java index a6b3a893aec..ab229353408 100644 --- a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java +++ b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java @@ -95,7 +95,7 @@ public void methodAdvice(MethodTransformer transformer) { } static class GraphStageLogicAdvice { - // TODO: determine whether or not the field is final + // TODO: JEP 500 - avoid mutating final fields @SuppressForbidden @Advice.OnMethodExit(suppress = Throwable.class) static void after(@Advice.This GraphStageLogic thiz) diff --git a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy index c7057ff6214..c9abe1cd3fa 100644 --- a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy @@ -32,7 +32,6 @@ class GrizzlyByteBodyInstrumentationTest extends InstrumentationSpecification { def supplier boolean bodyDone - // TODO: determine whether or not the field is final def prepare(String encoding = null) { _ * mockHttpHeader.attributes >> attributeHolder if (encoding) { diff --git a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy index 64f452381f4..8ab0d014c35 100644 --- a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy @@ -29,7 +29,6 @@ class GrizzlyCharBodyInstrumentationTest extends InstrumentationSpecification { def supplier boolean bodyDone - // TODO: determine whether or not the field is final def setup() { _ * mockHttpHeader.attributes >> attributeHolder 1 * attributeHolder.setAttribute('datadog.intercepted_request_body', Boolean.TRUE) diff --git a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy index 7c082a2b068..7a652ad5927 100644 --- a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy +++ b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy @@ -31,7 +31,6 @@ class SslSocketTest extends InstrumentationSpecification { injectSysConfig("dd.usm.enabled", "true") } - // TODO: determine whether or not the fields are final def "simple HTTPS request"() { setup: HttpsURLConnection.setDefaultSSLSocketFactory(server.sslContext.getSocketFactory()) diff --git a/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java b/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java index 71a6d343eb2..fe8e60e4fd2 100644 --- a/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java +++ b/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java @@ -43,7 +43,7 @@ public void methodAdvice(MethodTransformer transformer) { } public static class SbtTaskCreationAdvice { - // TODO: determine whether or not the field is final + // TODO: JEP 500 - avoid mutating final fields @SuppressForbidden @Advice.OnMethodExit(suppress = Throwable.class) public static void onTaskCreation( diff --git a/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java b/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java index b2f8872189f..7c115bd857a 100644 --- a/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java +++ b/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java @@ -19,7 +19,6 @@ public static void main(final String[] args) { System.out.println("Started in " + ManagementFactory.getRuntimeMXBean().getUptime() + "ms"); } - // TODO: determine whether or not the field is final private static void activateAppSec() throws Exception { Class agentClass = ClassLoader.getSystemClassLoader().loadClass("datadog.trace.bootstrap.Agent"); diff --git a/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java b/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java index 3837bd8e009..835364fafcf 100644 --- a/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java +++ b/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java @@ -125,7 +125,9 @@ private static M get(final Field field) { } } - // TODO: determine whether or not the field is final + // Field::set() is forbidden because it may be used to mutate final fields, disallowed by + // https://openjdk.org/jeps/500. + // However, in this case the method is called on a non-final field, so it is safe. @SuppressForbidden private static void set(final Field field, final IastModule module) { try { diff --git a/internal-api/src/main/java/datadog/trace/util/MethodHandles.java b/internal-api/src/main/java/datadog/trace/util/MethodHandles.java index 4d2b82d3675..bb0f756bf6f 100644 --- a/internal-api/src/main/java/datadog/trace/util/MethodHandles.java +++ b/internal-api/src/main/java/datadog/trace/util/MethodHandles.java @@ -65,7 +65,6 @@ public MethodHandle privateFieldSetter(String className, String fieldName) { return clazz != null ? privateFieldSetter(clazz, fieldName) : null; } - // TODO: determine whether or not the field is final public MethodHandle privateFieldSetter(Class clazz, String fieldName) { return AccessController.doPrivileged( (PrivilegedAction) diff --git a/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java b/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java index c6ae4ae4ba8..06cd5f832a0 100644 --- a/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java +++ b/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java @@ -58,7 +58,7 @@ public static T tryShallowClone(T original) { } } - // TODO: determine whether or not the fields are final + // TODO: JEP 500 - avoid mutating final fields @SuppressForbidden private static void cloneFields(Class clazz, Object original, Object clone) throws Exception { for (Field field : clazz.getDeclaredFields()) { From 9aacc3cdc9081b521b6dd9e0c4fe63e38c189995 Mon Sep 17 00:00:00 2001 From: Sarah Chen Date: Thu, 22 Jan 2026 14:46:08 -0500 Subject: [PATCH 7/9] Actually mark what usages need changing --- .../java/com/datadog/debugger/agent/CapturedSnapshotTest.java | 2 +- .../akkahttp/appsec/Bug4304Instrumentation.java | 2 +- .../src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy | 1 + .../src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy | 1 + .../java-net-1.8/src/test/groovy/SslSocketTest.groovy | 1 + .../trace/instrumentation/weaver/WeaverInstrumentation.java | 2 +- .../smoketest/appsec/springboot/SpringbootApplication.java | 1 + .../java/datadog/trace/api/iast/InstrumentationBridge.java | 4 +--- .../src/main/java/datadog/trace/util/MethodHandles.java | 3 +++ .../src/main/java/datadog/trace/util/UnsafeUtils.java | 2 +- 10 files changed, 12 insertions(+), 7 deletions(-) diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java index 62dff6442d9..57741a9583a 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java @@ -2845,7 +2845,7 @@ private TestSnapshotListener setupInstrumentTheWorldTransformer( return listener; } - // TODO: JEP 500 - avoid mutating final fields + // TODO: determine whether or not the field is final private void setCorrelationSingleton(Object instance) { Class singletonClass = CorrelationAccess.class.getDeclaredClasses()[0]; try { diff --git a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java index ab229353408..a6b3a893aec 100644 --- a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java +++ b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java @@ -95,7 +95,7 @@ public void methodAdvice(MethodTransformer transformer) { } static class GraphStageLogicAdvice { - // TODO: JEP 500 - avoid mutating final fields + // TODO: determine whether or not the field is final @SuppressForbidden @Advice.OnMethodExit(suppress = Throwable.class) static void after(@Advice.This GraphStageLogic thiz) diff --git a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy index c9abe1cd3fa..c7057ff6214 100644 --- a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy @@ -32,6 +32,7 @@ class GrizzlyByteBodyInstrumentationTest extends InstrumentationSpecification { def supplier boolean bodyDone + // TODO: determine whether or not the field is final def prepare(String encoding = null) { _ * mockHttpHeader.attributes >> attributeHolder if (encoding) { diff --git a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy index 8ab0d014c35..64f452381f4 100644 --- a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy @@ -29,6 +29,7 @@ class GrizzlyCharBodyInstrumentationTest extends InstrumentationSpecification { def supplier boolean bodyDone + // TODO: determine whether or not the field is final def setup() { _ * mockHttpHeader.attributes >> attributeHolder 1 * attributeHolder.setAttribute('datadog.intercepted_request_body', Boolean.TRUE) diff --git a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy index 7a652ad5927..7c082a2b068 100644 --- a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy +++ b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy @@ -31,6 +31,7 @@ class SslSocketTest extends InstrumentationSpecification { injectSysConfig("dd.usm.enabled", "true") } + // TODO: determine whether or not the fields are final def "simple HTTPS request"() { setup: HttpsURLConnection.setDefaultSSLSocketFactory(server.sslContext.getSocketFactory()) diff --git a/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java b/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java index fe8e60e4fd2..71a6d343eb2 100644 --- a/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java +++ b/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java @@ -43,7 +43,7 @@ public void methodAdvice(MethodTransformer transformer) { } public static class SbtTaskCreationAdvice { - // TODO: JEP 500 - avoid mutating final fields + // TODO: determine whether or not the field is final @SuppressForbidden @Advice.OnMethodExit(suppress = Throwable.class) public static void onTaskCreation( diff --git a/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java b/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java index 7c115bd857a..b2f8872189f 100644 --- a/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java +++ b/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java @@ -19,6 +19,7 @@ public static void main(final String[] args) { System.out.println("Started in " + ManagementFactory.getRuntimeMXBean().getUptime() + "ms"); } + // TODO: determine whether or not the field is final private static void activateAppSec() throws Exception { Class agentClass = ClassLoader.getSystemClassLoader().loadClass("datadog.trace.bootstrap.Agent"); diff --git a/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java b/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java index 835364fafcf..3837bd8e009 100644 --- a/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java +++ b/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java @@ -125,9 +125,7 @@ private static M get(final Field field) { } } - // Field::set() is forbidden because it may be used to mutate final fields, disallowed by - // https://openjdk.org/jeps/500. - // However, in this case the method is called on a non-final field, so it is safe. + // TODO: determine whether or not the field is final @SuppressForbidden private static void set(final Field field, final IastModule module) { try { diff --git a/internal-api/src/main/java/datadog/trace/util/MethodHandles.java b/internal-api/src/main/java/datadog/trace/util/MethodHandles.java index bb0f756bf6f..70e6c61c4e2 100644 --- a/internal-api/src/main/java/datadog/trace/util/MethodHandles.java +++ b/internal-api/src/main/java/datadog/trace/util/MethodHandles.java @@ -1,6 +1,7 @@ package datadog.trace.util; import datadog.trace.api.telemetry.LogCollector; +// import de.thetaphi.forbiddenapis.SuppressForbidden; import java.lang.invoke.MethodHandle; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -65,6 +66,8 @@ public MethodHandle privateFieldSetter(String className, String fieldName) { return clazz != null ? privateFieldSetter(clazz, fieldName) : null; } + // TODO: determine whether or not the field is final + // @SuppressForbidden public MethodHandle privateFieldSetter(Class clazz, String fieldName) { return AccessController.doPrivileged( (PrivilegedAction) diff --git a/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java b/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java index 06cd5f832a0..c6ae4ae4ba8 100644 --- a/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java +++ b/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java @@ -58,7 +58,7 @@ public static T tryShallowClone(T original) { } } - // TODO: JEP 500 - avoid mutating final fields + // TODO: determine whether or not the fields are final @SuppressForbidden private static void cloneFields(Class clazz, Object original, Object clone) throws Exception { for (Field field : clazz.getDeclaredFields()) { From c997577e236fdbfa88876e9d1e1f2a36fa382ef5 Mon Sep 17 00:00:00 2001 From: Sarah Chen Date: Thu, 22 Jan 2026 14:47:44 -0500 Subject: [PATCH 8/9] Revert "Actually mark what usages need changing" This reverts commit 9aacc3cdc9081b521b6dd9e0c4fe63e38c189995. --- .../java/com/datadog/debugger/agent/CapturedSnapshotTest.java | 2 +- .../akkahttp/appsec/Bug4304Instrumentation.java | 2 +- .../src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy | 1 - .../src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy | 1 - .../java-net-1.8/src/test/groovy/SslSocketTest.groovy | 1 - .../trace/instrumentation/weaver/WeaverInstrumentation.java | 2 +- .../smoketest/appsec/springboot/SpringbootApplication.java | 1 - .../java/datadog/trace/api/iast/InstrumentationBridge.java | 4 +++- .../src/main/java/datadog/trace/util/MethodHandles.java | 3 --- .../src/main/java/datadog/trace/util/UnsafeUtils.java | 2 +- 10 files changed, 7 insertions(+), 12 deletions(-) diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java index 57741a9583a..62dff6442d9 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java @@ -2845,7 +2845,7 @@ private TestSnapshotListener setupInstrumentTheWorldTransformer( return listener; } - // TODO: determine whether or not the field is final + // TODO: JEP 500 - avoid mutating final fields private void setCorrelationSingleton(Object instance) { Class singletonClass = CorrelationAccess.class.getDeclaredClasses()[0]; try { diff --git a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java index a6b3a893aec..ab229353408 100644 --- a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java +++ b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java @@ -95,7 +95,7 @@ public void methodAdvice(MethodTransformer transformer) { } static class GraphStageLogicAdvice { - // TODO: determine whether or not the field is final + // TODO: JEP 500 - avoid mutating final fields @SuppressForbidden @Advice.OnMethodExit(suppress = Throwable.class) static void after(@Advice.This GraphStageLogic thiz) diff --git a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy index c7057ff6214..c9abe1cd3fa 100644 --- a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyByteBodyInstrumentationTest.groovy @@ -32,7 +32,6 @@ class GrizzlyByteBodyInstrumentationTest extends InstrumentationSpecification { def supplier boolean bodyDone - // TODO: determine whether or not the field is final def prepare(String encoding = null) { _ * mockHttpHeader.attributes >> attributeHolder if (encoding) { diff --git a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy index 64f452381f4..8ab0d014c35 100644 --- a/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/grizzly/grizzly-http-2.3.20/src/test/groovy/GrizzlyCharBodyInstrumentationTest.groovy @@ -29,7 +29,6 @@ class GrizzlyCharBodyInstrumentationTest extends InstrumentationSpecification { def supplier boolean bodyDone - // TODO: determine whether or not the field is final def setup() { _ * mockHttpHeader.attributes >> attributeHolder 1 * attributeHolder.setAttribute('datadog.intercepted_request_body', Boolean.TRUE) diff --git a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy index 7c082a2b068..7a652ad5927 100644 --- a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy +++ b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SslSocketTest.groovy @@ -31,7 +31,6 @@ class SslSocketTest extends InstrumentationSpecification { injectSysConfig("dd.usm.enabled", "true") } - // TODO: determine whether or not the fields are final def "simple HTTPS request"() { setup: HttpsURLConnection.setDefaultSSLSocketFactory(server.sslContext.getSocketFactory()) diff --git a/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java b/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java index 71a6d343eb2..fe8e60e4fd2 100644 --- a/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java +++ b/dd-java-agent/instrumentation/weaver-0.9/src/main/java/datadog/trace/instrumentation/weaver/WeaverInstrumentation.java @@ -43,7 +43,7 @@ public void methodAdvice(MethodTransformer transformer) { } public static class SbtTaskCreationAdvice { - // TODO: determine whether or not the field is final + // TODO: JEP 500 - avoid mutating final fields @SuppressForbidden @Advice.OnMethodExit(suppress = Throwable.class) public static void onTaskCreation( diff --git a/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java b/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java index b2f8872189f..7c115bd857a 100644 --- a/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java +++ b/dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/SpringbootApplication.java @@ -19,7 +19,6 @@ public static void main(final String[] args) { System.out.println("Started in " + ManagementFactory.getRuntimeMXBean().getUptime() + "ms"); } - // TODO: determine whether or not the field is final private static void activateAppSec() throws Exception { Class agentClass = ClassLoader.getSystemClassLoader().loadClass("datadog.trace.bootstrap.Agent"); diff --git a/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java b/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java index 3837bd8e009..835364fafcf 100644 --- a/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java +++ b/internal-api/src/main/java/datadog/trace/api/iast/InstrumentationBridge.java @@ -125,7 +125,9 @@ private static M get(final Field field) { } } - // TODO: determine whether or not the field is final + // Field::set() is forbidden because it may be used to mutate final fields, disallowed by + // https://openjdk.org/jeps/500. + // However, in this case the method is called on a non-final field, so it is safe. @SuppressForbidden private static void set(final Field field, final IastModule module) { try { diff --git a/internal-api/src/main/java/datadog/trace/util/MethodHandles.java b/internal-api/src/main/java/datadog/trace/util/MethodHandles.java index 70e6c61c4e2..bb0f756bf6f 100644 --- a/internal-api/src/main/java/datadog/trace/util/MethodHandles.java +++ b/internal-api/src/main/java/datadog/trace/util/MethodHandles.java @@ -1,7 +1,6 @@ package datadog.trace.util; import datadog.trace.api.telemetry.LogCollector; -// import de.thetaphi.forbiddenapis.SuppressForbidden; import java.lang.invoke.MethodHandle; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -66,8 +65,6 @@ public MethodHandle privateFieldSetter(String className, String fieldName) { return clazz != null ? privateFieldSetter(clazz, fieldName) : null; } - // TODO: determine whether or not the field is final - // @SuppressForbidden public MethodHandle privateFieldSetter(Class clazz, String fieldName) { return AccessController.doPrivileged( (PrivilegedAction) diff --git a/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java b/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java index c6ae4ae4ba8..06cd5f832a0 100644 --- a/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java +++ b/internal-api/src/main/java/datadog/trace/util/UnsafeUtils.java @@ -58,7 +58,7 @@ public static T tryShallowClone(T original) { } } - // TODO: determine whether or not the fields are final + // TODO: JEP 500 - avoid mutating final fields @SuppressForbidden private static void cloneFields(Class clazz, Object original, Object clone) throws Exception { for (Field field : clazz.getDeclaredFields()) { From de63e7031b758ace5d6c873e5d9498f75b49a1c9 Mon Sep 17 00:00:00 2001 From: Sarah Chen Date: Fri, 23 Jan 2026 16:30:54 -0500 Subject: [PATCH 9/9] Add comment to Bug4304Instrumentation --- .../akkahttp/appsec/Bug4304Instrumentation.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java index ab229353408..dcfcfe207cb 100644 --- a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java +++ b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/Bug4304Instrumentation.java @@ -95,7 +95,10 @@ public void methodAdvice(MethodTransformer transformer) { } static class GraphStageLogicAdvice { - // TODO: JEP 500 - avoid mutating final fields + // Field::set() is forbidden because it may be used to mutate final fields, disallowed by + // https://openjdk.org/jeps/500. + // However, in this case the method is called on a non-final field, so it is safe. See + // https://github.com/akka/akka-http/blob/8fb19fce3548c3bfa1e8ebcb1115be29f342df69/akka-http-core/src/main/scala/akka/http/impl/engine/server/HttpServerBluePrint.scala#L588 @SuppressForbidden @Advice.OnMethodExit(suppress = Throwable.class) static void after(@Advice.This GraphStageLogic thiz)