Skip to content

Commit 2b6bf3d

Browse files
committed
Fix appsec.rasp.error and appsec.waf.error telemetry metrics (#8624)
What Does This Do Remove counters from AppsecRequestContext as they are not used in the metrics Fix appsec.waf.error metric tags as they didn't match the RFC Fix that appsec.waf.error was created in the same loop using the rasp counter instead of using the waf counter Increment metrics if UnclassifiedPowerwafException is thrown Replace ConcurrentHashMap with AtomicLongArray for raspErrorCodeCounter to improve performance and memory efficiency Add test to cover WafModule implementation
1 parent b9cd4c4 commit 2b6bf3d

File tree

6 files changed

+233
-290
lines changed

6 files changed

+233
-290
lines changed

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

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import datadog.trace.api.gateway.Flow;
4141
import datadog.trace.api.telemetry.LogCollector;
4242
import datadog.trace.api.telemetry.WafMetricCollector;
43-
import datadog.trace.api.telemetry.WafTruncatedType;
4443
import datadog.trace.api.time.SystemTimeSource;
4544
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
4645
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
@@ -440,20 +439,17 @@ public void onDataAvailable(
440439
WafMetricCollector.get().raspTimeout(gwCtx.raspRuleType);
441440
} else {
442441
reqCtx.increaseWafTimeouts();
443-
WafMetricCollector.get().wafRequestTimeout();
444442
log.debug(LogCollector.EXCLUDE_TELEMETRY, "Timeout calling the WAF", tpe);
445443
}
446444
return;
447445
} catch (UnclassifiedWafException e) {
448446
if (!reqCtx.isWafContextClosed()) {
449447
log.error("Error calling WAF", e);
450448
}
451-
// TODO this is wrong and will be fixed in another PR
452-
WafMetricCollector.get().wafRequestError();
453-
incrementErrorCodeMetric(gwCtx, e.code);
449+
incrementErrorCodeMetric(reqCtx, gwCtx, e.code);
454450
return;
455451
} catch (AbstractWafException e) {
456-
incrementErrorCodeMetric(gwCtx, e.code);
452+
incrementErrorCodeMetric(reqCtx, gwCtx, e.code);
457453
return;
458454
} finally {
459455
if (log.isDebugEnabled()) {
@@ -467,17 +463,8 @@ public void onDataAvailable(
467463
final long listMapTooLarge = wafMetrics.getTruncatedListMapTooLargeCount();
468464
final long objectTooDeep = wafMetrics.getTruncatedObjectTooDeepCount();
469465

470-
if (stringTooLong > 0) {
471-
WafMetricCollector.get()
472-
.wafInputTruncated(WafTruncatedType.STRING_TOO_LONG, stringTooLong);
473-
}
474-
if (listMapTooLarge > 0) {
475-
WafMetricCollector.get()
476-
.wafInputTruncated(WafTruncatedType.LIST_MAP_TOO_LARGE, listMapTooLarge);
477-
}
478-
if (objectTooDeep > 0) {
479-
WafMetricCollector.get()
480-
.wafInputTruncated(WafTruncatedType.OBJECT_TOO_DEEP, objectTooDeep);
466+
if (stringTooLong > 0 || listMapTooLarge > 0 || objectTooDeep > 0) {
467+
reqCtx.setWafTruncated();
481468
}
482469
}
483470
}
@@ -501,10 +488,12 @@ public void onDataAvailable(
501488
ActionInfo actionInfo = new ActionInfo(actionType, actionParams);
502489

503490
if ("block_request".equals(actionInfo.type)) {
504-
Flow.Action.RequestBlockingAction rba = createBlockRequestAction(actionInfo);
491+
Flow.Action.RequestBlockingAction rba =
492+
createBlockRequestAction(actionInfo, reqCtx, gwCtx.isRasp);
505493
flow.setAction(rba);
506494
} else if ("redirect_request".equals(actionInfo.type)) {
507-
Flow.Action.RequestBlockingAction rba = createRedirectRequestAction(actionInfo);
495+
Flow.Action.RequestBlockingAction rba =
496+
createRedirectRequestAction(actionInfo, reqCtx, gwCtx.isRasp);
508497
flow.setAction(rba);
509498
} else if ("generate_stack".equals(actionInfo.type)) {
510499
if (Config.get().isAppSecStackTraceEnabled()) {
@@ -516,7 +505,9 @@ public void onDataAvailable(
516505
}
517506
} else {
518507
log.info("Ignoring action with type {}", actionInfo.type);
519-
WafMetricCollector.get().wafRequestBlockFailure();
508+
if (!gwCtx.isRasp) {
509+
reqCtx.setWafRequestBlockFailure();
510+
}
520511
}
521512
}
522513
Collection<AppSecEvent> events = buildEvents(resultWithData);
@@ -543,12 +534,16 @@ public void onDataAvailable(
543534
reqCtx.reportEvents(events);
544535
} else {
545536
log.debug("Rate limited WAF events");
546-
WafMetricCollector.get().wafRequestRateLimited();
537+
if (!gwCtx.isRasp) {
538+
reqCtx.setWafRateLimited();
539+
}
547540
}
548541
}
549542

550543
if (flow.isBlocking()) {
551-
reqCtx.setBlocked();
544+
if (!gwCtx.isRasp) {
545+
reqCtx.setWafBlocked();
546+
}
552547
}
553548
}
554549

@@ -557,7 +552,8 @@ public void onDataAvailable(
557552
}
558553
}
559554

