Skip to content
Draft

DRAFT IW #10055

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c542c6b
[appsec] Add Remote Config subscription for ASM_SCA product
jandro996 Dec 1, 2025
6893a5d
AppSecConfigServiceImpl integration
jandro996 Dec 1, 2025
d6fd88f
[appsec] Implement SCA hot instrumentation via ClassFileTransformer
jandro996 Dec 1, 2025
c171a65
[appsec] Integrate SCA instrumentation with agent bootstrap and wire …
jandro996 Dec 1, 2025
c05c8b3
add new smoke tests
jandro996 Dec 1, 2025
795c545
spotless
jandro996 Dec 1, 2025
4ee33f8
more smoke tests
jandro996 Dec 2, 2025
40e553b
relocate and fix smoke tests
jandro996 Dec 2, 2025
2a4922d
spotless
jandro996 Dec 2, 2025
8b3c226
get config from debug product
jandro996 Dec 2, 2025
aaeac6d
DEBUG product is going to be use for POC
jandro996 Dec 2, 2025
e8e8a93
wip
jandro996 Dec 2, 2025
80e0372
wip
jandro996 Dec 2, 2025
d0140f7
wip
jandro996 Dec 2, 2025
ab7d041
wip
jandro996 Dec 2, 2025
519d8e1
wip
jandro996 Dec 2, 2025
5553696
wip
jandro996 Dec 2, 2025
bff8d0b
fix deserializer
jandro996 Dec 2, 2025
7b90973
wip - working and sending traces with info
jandro996 Dec 3, 2025
2dbf4e6
wip - scalocation
jandro996 Dec 3, 2025
3880d14
remove location - is not accurate
jandro996 Dec 4, 2025
dee011e
AppSecSCADetector POC
jandro996 Dec 4, 2025
2eaf287
AppSecSCADetector test
jandro996 Dec 4, 2025
75ab18b
spotless
jandro996 Dec 4, 2025
da18635
AppSecSCAInstrumentationUpdater reviewed and tested
jandro996 Dec 4, 2025
867fa7d
AppSecSCATransformer reviewed and tested
jandro996 Dec 4, 2025
f993e89
improve testing and minor log issues
jandro996 Dec 4, 2025
e8a0e78
review AppSecSCAConfig and exclude it from coverage
jandro996 Dec 4, 2025
291b27d
fix and test deserializer
jandro996 Dec 4, 2025
a40ef69
comment smoke test to make them pass
jandro996 Dec 4, 2025
a405262
spotless
jandro996 Dec 4, 2025
8639057
codenarc
jandro996 Dec 5, 2025
3aea4a6
fix
jandro996 Dec 5, 2025
6e61adb
fix
jandro996 Dec 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ public void execute() {
resumeRemoteComponents();
}

maybeStartAppSec(scoClass, sco);
maybeStartAppSec(instrumentation, scoClass, sco);
maybeStartCiVisibility(instrumentation, scoClass, sco);
maybeStartLLMObs(instrumentation, scoClass, sco);
// start debugger before remote config to subscribe to it before starting to poll
Expand Down Expand Up @@ -973,7 +973,7 @@ private static void maybeStartAiGuard() {
}
}

