Skip to content

Commit deb6fda

Browse files
Add new features for trace tagging rules (#9131)
Signed-off-by: sezen.leblay <[email protected]>
1 parent 4644198 commit deb6fda

File tree

11 files changed

+1052
-33
lines changed

11 files changed

+1052
-33
lines changed

dd-java-agent/appsec/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ dependencies {
1414
implementation project(':internal-api')
1515
implementation project(':communication')
1616
implementation project(':telemetry')
17-
implementation group: 'io.sqreen', name: 'libsqreen', version: '15.0.1'
17+
implementation group: 'io.sqreen', name: 'libsqreen', version: '16.0.0'
1818
implementation libs.moshi
1919

2020
testImplementation libs.bytebuddy

dd-java-agent/appsec/src/main/java/com/datadog/appsec/config/AppSecConfigServiceImpl.java

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_AUTO_USER_INSTRUM_MODE;
66
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_CUSTOM_BLOCKING_RESPONSE;
77
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_CUSTOM_RULES;
8+
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_DD_MULTICONFIG;
89
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_DD_RULES;
910
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_EXCLUSIONS;
1011
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_EXCLUSION_DATA;
@@ -18,6 +19,7 @@
1819
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_RASP_SSRF;
1920
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_REQUEST_BLOCKING;
2021
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_SESSION_FINGERPRINT;
22+
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_TRACE_TAGGING_RULES;
2123
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_TRUSTED_IPS;
2224
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_USER_BLOCKING;
2325
import static datadog.remoteconfig.Capabilities.CAPABILITY_ENDPOINT_FINGERPRINT;
@@ -37,8 +39,9 @@
3739
import com.datadog.ddwaf.exception.InvalidRuleSetException;
3840
import com.datadog.ddwaf.exception.UnclassifiedWafException;
3941
import com.squareup.moshi.JsonAdapter;
42+
import com.squareup.moshi.JsonReader;
43+
import com.squareup.moshi.JsonWriter;
4044
import com.squareup.moshi.Moshi;
41-
import com.squareup.moshi.Types;
4245
import datadog.remoteconfig.ConfigurationEndListener;
4346
import datadog.remoteconfig.ConfigurationPoller;
4447
import datadog.remoteconfig.PollingRateHinter;
@@ -65,6 +68,7 @@
6568
import java.util.Set;
6669
import java.util.concurrent.ConcurrentHashMap;
6770
import java.util.concurrent.atomic.AtomicBoolean;
71+
import javax.annotation.Nullable;
6872
import okio.Okio;
6973
import org.slf4j.Logger;
7074
import org.slf4j.LoggerFactory;
@@ -92,10 +96,25 @@ public class AppSecConfigServiceImpl implements AppSecConfigService {
9296
new WAFInitializationResultReporter();
9397
private final WAFStatsReporter statsReporter = new WAFStatsReporter();
9498

95-
private static final JsonAdapter<Map<String, Object>> ADAPTER =
99+
private static final JsonAdapter<Object> ADAPTER =
96100
new Moshi.Builder()
101+
.add(
102+
Double.class,
103+
new JsonAdapter<Number>() {
104+
@Override
105+
public Number fromJson(JsonReader reader) throws IOException {
106+
double value = reader.nextDouble();
107+
long longValue = (long) value;
108+
return value % 1 == 0 ? longValue : value;
109+
}
110+
111+
@Override
112+
public void toJson(JsonWriter writer, @Nullable Number value) throws IOException {
113+
throw new UnsupportedOperationException();
114+
}
115+
})
97116
.build()
98-
.adapter(Types.newParameterizedType(Map.class, String.class, Object.class));
117+
.adapter(Object.class);
99118

100119
private boolean hasUserWafConfig;
101120
private boolean defaultConfigActivated;
@@ -104,7 +123,7 @@ public class AppSecConfigServiceImpl implements AppSecConfigService {
104123
Collections.newSetFromMap(new ConcurrentHashMap<>());
105124
private final Set<String> ignoredConfigKeys =
106125
Collections.newSetFromMap(new ConcurrentHashMap<>());
107-
private final String DEFAULT_WAF_CONFIG_RULE = "DEFAULT_WAF_CONFIG";
126+
private final String DEFAULT_WAF_CONFIG_RULE = "ASM_DD/default";
108127
private String currentRuleVersion;
109128
private List<AppSecModule> modulesToUpdateVersionIn;
110129

@@ -137,6 +156,7 @@ private void subscribeConfigurationPoller() {
137156
private long getRulesAndDataCapabilities() {
138157
long capabilities =
139158
CAPABILITY_ASM_DD_RULES
159+
| CAPABILITY_ASM_DD_MULTICONFIG
140160
| CAPABILITY_ASM_IP_BLOCKING
141161
| CAPABILITY_ASM_EXCLUSIONS
142162
| CAPABILITY_ASM_EXCLUSION_DATA
@@ -148,7 +168,8 @@ private long getRulesAndDataCapabilities() {
148168
| CAPABILITY_ENDPOINT_FINGERPRINT
149169
| CAPABILITY_ASM_SESSION_FINGERPRINT
150170
| CAPABILITY_ASM_NETWORK_FINGERPRINT
151-
| CAPABILITY_ASM_HEADER_FINGERPRINT;
171+
| CAPABILITY_ASM_HEADER_FINGERPRINT
172+
| CAPABILITY_ASM_TRACE_TAGGING_RULES;
152173
if (tracerConfig.isAppSecRaspEnabled()) {
153174
capabilities |= CAPABILITY_ASM_RASP_SQLI;
154175
capabilities |= CAPABILITY_ASM_RASP_SSRF;
@@ -210,7 +231,8 @@ public void accept(ConfigKey configKey, byte[] content, PollingRateHinter pollin
210231
}
211232
final String key = configKey.toString();
212233
Map<String, Object> contentMap =
213-
ADAPTER.fromJson(Okio.buffer(Okio.source(new ByteArrayInputStream(content))));
234+
(Map<String, Object>)
235+
ADAPTER.fromJson(Okio.buffer(Okio.source(new ByteArrayInputStream(content))));
214236
if (contentMap == null || contentMap.isEmpty()) {
215237
ignoredConfigKeys.add(key);
216238
} else {
@@ -255,7 +277,7 @@ private class AppSecConfigChangesDDListener extends AppSecConfigChangesListener
255277
@Override
256278
protected void beforeApply(final String key, final Map<String, Object> config) {
257279
if (defaultConfigActivated) { // if we get any config, remove the default one
258-
log.debug("Removing default config");
280+
log.debug("Removing default config ASM_DD/default");
259281
try {
260282
wafBuilder.removeConfig(DEFAULT_WAF_CONFIG_RULE);
261283
} catch (UnclassifiedWafException e) {
@@ -466,7 +488,8 @@ private static Map<String, Object> loadDefaultWafConfig() throws IOException {
466488
throw new IOException("Resource " + DEFAULT_CONFIG_LOCATION + " not found");
467489
}
468490

469-
Map<String, Object> ret = ADAPTER.fromJson(Okio.buffer(Okio.source(is)));
491+
Map<String, Object> ret =
492+
(Map<String, Object>) ADAPTER.fromJson(Okio.buffer(Okio.source(is)));
470493

471494
StandardizedLogging._initialConfigSourceAndLibddwafVersion(log, "<bundled config>");
472495
if (log.isInfoEnabled()) {
@@ -483,7 +506,8 @@ private static Map<String, Object> loadUserWafConfig(Config tracerConfig) throws
483506
return null;
484507
}
485508
try (InputStream is = new FileInputStream(filename)) {
486-
Map<String, Object> ret = ADAPTER.fromJson(Okio.buffer(Okio.source(is)));
509+
Map<String, Object> ret =
510+
(Map<String, Object>) ADAPTER.fromJson(Okio.buffer(Okio.source(is)));
487511

488512
StandardizedLogging._initialConfigSourceAndLibddwafVersion(log, filename);
489513
if (log.isInfoEnabled()) {
@@ -512,6 +536,7 @@ public void close() {
512536
this.configurationPoller.removeCapabilities(
513537
CAPABILITY_ASM_ACTIVATION
514538
| CAPABILITY_ASM_DD_RULES
539+
| CAPABILITY_ASM_DD_MULTICONFIG
515540
| CAPABILITY_ASM_IP_BLOCKING
516541
| CAPABILITY_ASM_EXCLUSIONS
517542
| CAPABILITY_ASM_EXCLUSION_DATA
@@ -529,7 +554,8 @@ public void close() {
529554
| CAPABILITY_ENDPOINT_FINGERPRINT
530555
| CAPABILITY_ASM_SESSION_FINGERPRINT
531556
| CAPABILITY_ASM_NETWORK_FINGERPRINT
532-
| CAPABILITY_ASM_HEADER_FINGERPRINT);
557+
| CAPABILITY_ASM_HEADER_FINGERPRINT
558+
| CAPABILITY_ASM_TRACE_TAGGING_RULES);
533559
this.configurationPoller.removeListeners(Product.ASM_DD);
534560
this.configurationPoller.removeListeners(Product.ASM_DATA);
535561
this.configurationPoller.removeListeners(Product.ASM);

dd-java-agent/appsec/src/main/java/com/datadog/appsec/ddwaf/WAFModule.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import datadog.trace.api.ProductActivation;
3535
import datadog.trace.api.ProductTraceSource;
3636
import datadog.trace.api.gateway.Flow;
37+
import datadog.trace.api.sampling.PrioritySampling;
3738
import datadog.trace.api.telemetry.LogCollector;
3839
import datadog.trace.api.telemetry.WafMetricCollector;
3940
import datadog.trace.api.time.SystemTimeSource;
@@ -402,12 +403,13 @@ public void onDataAvailable(
402403
}
403404
}
404405
Collection<AppSecEvent> events = buildEvents(resultWithData);
406+
boolean isThrottled = reqCtx.isThrottled(rateLimiter);
405407

406-
if (!events.isEmpty()) {
407-
if (!reqCtx.isThrottled(rateLimiter)) {
408+
if (resultWithData.keep) {
409+
if (!isThrottled) {
408410
AgentSpan activeSpan = AgentTracer.get().activeSpan();
409411
if (activeSpan != null) {
410-
log.debug("Setting force-keep tag on the current span");
412+
log.debug("Setting force-keep tag and manual keep tag on the current span");
411413
// Keep event related span, because it could be ignored in case of
412414
// reduced datadog sampling rate.
413415
activeSpan.getLocalRootSpan().setTag(Tags.ASM_KEEP, true);
@@ -418,18 +420,19 @@ public void onDataAvailable(
418420
.getLocalRootSpan()
419421
.setTag(Tags.PROPAGATED_TRACE_SOURCE, ProductTraceSource.ASM);
420422
} else {
421-
// If active span is not available the ASM_KEEP tag will be set in the GatewayBridge
422-
// when the request ends
423+
// If active span is not available then we need to set manual keep in GatewayBridge
423424
log.debug("There is no active span available");
424425
}
425-
reqCtx.reportEvents(events);
426426
} else {
427427
log.debug("Rate limited WAF events");
428428
if (!gwCtx.isRasp) {
429429
reqCtx.setWafRateLimited();
430430
}
431431
}
432432
}
433+
if (resultWithData.events && !events.isEmpty() && !isThrottled) {
434+
reqCtx.reportEvents(events);
435+
}
433436

434437
if (flow.isBlocking()) {
435438
if (!gwCtx.isRasp) {
@@ -438,8 +441,11 @@ public void onDataAvailable(
438441
}
439442
}
440443

441-
if (resultWithData.derivatives != null) {
442-
reqCtx.reportDerivatives(resultWithData.derivatives);
444+
reqCtx.setKeepType(
445+
resultWithData.keep ? PrioritySampling.USER_KEEP : PrioritySampling.USER_DROP);
446+
447+
if (resultWithData.attributes != null && !resultWithData.attributes.isEmpty()) {
448+
reqCtx.reportDerivatives(resultWithData.attributes);
443449
}
444450
}
445451

@@ -564,6 +570,10 @@ private Collection<AppSecEvent> buildEvents(Waf.ResultWithData actionWithData) {
564570
}
565571
Collection<WAFResultData> listResults;
566572
try {
573+
if (actionWithData.data == null || actionWithData.data.isEmpty()) {
574+
log.debug("WAF returned no data");
575+
return emptyList();
576+
}
567577
listResults = RES_JSON_ADAPTER.fromJson(actionWithData.data);
568578
} catch (IOException e) {
569579
throw new UndeclaredThrowableException(e);

dd-java-agent/appsec/src/main/java/com/datadog/appsec/event/data/ObjectIntrospection.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.lang.reflect.Modifier;
1717
import java.util.ArrayList;
1818
import java.util.Collection;
19+
import java.util.Date;
1920
import java.util.HashMap;
2021
import java.util.Iterator;
2122
import java.util.List;
@@ -184,6 +185,11 @@ private static Object doConversion(Object obj, int depth, State state) {
184185
return obj.toString();
185186
}
186187

188+
// Date objects - avoid accessing private fastTime field
189+
if (obj instanceof Date) {
190+
return ((Date) obj).getTime();
191+
}
192+
187193
// Jackson databind nodes (via reflection)
188194
Class<?> clazz = obj.getClass();
189195
if (clazz.getName().startsWith("com.fasterxml.jackson.databind.node.")) {

0 commit comments

Comments
 (0)