560-
private Flow.Action.RequestBlockingAction createBlockRequestAction(ActionInfo actionInfo) {
555+
private Flow.Action.RequestBlockingAction createBlockRequestAction(
556+
final ActionInfo actionInfo, final AppSecRequestContext reqCtx, final boolean isRasp) {
561557
try {
562558
int statusCode;
563559
Object statusCodeObj = actionInfo.parameters.get("status_code");
@@ -578,12 +574,15 @@ private Flow.Action.RequestBlockingAction createBlockRequestAction(ActionInfo ac
578574
return new Flow.Action.RequestBlockingAction(statusCode, blockingContentType);
579575
} catch (RuntimeException cce) {
580576
log.warn("Invalid blocking action data", cce);
581-
WafMetricCollector.get().wafRequestBlockFailure();
577+
if (!isRasp) {
578+
reqCtx.setWafRequestBlockFailure();
579+
}
582580
return null;
583581
}
584582
}
585583

586-
private Flow.Action.RequestBlockingAction createRedirectRequestAction(ActionInfo actionInfo) {
584+
private Flow.Action.RequestBlockingAction createRedirectRequestAction(
585+
final ActionInfo actionInfo, final AppSecRequestContext reqCtx, final boolean isRasp) {
587586
try {
588587
int statusCode;
589588
Object statusCodeObj = actionInfo.parameters.get("status_code");
@@ -604,7 +603,9 @@ private Flow.Action.RequestBlockingAction createRedirectRequestAction(ActionInfo
604603
return Flow.Action.RequestBlockingAction.forRedirect(statusCode, location);
605604
} catch (RuntimeException cce) {
606605
log.warn("Invalid blocking action data", cce);
607-
WafMetricCollector.get().wafRequestBlockFailure();
606+
if (!isRasp) {
607+
reqCtx.setWafRequestBlockFailure();
608+
}
608609
return null;
609610
}
610611
}
@@ -649,11 +650,13 @@ private Waf.ResultWithData runWafContext(
649650
}
650651
}
651652

652-
private static void incrementErrorCodeMetric(GatewayContext gwCtx, int code) {
653+
private static void incrementErrorCodeMetric(
654+
AppSecRequestContext reqCtx, GatewayContext gwCtx, int code) {
653655
if (gwCtx.isRasp) {
654656
WafMetricCollector.get().raspErrorCode(gwCtx.raspRuleType, code);
655657
} else {
656658
WafMetricCollector.get().wafErrorCode(code);
659+
reqCtx.setWafErrors();
657660
}
658661
}
659662

dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/AppSecRequestContext.java

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,13 @@ public class AppSecRequestContext implements DataBundle, Closeable {
119119
private volatile WafMetrics wafMetrics;
120120
private volatile WafMetrics raspMetrics;
121121
private final AtomicInteger raspMetricsCounter = new AtomicInteger(0);
122-
private volatile boolean blocked;
122+
123+
private volatile boolean wafBlocked;
124+
private volatile boolean wafErrors;
125+
private volatile boolean wafTruncated;
126+
private volatile boolean wafRequestBlockFailure;
127+
private volatile boolean wafRateLimited;
128+
123129
private volatile int wafTimeouts;
124130
private volatile int raspTimeouts;
125131

@@ -174,12 +180,44 @@ public AtomicInteger getRaspMetricsCounter() {
174180
return raspMetricsCounter;
175181
}
176182

177-
public void setBlocked() {
178-
this.blocked = true;
183+
public void setWafBlocked() {
184+
this.wafBlocked = true;
185+
}
186+
187+
public boolean isWafBlocked() {
188+
return wafBlocked;
189+
}
190+
191+
public void setWafErrors() {
192+
this.wafErrors = true;
193+
}
194+
195+
public boolean hasWafErrors() {
196+
return wafErrors;
197+
}
198+
199+
public void setWafTruncated() {
200+
this.wafTruncated = true;
201+
}
202+
203+
public boolean isWafTruncated() {
204+
return wafTruncated;
205+
}
206+
207+
public void setWafRequestBlockFailure() {
208+
this.wafRequestBlockFailure = true;
209+
}
210+
211+
public boolean isWafRequestBlockFailure() {
212+
return wafRequestBlockFailure;
213+
}
214+
215+
public void setWafRateLimited() {
216+
this.wafRateLimited = true;
179217
}
180218

181-
public boolean isBlocked() {
182-
return blocked;
219+
public boolean isWafRateLimited() {
220+
return wafRateLimited;
183221
}
184222

185223
public void increaseWafTimeouts() {

dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/GatewayBridge.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -724,13 +724,16 @@ private NoopFlow onRequestEnded(RequestContext ctx_, IGSpanInfo spanInfo) {
724724
log.debug("Unable to commit, derivatives will be skipped {}", ctx.getDerivativeKeys());
725725
}
726726

727-
if (ctx.isBlocked()) {
728-
WafMetricCollector.get().wafRequestBlocked();
729-
} else if (!collectedEvents.isEmpty()) {
730-
WafMetricCollector.get().wafRequestTriggered();
731-
} else {
732-
WafMetricCollector.get().wafRequest();
733-
}
727+
WafMetricCollector.get()
728+
.wafRequest(
729+
!collectedEvents.isEmpty(), // ruleTriggered
730+
ctx.isWafBlocked(), // requestBlocked
731+
ctx.hasWafErrors(), // wafError
732+
ctx.getWafTimeouts() > 0, // wafTimeout,
733+
ctx.isWafRequestBlockFailure(), // blockFailure,
734+
ctx.isWafRateLimited(), // rateLimited,
735+
ctx.isWafTruncated() // inputTruncated
736+
);
734737
}
735738

736739
ctx.close();

dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/ddwaf/WAFModuleSpecification.groovy

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ class WAFModuleSpecification extends DDSpecification {
475475
2 * ctx.getWafMetrics()
476476
1 * ctx.isWafContextClosed() >> false
477477
1 * ctx.closeWafContext() >> { wafContext.close() }
478-
1 * ctx.setBlocked()
478+
1 * ctx.setWafBlocked()
479479
1 * ctx.isThrottled(null)
480480
0 * _
481481

@@ -560,7 +560,7 @@ class WAFModuleSpecification extends DDSpecification {
560560
2 * ctx.getWafMetrics()
561561
1 * ctx.isWafContextClosed() >> false
562562
1 * ctx.closeWafContext()
563-
1 * ctx.setBlocked()
563+
1 * ctx.setWafBlocked()
564564
1 * ctx.isThrottled(null)
565565
0 * _
566566
}
@@ -665,7 +665,7 @@ class WAFModuleSpecification extends DDSpecification {
665665
1 * ctx.isWafContextClosed() >> false
666666
1 * ctx.closeWafContext()
667667
1 * ctx.reportEvents(_)
668-
1 * ctx.setBlocked()
668+
1 * ctx.setWafBlocked()
669669
1 * ctx.isThrottled(null)
670670
0 * ctx._(*_)
671671
flow.blocking == true
@@ -731,7 +731,7 @@ class WAFModuleSpecification extends DDSpecification {
731731
1 * ctx.isWafContextClosed() >> false
732732
1 * ctx.closeWafContext()
733733
1 * ctx.reportEvents(_)
734-
1 * ctx.setBlocked()
734+
1 * ctx.setWafBlocked()
735735
1 * ctx.isThrottled(null)
736736
0 * ctx._(*_)
737737
flow.blocking == true
@@ -758,7 +758,7 @@ class WAFModuleSpecification extends DDSpecification {
758758
1 * ctx.isWafContextClosed() >> false
759759
1 * ctx.closeWafContext()
760760
1 * ctx.reportEvents(_)
761-
1 * ctx.setBlocked()
761+
1 * ctx.setWafBlocked()
762762
1 * ctx.isThrottled(null)
763763
0 * ctx._(*_)
764764
metrics == null
@@ -817,7 +817,7 @@ class WAFModuleSpecification extends DDSpecification {
817817
}
818818
2 * ctx.getWafMetrics() >> metrics
819819
1 * ctx.reportEvents(*_)
820-
1 * ctx.setBlocked()
820+
1 * ctx.setWafBlocked()
821821
1 * ctx.isThrottled(null)
822822
1 * ctx.isWafContextClosed() >> false
823823
0 * ctx._(*_)
@@ -1016,7 +1016,6 @@ class WAFModuleSpecification extends DDSpecification {
10161016
wafContext = it[0].openContext() }
10171017
2 * ctx.getWafMetrics()
10181018
1 * ctx.increaseWafTimeouts()
1019-
1 * wafMetricCollector.get().wafRequestTimeout()
10201019
0 * _
10211020
10221021
when:
@@ -1787,7 +1786,6 @@ class WAFModuleSpecification extends DDSpecification {
17871786
1 * wafMetricCollector.wafInit(Waf.LIB_VERSION, _, true)
17881787
1 * ctx.getRaspMetrics()
17891788
1 * ctx.getRaspMetricsCounter()
1790-
(0..1) * WafMetricCollector.get().wafRequestError() // TODO: remove this line when WAFModule is removed
17911789
1 * wafMetricCollector.raspErrorCode(RuleType.SQL_INJECTION, wafErrorCode.code)
17921790
0 * _
17931791

@@ -1816,8 +1814,8 @@ class WAFModuleSpecification extends DDSpecification {
18161814
1 * wafContext.run(_, _, _) >> { throw createWafException(wafErrorCode) }
18171815
1 * wafMetricCollector.wafInit(Waf.LIB_VERSION, _, true)
18181816
2 * ctx.getWafMetrics()
1819-
(0..1) * WafMetricCollector.get().wafRequestError() // TODO: remove this line when WAFModule is removed
18201817
1 * wafMetricCollector.wafErrorCode(wafErrorCode.code)
1818+
1 * ctx.setWafErrors()
18211819
0 * _
18221820

18231821
where:

0 commit comments

Comments
 (0)