private static void maybeStartAppSec(Class<?> scoClass, Object o) {
private static void maybeStartAppSec(Instrumentation inst, Class<?> scoClass, Object o) {

try {
// event tracking SDK must be available for customers even if AppSec is fully disabled
Expand All @@ -990,21 +990,23 @@ private static void maybeStartAppSec(Class<?> scoClass, Object o) {

try {
SubscriptionService ss = AgentTracer.get().getSubscriptionService(RequestContextSlot.APPSEC);
startAppSec(ss, scoClass, o);
startAppSec(inst, ss, scoClass, o);
} catch (Exception e) {
log.error("Error starting AppSec System", e);
}

StaticEventLogger.end("AppSec");
}

private static void startAppSec(SubscriptionService ss, Class<?> scoClass, Object sco) {
private static void startAppSec(
Instrumentation inst, SubscriptionService ss, Class<?> scoClass, Object sco) {
try {
final Class<?> appSecSysClass =
AGENT_CLASSLOADER.loadClass("com.datadog.appsec.AppSecSystem");
final Method appSecInstallerMethod =
appSecSysClass.getMethod("start", SubscriptionService.class, scoClass);
appSecInstallerMethod.invoke(null, ss, sco);
appSecSysClass.getMethod(
"start", Instrumentation.class, SubscriptionService.class, scoClass);
appSecInstallerMethod.invoke(null, inst, ss, sco);
} catch (final Throwable ex) {
log.warn("Not starting AppSec subsystem: {}", ex.getMessage());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package datadog.trace.bootstrap.instrumentation.appsec;

import datadog.trace.api.gateway.RequestContext;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import datadog.trace.util.stacktrace.StackTraceEvent;
import datadog.trace.util.stacktrace.StackTraceFrame;
import datadog.trace.util.stacktrace.StackUtils;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* SCA (Supply Chain Analysis) detection handler.
*
* <p>This class is called from instrumented bytecode when vulnerable library methods are invoked.
* It must be in the bootstrap classloader to be accessible from any instrumented class.
*
* <p>Adds vulnerability metadata to the root span for backend reporting and logs detections at
* debug level.
*/
public class AppSecSCADetector {

private static final Logger log = LoggerFactory.getLogger(AppSecSCADetector.class);

private static final String METASTRUCT_SCA = "sca";

private static final String PREFIX = "_dd.appsec.sca.";

/**
* Called when a vulnerable method is invoked.
*
* <p>This method is invoked from instrumented bytecode injected by {@code AppSecSCATransformer}.
*
* @param className The internal class name (e.g., "com/example/Foo")
* @param methodName The method name
* @param descriptor The method descriptor
* @param advisory The advisory ID (e.g., "GHSA-77xx-rxvh-q682"), may be null
* @param cve The CVE ID (e.g., "CVE-2022-41853"), may be null
*/
public static void onMethodInvocation(
String className, String methodName, String descriptor, String advisory, String cve) {
try {
// Convert internal class name to binary name for readability
String binaryClassName = className.replace('/', '.');

// Get the active span and add tags to root span
AgentSpan activeSpan = AgentTracer.activeSpan();
if (activeSpan != null) {
AgentSpan rootSpan = activeSpan.getLocalRootSpan();
if (rootSpan != null) {
// Tag the root span with SCA detection metadata
rootSpan.setTag(PREFIX + "class", binaryClassName);
rootSpan.setTag(PREFIX + "method", methodName);

if (advisory != null) {
rootSpan.setTag(PREFIX + "advisory", advisory);
}
if (cve != null) {
rootSpan.setTag(PREFIX + "cve", cve);
}

// Capture and add stack trace using IAST's system
String stackId = addSCAStackTrace(rootSpan);
if (stackId != null) {
rootSpan.setTag(PREFIX + "stack_id", stackId);
}
}
}

// Log at debug level
log.debug(
"SCA detection: {} - Vulnerable method invoked: {}#{}", cve, binaryClassName, methodName);

// TODO: Future enhancements:
// - Report Location
// - Report multiple vulnerabilities per request
// - Implement rate limiting to avoid log spam
// - Add sampling for high-frequency methods

} catch (Throwable t) {
// Never throw from instrumented callback - would break application
// Silently ignore errors
log.debug("Error in SCA detection handler", t);
}
}

/**
* Captures and adds the current stack trace to the meta struct using IAST's system.
*
* <p>Uses the same stacktrace mechanism as IAST to store frames as structured data in the meta
* struct, which will be serialized as an array in the backend.
*
* @param span the span to attach the stack trace to
* @return the stack ID if successful, null otherwise
*/
private static String addSCAStackTrace(AgentSpan span) {
try {
final RequestContext reqCtx = span.getRequestContext();
if (reqCtx == null) {
return null;
}

// Generate user code stack trace (filters out Datadog internal frames)
List<StackTraceFrame> frames = StackUtils.generateUserCodeStackTrace();
if (frames == null || frames.isEmpty()) {
return null;
}

// Create a stack trace event with a unique ID
// Use timestamp + thread ID to create a reasonably unique ID
String stackId = "sca_" + System.currentTimeMillis() + "_" + Thread.currentThread().getId();
StackTraceEvent stackTraceEvent =
new StackTraceEvent(frames, StackTraceEvent.DEFAULT_LANGUAGE, stackId, null);

// Add to meta struct using the same system as IAST
StackUtils.addStacktraceEventsToMetaStruct(
reqCtx, METASTRUCT_SCA, Collections.singletonList(stackTraceEvent));

return stackId;

} catch (Throwable t) {
// Never throw from instrumented callback - would break application
log.debug("Failed to capture SCA stack trace", t);
return null;
}
}
}
Loading
Loading