From e6c7ca33c432e821b17a3519ff36a9ffbba9871e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Vidal=20Dom=C3=ADnguez?= Date: Fri, 7 Feb 2025 12:17:35 +0100 Subject: [PATCH 01/12] Add waf_error tag to appsec.waf.requests metric --- .../appsec/powerwaf/PowerWAFModule.java | 1 + .../api/telemetry/WafMetricCollector.java | 26 ++++++++++++++ .../telemetry/WafMetricCollectorTest.groovy | 25 +++++++++++-- ...afMetricPeriodicActionSpecification.groovy | 36 +++++++++++++++++++ 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java index 23a05c3aa22..3acaad40ce6 100644 --- a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java +++ b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java @@ -445,6 +445,7 @@ public void onDataAvailable( if (!reqCtx.isAdditiveClosed()) { log.error("Error calling WAF", e); } + WafMetricCollector.get().wafRequestError(); return; } catch (AbstractPowerwafException e) { if (gwCtx.isRasp) { diff --git a/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java b/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java index 637cfddbee5..011c83a25c7 100644 --- a/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java +++ b/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java @@ -38,6 +38,7 @@ private WafMetricCollector() { private static final AtomicRequestCounter wafTriggeredRequestCounter = new AtomicRequestCounter(); private static final AtomicRequestCounter wafBlockedRequestCounter = new AtomicRequestCounter(); private static final AtomicRequestCounter wafTimeoutRequestCounter = new AtomicRequestCounter(); + private static final AtomicRequestCounter wafErrorRequestCounter = new AtomicRequestCounter(); private static final AtomicLongArray raspRuleEvalCounter = new AtomicLongArray(RuleType.getNumValues()); private static final AtomicLongArray raspRuleMatchCounter = @@ -108,6 +109,10 @@ public void wafRequestTimeout() { wafTimeoutRequestCounter.increment(); } + public void wafRequestError() { + wafErrorRequestCounter.increment(); + } + public void raspRuleEval(final RuleType ruleType) { raspRuleEvalCounter.incrementAndGet(ruleType.ordinal()); } @@ -160,6 +165,7 @@ public void prepareMetrics() { WafMetricCollector.rulesVersion, false, false, + false, false))) { return; } @@ -174,6 +180,7 @@ public void prepareMetrics() { WafMetricCollector.rulesVersion, true, false, + false, false))) { return; } @@ -188,6 +195,7 @@ public void prepareMetrics() { WafMetricCollector.rulesVersion, true, true, + false, false))) { return; } @@ -202,11 +210,27 @@ public void prepareMetrics() { WafMetricCollector.rulesVersion, false, false, + false, true))) { return; } } + // WAF error requests + if (wafErrorRequestCounter.get() > 0) { + if (!rawMetricsQueue.offer( + new WafRequestsRawMetric( + wafErrorRequestCounter.getAndReset(), + WafMetricCollector.wafVersion, + WafMetricCollector.rulesVersion, + false, + false, + true, + false))) { + return; + } + } + // RASP rule eval per rule type for (RuleType ruleType : RuleType.values()) { long counter = raspRuleEvalCounter.getAndSet(ruleType.ordinal(), 0); @@ -348,6 +372,7 @@ public WafRequestsRawMetric( final String rulesVersion, final boolean triggered, final boolean blocked, + final boolean wafError, final boolean wafTimeout) { super( "waf.requests", @@ -356,6 +381,7 @@ public WafRequestsRawMetric( "event_rules_version:" + rulesVersion, "rule_triggered:" + triggered, "request_blocked:" + blocked, + "waf_error:" + wafError, "waf_timeout:" + wafTimeout); } } diff --git a/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy index c3d792968fb..c0aa39ba97f 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy @@ -30,6 +30,7 @@ class WafMetricCollectorTest extends DDSpecification { WafMetricCollector.get().wafRequestTriggered() WafMetricCollector.get().wafRequestBlocked() WafMetricCollector.get().wafRequestTimeout() + WafMetricCollector.get().wafRequestError() WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION) WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION) WafMetricCollector.get().raspRuleMatch(RuleType.SQL_INJECTION) @@ -76,6 +77,7 @@ class WafMetricCollectorTest extends DDSpecification { 'event_rules_version:rules.3', 'rule_triggered:false', 'request_blocked:false', + 'waf_error:false', 'waf_timeout:false' ].toSet() @@ -88,6 +90,7 @@ class WafMetricCollectorTest extends DDSpecification { 'event_rules_version:rules.3', 'rule_triggered:true', 'request_blocked:false', + 'waf_error:false', 'waf_timeout:false' ].toSet() @@ -102,6 +105,7 @@ class WafMetricCollectorTest extends DDSpecification { 'event_rules_version:rules.3', 'rule_triggered:true', 'request_blocked:true', + 'waf_error:false', 'waf_timeout:false' ].toSet() @@ -115,24 +119,39 @@ class WafMetricCollectorTest extends DDSpecification { 'event_rules_version:rules.3', 'rule_triggered:false', 'request_blocked:false', + 'waf_error:false', 'waf_timeout:true' ].toSet() - def raspRuleEvalSqli = (WafMetricCollector.RaspRuleEval)metrics[7] + def requestWafErrorMetric = (WafMetricCollector.WafRequestsRawMetric)metrics[7] + requestWafErrorMetric.namespace == 'appsec' + requestWafErrorMetric.metricName == 'waf.requests' + requestWafErrorMetric.type == 'count' + requestWafErrorMetric.value == 1 + requestWafErrorMetric.tags.toSet() == [ + 'waf_version:waf_ver1', + 'event_rules_version:rules.3', + 'rule_triggered:false', + 'request_blocked:false', + 'waf_error:true', + 'waf_timeout:false' + ].toSet() + + def raspRuleEvalSqli = (WafMetricCollector.RaspRuleEval)metrics[8] raspRuleEvalSqli.type == 'count' raspRuleEvalSqli.value == 3 raspRuleEvalSqli.namespace == 'appsec' raspRuleEvalSqli.metricName == 'rasp.rule.eval' raspRuleEvalSqli.tags.toSet() == ['rule_type:sql_injection', 'waf_version:waf_ver1'].toSet() - def raspRuleMatch = (WafMetricCollector.RaspRuleMatch)metrics[8] + def raspRuleMatch = (WafMetricCollector.RaspRuleMatch)metrics[9] raspRuleMatch.type == 'count' raspRuleMatch.value == 1 raspRuleMatch.namespace == 'appsec' raspRuleMatch.metricName == 'rasp.rule.match' raspRuleMatch.tags.toSet() == ['rule_type:sql_injection', 'waf_version:waf_ver1'].toSet() - def raspTimeout = (WafMetricCollector.RaspTimeout)metrics[9] + def raspTimeout = (WafMetricCollector.RaspTimeout)metrics[10] raspTimeout.type == 'count' raspTimeout.value == 1 raspTimeout.namespace == 'appsec' diff --git a/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy b/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy index 329dcb18c14..15eabf07c14 100644 --- a/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy +++ b/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy @@ -49,6 +49,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { WafMetricCollector.get().wafRequestBlocked() WafMetricCollector.get().wafRequest() WafMetricCollector.get().wafRequestTimeout() + WafMetricCollector.get().wafRequestError() WafMetricCollector.get().prepareMetrics() periodicAction.doIteration(telemetryService) @@ -66,6 +67,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'event_rules_version:rules_ver_1', 'rule_triggered:false', 'request_blocked:false', + 'waf_error:false', 'waf_timeout:false' ] } ) @@ -78,6 +80,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'event_rules_version:rules_ver_1', 'rule_triggered:true', 'request_blocked:false', + 'waf_error:false', 'waf_timeout:false' ] } ) @@ -90,6 +93,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'event_rules_version:rules_ver_1', 'rule_triggered:true', 'request_blocked:true', + 'waf_error:false', 'waf_timeout:false' ] } ) @@ -102,9 +106,23 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'event_rules_version:rules_ver_1', 'rule_triggered:false', 'request_blocked:false', + 'waf_error:false', 'waf_timeout:true' ] } ) + 1 * telemetryService.addMetric( { Metric metric -> + metric.namespace == 'appsec' && + metric.metric == 'waf.requests' && + metric.points[0][1] == 1 && + metric.tags == [ + 'waf_version:0.0.0', + 'event_rules_version:rules_ver_1', + 'rule_triggered:false', + 'request_blocked:false', + 'waf_error:true', + 'waf_timeout:false' + ] + } ) 0 * _._ when: 'waf.updates happens' @@ -113,6 +131,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { WafMetricCollector.get().wafRequestTriggered() WafMetricCollector.get().wafRequestBlocked() WafMetricCollector.get().wafRequestTimeout() + WafMetricCollector.get().wafRequestError() WafMetricCollector.get().prepareMetrics() periodicAction.doIteration(telemetryService) @@ -130,6 +149,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'event_rules_version:rules_ver_2', 'rule_triggered:false', 'request_blocked:false', + 'waf_error:false', 'waf_timeout:false' ] } ) @@ -142,6 +162,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'event_rules_version:rules_ver_2', 'rule_triggered:true', 'request_blocked:false', + 'waf_error:false', 'waf_timeout:false' ] } ) @@ -154,6 +175,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'event_rules_version:rules_ver_2', 'rule_triggered:true', 'request_blocked:true', + 'waf_error:false', 'waf_timeout:false' ] } ) @@ -166,9 +188,23 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'event_rules_version:rules_ver_2', 'rule_triggered:false', 'request_blocked:false', + 'waf_error:false', 'waf_timeout:true' ] } ) + 1 * telemetryService.addMetric( { Metric metric -> + metric.namespace == 'appsec' && + metric.metric == 'waf.requests' && + metric.points[0][1] == 1 && + metric.tags == [ + 'waf_version:0.0.0', + 'event_rules_version:rules_ver_2', + 'rule_triggered:false', + 'request_blocked:false', + 'waf_error:true', + 'waf_timeout:false' + ] + } ) 0 * _._ } } From 139680f8016084385e0ea66135c61be7f15823a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Vidal=20Dom=C3=ADnguez?= Date: Fri, 7 Feb 2025 14:28:40 +0100 Subject: [PATCH 02/12] Add tag rate_limited # Conflicts: # dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java --- .../appsec/powerwaf/PowerWAFModule.java | 39 +++++++++++-------- .../api/telemetry/WafMetricCollector.java | 29 ++++++++++---- .../telemetry/WafMetricCollectorTest.groovy | 16 +++++--- ...afMetricPeriodicActionSpecification.groovy | 32 ++++++++++----- 4 files changed, 77 insertions(+), 39 deletions(-) diff --git a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java index 3acaad40ce6..af8427c6540 100644 --- a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java +++ b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java @@ -500,25 +500,30 @@ public void onDataAvailable( } Collection events = buildEvents(resultWithData); - if (!events.isEmpty() && !reqCtx.isThrottled(rateLimiter)) { - AgentSpan activeSpan = AgentTracer.get().activeSpan(); - if (activeSpan != null) { - log.debug("Setting force-keep tag on the current span"); - // Keep event related span, because it could be ignored in case of - // reduced datadog sampling rate. - activeSpan.getLocalRootSpan().setTag(Tags.ASM_KEEP, true); - // If APM is disabled, inform downstream services that the current - // distributed trace contains at least one ASM event and must inherit - // the given force-keep priority - activeSpan - .getLocalRootSpan() - .setTag(Tags.PROPAGATED_TRACE_SOURCE, ProductTraceSource.ASM); + if (!events.isEmpty()) { + if (!reqCtx.isThrottled(rateLimiter)) { + AgentSpan activeSpan = AgentTracer.get().activeSpan(); + if (activeSpan != null) { + log.debug("Setting force-keep tag on the current span"); + // Keep event related span, because it could be ignored in case of + // reduced datadog sampling rate. + activeSpan.getLocalRootSpan().setTag(Tags.ASM_KEEP, true); + // If APM is disabled, inform downstream services that the current + // distributed trace contains at least one ASM event and must inherit + // the given force-keep priority + activeSpan + .getLocalRootSpan() + .setTag(Tags.PROPAGATED_TRACE_SOURCE, ProductTraceSource.ASM); + } else { + // If active span is not available the ASK_KEEP tag will be set in the GatewayBridge + // when the request ends + log.debug("There is no active span available"); + } + reqCtx.reportEvents(events); } else { - // If active span is not available the ASK_KEEP tag will be set in the GatewayBridge - // when the request ends - log.debug("There is no active span available"); + log.debug("Rate limited WAF events"); + WafMetricCollector.get().wafRequestRateLimited(); } - reqCtx.reportEvents(events); } if (flow.isBlocking()) { diff --git a/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java b/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java index 011c83a25c7..11583c2ed8e 100644 --- a/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java +++ b/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java @@ -39,6 +39,8 @@ private WafMetricCollector() { private static final AtomicRequestCounter wafBlockedRequestCounter = new AtomicRequestCounter(); private static final AtomicRequestCounter wafTimeoutRequestCounter = new AtomicRequestCounter(); private static final AtomicRequestCounter wafErrorRequestCounter = new AtomicRequestCounter(); + private static final AtomicRequestCounter wafRateLimitedRequestCounter = + new AtomicRequestCounter(); private static final AtomicLongArray raspRuleEvalCounter = new AtomicLongArray(RuleType.getNumValues()); private static final AtomicLongArray raspRuleMatchCounter = @@ -113,6 +115,10 @@ public void wafRequestError() { wafErrorRequestCounter.increment(); } + public void wafRequestRateLimited() { + wafRateLimitedRequestCounter.increment(); + } + public void raspRuleEval(final RuleType ruleType) { raspRuleEvalCounter.incrementAndGet(ruleType.ordinal()); } @@ -156,6 +162,8 @@ public Collection drain() { @Override public void prepareMetrics() { + final boolean isRateLimited = wafRateLimitedRequestCounter.getAndReset() > 0; + // Requests if (wafRequestCounter.get() > 0) { if (!rawMetricsQueue.offer( @@ -166,7 +174,8 @@ public void prepareMetrics() { false, false, false, - false))) { + false, + isRateLimited))) { return; } } @@ -181,7 +190,8 @@ public void prepareMetrics() { true, false, false, - false))) { + false, + isRateLimited))) { return; } } @@ -196,7 +206,8 @@ public void prepareMetrics() { true, true, false, - false))) { + false, + isRateLimited))) { return; } } @@ -211,7 +222,8 @@ public void prepareMetrics() { false, false, false, - true))) { + true, + isRateLimited))) { return; } } @@ -226,7 +238,8 @@ public void prepareMetrics() { false, false, true, - false))) { + false, + isRateLimited))) { return; } } @@ -373,7 +386,8 @@ public WafRequestsRawMetric( final boolean triggered, final boolean blocked, final boolean wafError, - final boolean wafTimeout) { + final boolean wafTimeout, + final boolean rateLimited) { super( "waf.requests", counter, @@ -382,7 +396,8 @@ public WafRequestsRawMetric( "rule_triggered:" + triggered, "request_blocked:" + blocked, "waf_error:" + wafError, - "waf_timeout:" + wafTimeout); + "waf_timeout:" + wafTimeout, + "rate_limited:" + rateLimited); } } diff --git a/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy index c0aa39ba97f..db9da010202 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy @@ -31,6 +31,7 @@ class WafMetricCollectorTest extends DDSpecification { WafMetricCollector.get().wafRequestBlocked() WafMetricCollector.get().wafRequestTimeout() WafMetricCollector.get().wafRequestError() + WafMetricCollector.get().wafRequestRateLimited() WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION) WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION) WafMetricCollector.get().raspRuleMatch(RuleType.SQL_INJECTION) @@ -78,7 +79,8 @@ class WafMetricCollectorTest extends DDSpecification { 'rule_triggered:false', 'request_blocked:false', 'waf_error:false', - 'waf_timeout:false' + 'waf_timeout:false', + 'rate_limited:true' ].toSet() def requestTriggeredMetric = (WafMetricCollector.WafRequestsRawMetric)metrics[4] @@ -91,7 +93,8 @@ class WafMetricCollectorTest extends DDSpecification { 'rule_triggered:true', 'request_blocked:false', 'waf_error:false', - 'waf_timeout:false' + 'waf_timeout:false', + 'rate_limited:true' ].toSet() @@ -106,7 +109,8 @@ class WafMetricCollectorTest extends DDSpecification { 'rule_triggered:true', 'request_blocked:true', 'waf_error:false', - 'waf_timeout:false' + 'waf_timeout:false', + 'rate_limited:true' ].toSet() def requestTimeoutMetric = (WafMetricCollector.WafRequestsRawMetric)metrics[6] @@ -120,7 +124,8 @@ class WafMetricCollectorTest extends DDSpecification { 'rule_triggered:false', 'request_blocked:false', 'waf_error:false', - 'waf_timeout:true' + 'waf_timeout:true', + 'rate_limited:true' ].toSet() def requestWafErrorMetric = (WafMetricCollector.WafRequestsRawMetric)metrics[7] @@ -134,7 +139,8 @@ class WafMetricCollectorTest extends DDSpecification { 'rule_triggered:false', 'request_blocked:false', 'waf_error:true', - 'waf_timeout:false' + 'waf_timeout:false', + 'rate_limited:true' ].toSet() def raspRuleEvalSqli = (WafMetricCollector.RaspRuleEval)metrics[8] diff --git a/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy b/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy index 15eabf07c14..19d114da569 100644 --- a/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy +++ b/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy @@ -50,6 +50,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { WafMetricCollector.get().wafRequest() WafMetricCollector.get().wafRequestTimeout() WafMetricCollector.get().wafRequestError() + WafMetricCollector.get().wafRequestRateLimited() WafMetricCollector.get().prepareMetrics() periodicAction.doIteration(telemetryService) @@ -68,7 +69,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'rule_triggered:false', 'request_blocked:false', 'waf_error:false', - 'waf_timeout:false' + 'waf_timeout:false', + 'rate_limited:true' ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -81,7 +83,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'rule_triggered:true', 'request_blocked:false', 'waf_error:false', - 'waf_timeout:false' + 'waf_timeout:false', + 'rate_limited:true' ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -94,7 +97,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'rule_triggered:true', 'request_blocked:true', 'waf_error:false', - 'waf_timeout:false' + 'waf_timeout:false', + 'rate_limited:true' ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -107,7 +111,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'rule_triggered:false', 'request_blocked:false', 'waf_error:false', - 'waf_timeout:true' + 'waf_timeout:true', + 'rate_limited:true' ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -120,7 +125,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'rule_triggered:false', 'request_blocked:false', 'waf_error:true', - 'waf_timeout:false' + 'waf_timeout:false', + 'rate_limited:true' ] } ) 0 * _._ @@ -132,6 +138,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { WafMetricCollector.get().wafRequestBlocked() WafMetricCollector.get().wafRequestTimeout() WafMetricCollector.get().wafRequestError() + WafMetricCollector.get().wafRequestRateLimited() WafMetricCollector.get().prepareMetrics() periodicAction.doIteration(telemetryService) @@ -150,7 +157,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'rule_triggered:false', 'request_blocked:false', 'waf_error:false', - 'waf_timeout:false' + 'waf_timeout:false', + 'rate_limited:true' ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -163,7 +171,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'rule_triggered:true', 'request_blocked:false', 'waf_error:false', - 'waf_timeout:false' + 'waf_timeout:false', + 'rate_limited:true' ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -176,7 +185,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'rule_triggered:true', 'request_blocked:true', 'waf_error:false', - 'waf_timeout:false' + 'waf_timeout:false', + 'rate_limited:true' ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -189,7 +199,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'rule_triggered:false', 'request_blocked:false', 'waf_error:false', - 'waf_timeout:true' + 'waf_timeout:true', + 'rate_limited:true' ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -202,7 +213,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'rule_triggered:false', 'request_blocked:false', 'waf_error:true', - 'waf_timeout:false' + 'waf_timeout:false', + 'rate_limited:true' ] } ) 0 * _._ From 53d71ebb241aeb075b9617205d66792cf9f50223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Vidal=20Dom=C3=ADnguez?= Date: Fri, 7 Feb 2025 14:59:10 +0100 Subject: [PATCH 03/12] Add tag block_failure --- .../datadog/appsec/powerwaf/PowerWAFModule.java | 3 +++ .../trace/api/telemetry/WafMetricCollector.java | 14 ++++++++++++++ .../api/telemetry/WafMetricCollectorTest.groovy | 6 ++++++ .../WafMetricPeriodicActionSpecification.groovy | 12 ++++++++++++ 4 files changed, 35 insertions(+) diff --git a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java index af8427c6540..25f04a3f47a 100644 --- a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java +++ b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java @@ -496,6 +496,7 @@ public void onDataAvailable( } } else { log.info("Ignoring action with type {}", actionInfo.type); + WafMetricCollector.get().wafRequestBlockFailure(); } } Collection events = buildEvents(resultWithData); @@ -557,6 +558,7 @@ private Flow.Action.RequestBlockingAction createBlockRequestAction(ActionInfo ac return new Flow.Action.RequestBlockingAction(statusCode, blockingContentType); } catch (RuntimeException cce) { log.warn("Invalid blocking action data", cce); + WafMetricCollector.get().wafRequestBlockFailure(); return null; } } @@ -582,6 +584,7 @@ private Flow.Action.RequestBlockingAction createRedirectRequestAction(ActionInfo return Flow.Action.RequestBlockingAction.forRedirect(statusCode, location); } catch (RuntimeException cce) { log.warn("Invalid blocking action data", cce); + WafMetricCollector.get().wafRequestBlockFailure(); return null; } } diff --git a/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java b/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java index 11583c2ed8e..bcb0546225f 100644 --- a/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java +++ b/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java @@ -41,6 +41,8 @@ private WafMetricCollector() { private static final AtomicRequestCounter wafErrorRequestCounter = new AtomicRequestCounter(); private static final AtomicRequestCounter wafRateLimitedRequestCounter = new AtomicRequestCounter(); + private static final AtomicRequestCounter wafBlockFailureRequestCounter = + new AtomicRequestCounter(); private static final AtomicLongArray raspRuleEvalCounter = new AtomicLongArray(RuleType.getNumValues()); private static final AtomicLongArray raspRuleMatchCounter = @@ -119,6 +121,10 @@ public void wafRequestRateLimited() { wafRateLimitedRequestCounter.increment(); } + public void wafRequestBlockFailure() { + wafBlockFailureRequestCounter.increment(); + } + public void raspRuleEval(final RuleType ruleType) { raspRuleEvalCounter.incrementAndGet(ruleType.ordinal()); } @@ -163,6 +169,7 @@ public Collection drain() { @Override public void prepareMetrics() { final boolean isRateLimited = wafRateLimitedRequestCounter.getAndReset() > 0; + final boolean isBlockFailure = wafBlockFailureRequestCounter.getAndReset() > 0; // Requests if (wafRequestCounter.get() > 0) { @@ -175,6 +182,7 @@ public void prepareMetrics() { false, false, false, + isBlockFailure, isRateLimited))) { return; } @@ -191,6 +199,7 @@ public void prepareMetrics() { false, false, false, + isBlockFailure, isRateLimited))) { return; } @@ -207,6 +216,7 @@ public void prepareMetrics() { true, false, false, + isBlockFailure, isRateLimited))) { return; } @@ -223,6 +233,7 @@ public void prepareMetrics() { false, false, true, + isBlockFailure, isRateLimited))) { return; } @@ -239,6 +250,7 @@ public void prepareMetrics() { false, true, false, + isBlockFailure, isRateLimited))) { return; } @@ -387,6 +399,7 @@ public WafRequestsRawMetric( final boolean blocked, final boolean wafError, final boolean wafTimeout, + final boolean blockFailure, final boolean rateLimited) { super( "waf.requests", @@ -397,6 +410,7 @@ public WafRequestsRawMetric( "request_blocked:" + blocked, "waf_error:" + wafError, "waf_timeout:" + wafTimeout, + "block_failure:" + blockFailure, "rate_limited:" + rateLimited); } } diff --git a/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy index db9da010202..2d80c9fa3c7 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy @@ -32,6 +32,7 @@ class WafMetricCollectorTest extends DDSpecification { WafMetricCollector.get().wafRequestTimeout() WafMetricCollector.get().wafRequestError() WafMetricCollector.get().wafRequestRateLimited() + WafMetricCollector.get().wafRequestBlockFailure() WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION) WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION) WafMetricCollector.get().raspRuleMatch(RuleType.SQL_INJECTION) @@ -80,6 +81,7 @@ class WafMetricCollectorTest extends DDSpecification { 'request_blocked:false', 'waf_error:false', 'waf_timeout:false', + 'block_failure:true', 'rate_limited:true' ].toSet() @@ -94,6 +96,7 @@ class WafMetricCollectorTest extends DDSpecification { 'request_blocked:false', 'waf_error:false', 'waf_timeout:false', + 'block_failure:true', 'rate_limited:true' ].toSet() @@ -110,6 +113,7 @@ class WafMetricCollectorTest extends DDSpecification { 'request_blocked:true', 'waf_error:false', 'waf_timeout:false', + 'block_failure:true', 'rate_limited:true' ].toSet() @@ -125,6 +129,7 @@ class WafMetricCollectorTest extends DDSpecification { 'request_blocked:false', 'waf_error:false', 'waf_timeout:true', + 'block_failure:true', 'rate_limited:true' ].toSet() @@ -140,6 +145,7 @@ class WafMetricCollectorTest extends DDSpecification { 'request_blocked:false', 'waf_error:true', 'waf_timeout:false', + 'block_failure:true', 'rate_limited:true' ].toSet() diff --git a/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy b/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy index 19d114da569..8e38f225eb2 100644 --- a/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy +++ b/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy @@ -51,6 +51,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { WafMetricCollector.get().wafRequestTimeout() WafMetricCollector.get().wafRequestError() WafMetricCollector.get().wafRequestRateLimited() + WafMetricCollector.get().wafRequestBlockFailure() WafMetricCollector.get().prepareMetrics() periodicAction.doIteration(telemetryService) @@ -70,6 +71,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'request_blocked:false', 'waf_error:false', 'waf_timeout:false', + 'block_failure:true', 'rate_limited:true' ] } ) @@ -84,6 +86,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'request_blocked:false', 'waf_error:false', 'waf_timeout:false', + 'block_failure:true', 'rate_limited:true' ] } ) @@ -98,6 +101,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'request_blocked:true', 'waf_error:false', 'waf_timeout:false', + 'block_failure:true', 'rate_limited:true' ] } ) @@ -112,6 +116,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'request_blocked:false', 'waf_error:false', 'waf_timeout:true', + 'block_failure:true', 'rate_limited:true' ] } ) @@ -126,6 +131,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'request_blocked:false', 'waf_error:true', 'waf_timeout:false', + 'block_failure:true', 'rate_limited:true' ] } ) @@ -139,6 +145,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { WafMetricCollector.get().wafRequestTimeout() WafMetricCollector.get().wafRequestError() WafMetricCollector.get().wafRequestRateLimited() + WafMetricCollector.get().wafRequestBlockFailure() WafMetricCollector.get().prepareMetrics() periodicAction.doIteration(telemetryService) @@ -158,6 +165,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'request_blocked:false', 'waf_error:false', 'waf_timeout:false', + 'block_failure:true', 'rate_limited:true' ] } ) @@ -172,6 +180,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'request_blocked:false', 'waf_error:false', 'waf_timeout:false', + 'block_failure:true', 'rate_limited:true' ] } ) @@ -186,6 +195,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'request_blocked:true', 'waf_error:false', 'waf_timeout:false', + 'block_failure:true', 'rate_limited:true' ] } ) @@ -200,6 +210,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'request_blocked:false', 'waf_error:false', 'waf_timeout:true', + 'block_failure:true', 'rate_limited:true' ] } ) @@ -214,6 +225,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'request_blocked:false', 'waf_error:true', 'waf_timeout:false', + 'block_failure:true', 'rate_limited:true' ] } ) From 0f2c082ba930ca7caf0c29523a27d1e46e40f613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Vidal=20Dom=C3=ADnguez?= Date: Thu, 13 Feb 2025 13:49:49 +0100 Subject: [PATCH 04/12] Add tag input_truncated --- .../appsec/powerwaf/PowerWAFModule.java | 22 ++++++ .../PowerWAFModuleSpecification.groovy | 73 ++++++++++--------- .../trace/api/telemetry/TruncatedType.java | 17 +++++ .../api/telemetry/WafMetricCollector.java | 34 +++++++-- .../telemetry/WafMetricCollectorTest.groovy | 16 ++-- ...afMetricPeriodicActionSpecification.groovy | 33 ++++++--- 6 files changed, 140 insertions(+), 55 deletions(-) create mode 100644 internal-api/src/main/java/datadog/trace/api/telemetry/TruncatedType.java diff --git a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java index 25f04a3f47a..5ff6e9f8721 100644 --- a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java +++ b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java @@ -29,6 +29,7 @@ import datadog.trace.api.ProductTraceSource; import datadog.trace.api.gateway.Flow; import datadog.trace.api.telemetry.LogCollector; +import datadog.trace.api.telemetry.TruncatedType; import datadog.trace.api.telemetry.WafMetricCollector; import datadog.trace.api.time.SystemTimeSource; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -461,6 +462,27 @@ public void onDataAvailable( long elapsed = System.currentTimeMillis() - start; StandardizedLogging.finishedExecutionWAF(log, elapsed); } + if (!gwCtx.isRasp) { + PowerwafMetrics wafMetrics = reqCtx.getWafMetrics(); + if (wafMetrics != null) { + final long stringTooLong = wafMetrics.getWafInputsTruncatedStringTooLongCount(); + final long listMapTooLarge = wafMetrics.getWafInputsTruncatedListMapTooLargeCount(); + final long objectTooDeep = wafMetrics.getWafInputsTruncatedObjectTooDeepCount(); + + if (stringTooLong > 0) { + WafMetricCollector.get() + .wafInputTruncated(TruncatedType.STRING_TOO_LONG, stringTooLong); + } + if (listMapTooLarge > 0) { + WafMetricCollector.get() + .wafInputTruncated(TruncatedType.LIST_MAP_TOO_LARGE, listMapTooLarge); + } + if (objectTooDeep > 0) { + WafMetricCollector.get() + .wafInputTruncated(TruncatedType.OBJECT_TOO_DEEP, objectTooDeep); + } + } + } } StandardizedLogging.inAppWafReturn(log, resultWithData); diff --git a/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFModuleSpecification.groovy b/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFModuleSpecification.groovy index b5f5403bbd1..6c2f60a6af6 100644 --- a/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFModuleSpecification.groovy +++ b/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFModuleSpecification.groovy @@ -37,6 +37,7 @@ import spock.lang.Shared import spock.lang.Unroll import java.util.concurrent.CountDownLatch +import java.util.concurrent.atomic.AtomicLong import static datadog.trace.api.config.AppSecConfig.APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP import static datadog.trace.api.config.AppSecConfig.APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP @@ -221,7 +222,7 @@ class PowerWAFModuleSpecification extends DDSpecification { } 2 * tracer.activeSpan() 1 * ctx.reportEvents(_ as Collection) - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() 1 * flow.isBlocking() @@ -256,7 +257,7 @@ class PowerWAFModuleSpecification extends DDSpecification { } 2 * tracer.activeSpan() 1 * ctx.reportEvents(_ as Collection) - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 2 * ctx.closeAdditive() 1 * flow.isBlocking() @@ -299,7 +300,7 @@ class PowerWAFModuleSpecification extends DDSpecification { } 2 * tracer.activeSpan() 1 * ctx.reportEvents(_ as Collection) - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() 1 * flow.isBlocking() @@ -324,7 +325,7 @@ class PowerWAFModuleSpecification extends DDSpecification { } 2 * tracer.activeSpan() 1 * ctx.reportEvents(_ as Collection) - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() 1 * flow.isBlocking() @@ -382,7 +383,7 @@ class PowerWAFModuleSpecification extends DDSpecification { } 2 * tracer.activeSpan() 1 * ctx.reportEvents(_ as Collection) - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() 1 * flow.isBlocking() @@ -401,7 +402,7 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * ctx.getOrCreateAdditive(_ as PowerwafContext, true, false) >> { pwafAdditive = it[0].openAdditive() } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() 0 * _ @@ -463,7 +464,7 @@ class PowerWAFModuleSpecification extends DDSpecification { } 2 * tracer.activeSpan() 1 * ctx.reportEvents(_ as Collection) - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() >> { pwafAdditive.close() } 1 * ctx.setBlocked() @@ -484,7 +485,7 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * ctx.getOrCreateAdditive(_, true, false) >> { pwafAdditive = it[0].openAdditive() } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() 0 * _ @@ -546,10 +547,9 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * ctx.getOrCreateAdditive(_, true, false) >> { pwafAdditive = it[0].openAdditive() } - 3 * tracer.activeSpan() - // we get two events: one for origin rule, and one for the custom one - 1 * ctx.reportEvents(hasSize(2)) - 1 * ctx.getWafMetrics() + 2 * tracer.activeSpan() + 1 * ctx.reportEvents(hasSize(1)) + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() 1 * ctx.setBlocked() @@ -629,7 +629,7 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * ctx.getOrCreateAdditive(_, true, false) >> { it[0].openAdditive() } 2 * tracer.activeSpan() 1 * ctx.reportEvents(_ as Collection) - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() 1 * flow.isBlocking() @@ -653,7 +653,7 @@ class PowerWAFModuleSpecification extends DDSpecification { metrics = pwCtx.createMetrics() pwafAdditive } - 1 * ctx.getWafMetrics() >> metrics + 2 * ctx.getWafMetrics() >> metrics 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() 1 * ctx.reportEvents(_) @@ -719,7 +719,7 @@ class PowerWAFModuleSpecification extends DDSpecification { metrics = pwCtx.createMetrics() pwafAdditive } - 1 * ctx.getWafMetrics() >> metrics + 2 * ctx.getWafMetrics() >> metrics 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() 1 * ctx.reportEvents(_) @@ -746,7 +746,7 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * ctx.getOrCreateAdditive(_, false, false) >> { pwafAdditive = it[0].openAdditive() } - 1 * ctx.getWafMetrics() >> null + 2 * ctx.getWafMetrics() >> null 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() 1 * ctx.reportEvents(_) @@ -777,7 +777,14 @@ class PowerWAFModuleSpecification extends DDSpecification { pwafAdditive } 1 * ctx.closeAdditive() - 2 * ctx.getWafMetrics() >> { metrics.with { totalDdwafRunTimeNs = 1000; totalRunTimeNs = 2000; it} } + 3 * ctx.getWafMetrics() >> { + metrics.with { + totalDdwafRunTimeNs = new AtomicLong(1000) + totalRunTimeNs = new AtomicLong(2000) + wafInputsTruncatedStringTooLongCount = new AtomicLong(0) + wafInputsTruncatedListMapTooLargeCount = new AtomicLong(0) + wafInputsTruncatedObjectTooDeepCount = new AtomicLong(0) + it } } 1 * segment.setTagTop('_dd.appsec.waf.duration', 1) 1 * segment.setTagTop('_dd.appsec.waf.duration_ext', 2) @@ -800,7 +807,7 @@ class PowerWAFModuleSpecification extends DDSpecification { metrics = pwCtx.createMetrics() pwafAdditive } - 1 * ctx.getWafMetrics() >> metrics + 2 * ctx.getWafMetrics() >> metrics 1 * ctx.reportEvents(*_) 1 * ctx.setBlocked() 1 * ctx.isThrottled(null) @@ -999,7 +1006,7 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * ctx.isAdditiveClosed() 1 * ctx.getOrCreateAdditive(_, true, false) >> { pwafAdditive = it[0].openAdditive() } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.increaseWafTimeouts() 1 * wafMetricCollector.get().wafRequestTimeout() 0 * _ @@ -1083,7 +1090,7 @@ class PowerWAFModuleSpecification extends DDSpecification { pwafAdditive = it[0].openAdditive() } 1 * ctx.reportEvents(_ as Collection) 1 * ctx.isAdditiveClosed() - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isThrottled(null) 1 * ctx.closeAdditive() 2 * tracer.activeSpan() @@ -1126,7 +1133,7 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * ctx.getOrCreateAdditive(_, true, false) >> { pwafAdditive = it[0].openAdditive() } 2 * tracer.activeSpan() 1 * ctx.reportEvents(_ as Collection) - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * flow.setAction({ it.blocking }) 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() @@ -1183,7 +1190,7 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * reconf.reloadSubscriptions() 1 * ctx.getOrCreateAdditive(_, true, false) >> { pwafAdditive = it[0].openAdditive() } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() >> { pwafAdditive.close() } _ * ctx.increaseWafTimeouts() @@ -1207,7 +1214,7 @@ class PowerWAFModuleSpecification extends DDSpecification { then: 'no match; data was cleared (though rule is no longer disabled)' 1 * ctx.getOrCreateAdditive(_, true, false) >> { pwafAdditive = it[0].openAdditive() } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() >> {pwafAdditive.close()} 1 * wafMetricCollector.wafUpdates(_, true) @@ -1234,7 +1241,7 @@ class PowerWAFModuleSpecification extends DDSpecification { pwafAdditive = it[0].openAdditive() } 2 * tracer.activeSpan() 1 * ctx.reportEvents(_ as Collection) - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * flow.setAction({ it.blocking }) 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() >> {pwafAdditive.close()} @@ -1261,7 +1268,7 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * wafMetricCollector.wafUpdates(_, true) 1 * reconf.reloadSubscriptions() 1 * ctx.getOrCreateAdditive(_, true, false) >> { pwafAdditive = it[0].openAdditive() } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() _ * ctx.increaseWafTimeouts() @@ -1293,7 +1300,7 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * reconf.reloadSubscriptions() // no attack 1 * ctx.getOrCreateAdditive(_, true, false) >> { pwafAdditive = it[0].openAdditive() } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() >> {pwafAdditive.close()} _ * ctx.increaseWafTimeouts() @@ -1319,7 +1326,7 @@ class PowerWAFModuleSpecification extends DDSpecification { // no attack 1 * ctx.getOrCreateAdditive(_, true, false) >> { pwafAdditive = it[0].openAdditive() } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() >> {pwafAdditive.close()} _ * ctx.increaseWafTimeouts() @@ -1345,7 +1352,7 @@ class PowerWAFModuleSpecification extends DDSpecification { // attack found 1 * ctx.getOrCreateAdditive(_, true, false) >> { pwafAdditive = it[0].openAdditive() } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * flow.isBlocking() 1 * flow.setAction({ it.blocking }) 2 * tracer.activeSpan() @@ -1374,7 +1381,7 @@ class PowerWAFModuleSpecification extends DDSpecification { // no attack 1 * ctx.getOrCreateAdditive(_, true, false) >> { pwafAdditive = it[0].openAdditive() } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() _ * ctx.increaseWafTimeouts() @@ -1491,7 +1498,7 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * ctx.reportEvents(_ as Collection) >> { it[0].iterator().next().ruleMatches[0].parameters[0].value == '/cybercop' } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * flow.isBlocking() 1 * ctx.isThrottled(null) 1 * ctx.isAdditiveClosed() >> false @@ -1509,7 +1516,7 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * ctx.reportEvents(_ as Collection) >> { it[0].iterator().next().ruleMatches[0].parameters[0].value == 'user-to-block-1' } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isAdditiveClosed() >> false 1 * ctx.closeAdditive() 1 * ctx.isThrottled(null) @@ -1588,7 +1595,7 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * ctx.getOrCreateAdditive(_ as PowerwafContext, true, false) >> { pwafAdditive = it[0].openAdditive() } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isThrottled(null) 1 * ctx.reportEvents(_ as Collection) 1 * ctx.closeAdditive() @@ -1627,7 +1634,7 @@ class PowerWAFModuleSpecification extends DDSpecification { 1 * ctx.getOrCreateAdditive(_ as PowerwafContext, true, false) >> { pwafAdditive = it[0].openAdditive() } - 1 * ctx.getWafMetrics() + 2 * ctx.getWafMetrics() 1 * ctx.isThrottled(null) 1 * ctx.reportEvents(_ as Collection) 1 * ctx.closeAdditive() diff --git a/internal-api/src/main/java/datadog/trace/api/telemetry/TruncatedType.java b/internal-api/src/main/java/datadog/trace/api/telemetry/TruncatedType.java new file mode 100644 index 00000000000..a28f8b85bb1 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/telemetry/TruncatedType.java @@ -0,0 +1,17 @@ +package datadog.trace.api.telemetry; + +public enum TruncatedType { + STRING_TOO_LONG(1), + LIST_MAP_TOO_LARGE(2), + OBJECT_TOO_DEEP(4); + + private final int value; + + TruncatedType(int value) { + this.value = value; + } + + public int getValue() { + return this.value; + } +} diff --git a/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java b/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java index bcb0546225f..0dc3188fbee 100644 --- a/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java +++ b/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java @@ -43,6 +43,8 @@ private WafMetricCollector() { new AtomicRequestCounter(); private static final AtomicRequestCounter wafBlockFailureRequestCounter = new AtomicRequestCounter(); + private static final AtomicLongArray wafInputTruncatedCounter = + new AtomicLongArray(TruncatedType.values().length); private static final AtomicLongArray raspRuleEvalCounter = new AtomicLongArray(RuleType.getNumValues()); private static final AtomicLongArray raspRuleMatchCounter = @@ -125,6 +127,10 @@ public void wafRequestBlockFailure() { wafBlockFailureRequestCounter.increment(); } + public void wafInputTruncated(final TruncatedType truncatedType, long increment) { + wafInputTruncatedCounter.addAndGet(truncatedType.ordinal(), increment); + } + public void raspRuleEval(final RuleType ruleType) { raspRuleEvalCounter.incrementAndGet(ruleType.ordinal()); } @@ -170,6 +176,13 @@ public Collection drain() { public void prepareMetrics() { final boolean isRateLimited = wafRateLimitedRequestCounter.getAndReset() > 0; final boolean isBlockFailure = wafBlockFailureRequestCounter.getAndReset() > 0; + boolean isWafInputTruncated = false; + for (TruncatedType truncatedType : TruncatedType.values()) { + isWafInputTruncated = wafInputTruncatedCounter.getAndSet(truncatedType.ordinal(), 0) > 0; + if (isWafInputTruncated) { + break; + } + } // Requests if (wafRequestCounter.get() > 0) { @@ -183,7 +196,8 @@ public void prepareMetrics() { false, false, isBlockFailure, - isRateLimited))) { + isRateLimited, + isWafInputTruncated))) { return; } } @@ -200,7 +214,8 @@ public void prepareMetrics() { false, false, isBlockFailure, - isRateLimited))) { + isRateLimited, + isWafInputTruncated))) { return; } } @@ -217,7 +232,8 @@ public void prepareMetrics() { false, false, isBlockFailure, - isRateLimited))) { + isRateLimited, + isWafInputTruncated))) { return; } } @@ -234,7 +250,8 @@ public void prepareMetrics() { false, true, isBlockFailure, - isRateLimited))) { + isRateLimited, + isWafInputTruncated))) { return; } } @@ -251,7 +268,8 @@ public void prepareMetrics() { true, false, isBlockFailure, - isRateLimited))) { + isRateLimited, + isWafInputTruncated))) { return; } } @@ -400,7 +418,8 @@ public WafRequestsRawMetric( final boolean wafError, final boolean wafTimeout, final boolean blockFailure, - final boolean rateLimited) { + final boolean rateLimited, + final boolean inputTruncated) { super( "waf.requests", counter, @@ -411,7 +430,8 @@ public WafRequestsRawMetric( "waf_error:" + wafError, "waf_timeout:" + wafTimeout, "block_failure:" + blockFailure, - "rate_limited:" + rateLimited); + "rate_limited:" + rateLimited, + "input_truncated:" + inputTruncated); } } diff --git a/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy index 2d80c9fa3c7..3f904c82cd4 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy @@ -33,6 +33,7 @@ class WafMetricCollectorTest extends DDSpecification { WafMetricCollector.get().wafRequestError() WafMetricCollector.get().wafRequestRateLimited() WafMetricCollector.get().wafRequestBlockFailure() + WafMetricCollector.get().wafInputTruncated(TruncatedType.STRING_TOO_LONG, 5) WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION) WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION) WafMetricCollector.get().raspRuleMatch(RuleType.SQL_INJECTION) @@ -82,7 +83,8 @@ class WafMetricCollectorTest extends DDSpecification { 'waf_error:false', 'waf_timeout:false', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ].toSet() def requestTriggeredMetric = (WafMetricCollector.WafRequestsRawMetric)metrics[4] @@ -97,7 +99,8 @@ class WafMetricCollectorTest extends DDSpecification { 'waf_error:false', 'waf_timeout:false', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ].toSet() @@ -114,7 +117,8 @@ class WafMetricCollectorTest extends DDSpecification { 'waf_error:false', 'waf_timeout:false', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ].toSet() def requestTimeoutMetric = (WafMetricCollector.WafRequestsRawMetric)metrics[6] @@ -130,7 +134,8 @@ class WafMetricCollectorTest extends DDSpecification { 'waf_error:false', 'waf_timeout:true', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ].toSet() def requestWafErrorMetric = (WafMetricCollector.WafRequestsRawMetric)metrics[7] @@ -146,7 +151,8 @@ class WafMetricCollectorTest extends DDSpecification { 'waf_error:true', 'waf_timeout:false', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ].toSet() def raspRuleEvalSqli = (WafMetricCollector.RaspRuleEval)metrics[8] diff --git a/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy b/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy index 8e38f225eb2..c7400f0fb84 100644 --- a/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy +++ b/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy @@ -2,6 +2,7 @@ package datadog.telemetry.metric import datadog.telemetry.TelemetryService import datadog.telemetry.api.Metric +import datadog.trace.api.telemetry.TruncatedType import datadog.trace.api.telemetry.WafMetricCollector import datadog.trace.test.util.DDSpecification @@ -52,6 +53,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { WafMetricCollector.get().wafRequestError() WafMetricCollector.get().wafRequestRateLimited() WafMetricCollector.get().wafRequestBlockFailure() + WafMetricCollector.get().wafInputTruncated(TruncatedType.STRING_TOO_LONG, 5) WafMetricCollector.get().prepareMetrics() periodicAction.doIteration(telemetryService) @@ -72,7 +74,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'waf_error:false', 'waf_timeout:false', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -87,7 +90,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'waf_error:false', 'waf_timeout:false', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -102,7 +106,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'waf_error:false', 'waf_timeout:false', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -117,7 +122,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'waf_error:false', 'waf_timeout:true', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -132,7 +138,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'waf_error:true', 'waf_timeout:false', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ] } ) 0 * _._ @@ -146,6 +153,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { WafMetricCollector.get().wafRequestError() WafMetricCollector.get().wafRequestRateLimited() WafMetricCollector.get().wafRequestBlockFailure() + WafMetricCollector.get().wafInputTruncated(TruncatedType.STRING_TOO_LONG, 5) WafMetricCollector.get().prepareMetrics() periodicAction.doIteration(telemetryService) @@ -166,7 +174,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'waf_error:false', 'waf_timeout:false', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -181,7 +190,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'waf_error:false', 'waf_timeout:false', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -196,7 +206,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'waf_error:false', 'waf_timeout:false', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -211,7 +222,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'waf_error:false', 'waf_timeout:true', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ] } ) 1 * telemetryService.addMetric( { Metric metric -> @@ -226,7 +238,8 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { 'waf_error:true', 'waf_timeout:false', 'block_failure:true', - 'rate_limited:true' + 'rate_limited:true', + 'input_truncated:true', ] } ) 0 * _._ From 84f48b48d986f0bb1cdea8980659990c39916056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Vidal=20Dom=C3=ADnguez?= Date: Mon, 17 Feb 2025 11:18:39 +0100 Subject: [PATCH 05/12] Update latest libddwaf-java version --- dd-java-agent/appsec/build.gradle | 2 +- .../java/com/datadog/appsec/powerwaf/PowerWAFModule.java | 6 +++--- .../appsec/powerwaf/PowerWAFModuleSpecification.groovy | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dd-java-agent/appsec/build.gradle b/dd-java-agent/appsec/build.gradle index f501d453cec..31d82aec5ff 100644 --- a/dd-java-agent/appsec/build.gradle +++ b/dd-java-agent/appsec/build.gradle @@ -15,7 +15,7 @@ dependencies { implementation project(':internal-api') implementation project(':communication') implementation project(':telemetry') - implementation group: 'io.sqreen', name: 'libsqreen', version: '11.2.0' + implementation group: 'io.sqreen', name: 'libsqreen', version: '12.0.0' implementation libs.moshi testImplementation libs.bytebuddy diff --git a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java index 5ff6e9f8721..70a72f12e1e 100644 --- a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java +++ b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java @@ -465,9 +465,9 @@ public void onDataAvailable( if (!gwCtx.isRasp) { PowerwafMetrics wafMetrics = reqCtx.getWafMetrics(); if (wafMetrics != null) { - final long stringTooLong = wafMetrics.getWafInputsTruncatedStringTooLongCount(); - final long listMapTooLarge = wafMetrics.getWafInputsTruncatedListMapTooLargeCount(); - final long objectTooDeep = wafMetrics.getWafInputsTruncatedObjectTooDeepCount(); + final long stringTooLong = wafMetrics.getTruncatedStringTooLongCount(); + final long listMapTooLarge = wafMetrics.getTruncatedListMapTooLargeCount(); + final long objectTooDeep = wafMetrics.getTruncatedObjectTooDeepCount(); if (stringTooLong > 0) { WafMetricCollector.get() diff --git a/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFModuleSpecification.groovy b/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFModuleSpecification.groovy index 6c2f60a6af6..d6f98339511 100644 --- a/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFModuleSpecification.groovy +++ b/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFModuleSpecification.groovy @@ -781,9 +781,9 @@ class PowerWAFModuleSpecification extends DDSpecification { metrics.with { totalDdwafRunTimeNs = new AtomicLong(1000) totalRunTimeNs = new AtomicLong(2000) - wafInputsTruncatedStringTooLongCount = new AtomicLong(0) - wafInputsTruncatedListMapTooLargeCount = new AtomicLong(0) - wafInputsTruncatedObjectTooDeepCount = new AtomicLong(0) + truncatedStringTooLongCount = new AtomicLong(0) + truncatedListMapTooLargeCount = new AtomicLong(0) + truncatedObjectTooDeepCount = new AtomicLong(0) it } } 1 * segment.setTagTop('_dd.appsec.waf.duration', 1) From 52788f825051466f5684b4cfedfad99ca8bb2d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Vidal=20Dom=C3=ADnguez?= Date: Mon, 17 Feb 2025 11:57:21 +0100 Subject: [PATCH 06/12] Fix tests --- .../PowerWAFStatsReporterSpecification.groovy | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFStatsReporterSpecification.groovy b/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFStatsReporterSpecification.groovy index 4def8e88d83..db75e304e5c 100644 --- a/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFStatsReporterSpecification.groovy +++ b/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFStatsReporterSpecification.groovy @@ -6,6 +6,7 @@ import datadog.trace.test.util.DDSpecification import io.sqreen.powerwaf.PowerwafMetrics import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.AtomicLong class PowerWAFStatsReporterSpecification extends DDSpecification { PowerWAFStatsReporter reporter = new PowerWAFStatsReporter() @@ -14,8 +15,8 @@ class PowerWAFStatsReporterSpecification extends DDSpecification { void 'reporter reports waf timings and version'() { setup: PowerwafMetrics metrics = new PowerwafMetrics() - metrics.totalRunTimeNs = 2_000 - metrics.totalDdwafRunTimeNs = 1_000 + metrics.totalRunTimeNs = new AtomicLong(2_000) + metrics.totalDdwafRunTimeNs = new AtomicLong(1_000) TraceSegment segment = Mock() reporter.rulesVersion = '1.2.3' def wafTimeouts = 1 @@ -37,12 +38,12 @@ class PowerWAFStatsReporterSpecification extends DDSpecification { void 'reporter reports rasp timings and version'() { setup: PowerwafMetrics metrics = new PowerwafMetrics() - metrics.totalRunTimeNs = 2_000 - metrics.totalDdwafRunTimeNs = 1_000 + metrics.totalRunTimeNs = new AtomicLong(2_000) + metrics.totalDdwafRunTimeNs = new AtomicLong(1_000) PowerwafMetrics raspMetrics = new PowerwafMetrics() - raspMetrics.totalRunTimeNs = 4_000 - raspMetrics.totalDdwafRunTimeNs = 3_000 + raspMetrics.totalRunTimeNs = new AtomicLong(4_000) + raspMetrics.totalDdwafRunTimeNs = new AtomicLong(3_000) TraceSegment segment = Mock() reporter.rulesVersion = '1.2.3' def raspTimeouts = 1 From e7337760ab7332f8450549f107819395d9301d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Vidal=20Dom=C3=ADnguez?= Date: Mon, 17 Feb 2025 12:35:34 +0100 Subject: [PATCH 07/12] Update system-tests to d4974a9d88a10a70a8688afd08697affb5e82261 --- .circleci/config.continue.yml.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.continue.yml.j2 b/.circleci/config.continue.yml.j2 index 5c1729ddcac..0e6fee848ba 100644 --- a/.circleci/config.continue.yml.j2 +++ b/.circleci/config.continue.yml.j2 @@ -36,7 +36,7 @@ instrumentation_modules: &instrumentation_modules "dd-java-agent/instrumentation debugger_modules: &debugger_modules "dd-java-agent/agent-debugger|dd-java-agent/agent-bootstrap|dd-java-agent/agent-builder|internal-api|communication|dd-trace-core" profiling_modules: &profiling_modules "dd-java-agent/agent-profiling" -default_system_tests_commit: &default_system_tests_commit 315bc8a32cc888834726397f088336ba8038277f +default_system_tests_commit: &default_system_tests_commit d4974a9d88a10a70a8688afd08697affb5e82261 parameters: nightly: From 941eaf642ed08ab8bc6eb3714169e7ed9aa84d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Vidal=20Dom=C3=ADnguez?= Date: Mon, 17 Feb 2025 14:56:01 +0100 Subject: [PATCH 08/12] Change enum name to be more specific --- .../com/datadog/appsec/powerwaf/PowerWAFModule.java | 8 ++++---- .../trace/api/telemetry/WafMetricCollector.java | 10 +++++----- .../{TruncatedType.java => WafTruncatedType.java} | 4 ++-- .../trace/api/telemetry/WafMetricCollectorTest.groovy | 2 +- .../metric/WafMetricPeriodicActionSpecification.groovy | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) rename internal-api/src/main/java/datadog/trace/api/telemetry/{TruncatedType.java => WafTruncatedType.java} (77%) diff --git a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java index 70a72f12e1e..80ca577faf5 100644 --- a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java +++ b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java @@ -29,8 +29,8 @@ import datadog.trace.api.ProductTraceSource; import datadog.trace.api.gateway.Flow; import datadog.trace.api.telemetry.LogCollector; -import datadog.trace.api.telemetry.TruncatedType; import datadog.trace.api.telemetry.WafMetricCollector; +import datadog.trace.api.telemetry.WafTruncatedType; import datadog.trace.api.time.SystemTimeSource; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; @@ -471,15 +471,15 @@ public void onDataAvailable( if (stringTooLong > 0) { WafMetricCollector.get() - .wafInputTruncated(TruncatedType.STRING_TOO_LONG, stringTooLong); + .wafInputTruncated(WafTruncatedType.STRING_TOO_LONG, stringTooLong); } if (listMapTooLarge > 0) { WafMetricCollector.get() - .wafInputTruncated(TruncatedType.LIST_MAP_TOO_LARGE, listMapTooLarge); + .wafInputTruncated(WafTruncatedType.LIST_MAP_TOO_LARGE, listMapTooLarge); } if (objectTooDeep > 0) { WafMetricCollector.get() - .wafInputTruncated(TruncatedType.OBJECT_TOO_DEEP, objectTooDeep); + .wafInputTruncated(WafTruncatedType.OBJECT_TOO_DEEP, objectTooDeep); } } } diff --git a/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java b/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java index 0dc3188fbee..cfabd3d49e5 100644 --- a/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java +++ b/internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java @@ -44,7 +44,7 @@ private WafMetricCollector() { private static final AtomicRequestCounter wafBlockFailureRequestCounter = new AtomicRequestCounter(); private static final AtomicLongArray wafInputTruncatedCounter = - new AtomicLongArray(TruncatedType.values().length); + new AtomicLongArray(WafTruncatedType.values().length); private static final AtomicLongArray raspRuleEvalCounter = new AtomicLongArray(RuleType.getNumValues()); private static final AtomicLongArray raspRuleMatchCounter = @@ -127,8 +127,8 @@ public void wafRequestBlockFailure() { wafBlockFailureRequestCounter.increment(); } - public void wafInputTruncated(final TruncatedType truncatedType, long increment) { - wafInputTruncatedCounter.addAndGet(truncatedType.ordinal(), increment); + public void wafInputTruncated(final WafTruncatedType wafTruncatedType, long increment) { + wafInputTruncatedCounter.addAndGet(wafTruncatedType.ordinal(), increment); } public void raspRuleEval(final RuleType ruleType) { @@ -177,8 +177,8 @@ public void prepareMetrics() { final boolean isRateLimited = wafRateLimitedRequestCounter.getAndReset() > 0; final boolean isBlockFailure = wafBlockFailureRequestCounter.getAndReset() > 0; boolean isWafInputTruncated = false; - for (TruncatedType truncatedType : TruncatedType.values()) { - isWafInputTruncated = wafInputTruncatedCounter.getAndSet(truncatedType.ordinal(), 0) > 0; + for (WafTruncatedType wafTruncatedType : WafTruncatedType.values()) { + isWafInputTruncated = wafInputTruncatedCounter.getAndSet(wafTruncatedType.ordinal(), 0) > 0; if (isWafInputTruncated) { break; } diff --git a/internal-api/src/main/java/datadog/trace/api/telemetry/TruncatedType.java b/internal-api/src/main/java/datadog/trace/api/telemetry/WafTruncatedType.java similarity index 77% rename from internal-api/src/main/java/datadog/trace/api/telemetry/TruncatedType.java rename to internal-api/src/main/java/datadog/trace/api/telemetry/WafTruncatedType.java index a28f8b85bb1..e2051425fac 100644 --- a/internal-api/src/main/java/datadog/trace/api/telemetry/TruncatedType.java +++ b/internal-api/src/main/java/datadog/trace/api/telemetry/WafTruncatedType.java @@ -1,13 +1,13 @@ package datadog.trace.api.telemetry; -public enum TruncatedType { +public enum WafTruncatedType { STRING_TOO_LONG(1), LIST_MAP_TOO_LARGE(2), OBJECT_TOO_DEEP(4); private final int value; - TruncatedType(int value) { + WafTruncatedType(int value) { this.value = value; } diff --git a/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy index 3f904c82cd4..f42810d8d2b 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy @@ -33,7 +33,7 @@ class WafMetricCollectorTest extends DDSpecification { WafMetricCollector.get().wafRequestError() WafMetricCollector.get().wafRequestRateLimited() WafMetricCollector.get().wafRequestBlockFailure() - WafMetricCollector.get().wafInputTruncated(TruncatedType.STRING_TOO_LONG, 5) + WafMetricCollector.get().wafInputTruncated(WafTruncatedType.STRING_TOO_LONG, 5) WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION) WafMetricCollector.get().raspRuleEval(RuleType.SQL_INJECTION) WafMetricCollector.get().raspRuleMatch(RuleType.SQL_INJECTION) diff --git a/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy b/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy index c7400f0fb84..d4cc604fef9 100644 --- a/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy +++ b/telemetry/src/test/groovy/datadog/telemetry/metric/WafMetricPeriodicActionSpecification.groovy @@ -2,7 +2,7 @@ package datadog.telemetry.metric import datadog.telemetry.TelemetryService import datadog.telemetry.api.Metric -import datadog.trace.api.telemetry.TruncatedType +import datadog.trace.api.telemetry.WafTruncatedType import datadog.trace.api.telemetry.WafMetricCollector import datadog.trace.test.util.DDSpecification @@ -53,7 +53,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { WafMetricCollector.get().wafRequestError() WafMetricCollector.get().wafRequestRateLimited() WafMetricCollector.get().wafRequestBlockFailure() - WafMetricCollector.get().wafInputTruncated(TruncatedType.STRING_TOO_LONG, 5) + WafMetricCollector.get().wafInputTruncated(WafTruncatedType.STRING_TOO_LONG, 5) WafMetricCollector.get().prepareMetrics() periodicAction.doIteration(telemetryService) @@ -153,7 +153,7 @@ class WafMetricPeriodicActionSpecification extends DDSpecification { WafMetricCollector.get().wafRequestError() WafMetricCollector.get().wafRequestRateLimited() WafMetricCollector.get().wafRequestBlockFailure() - WafMetricCollector.get().wafInputTruncated(TruncatedType.STRING_TOO_LONG, 5) + WafMetricCollector.get().wafInputTruncated(WafTruncatedType.STRING_TOO_LONG, 5) WafMetricCollector.get().prepareMetrics() periodicAction.doIteration(telemetryService) From c42e81b1fb9b40ca4cd165a0e6a3cfdb4ab3a4ef Mon Sep 17 00:00:00 2001 From: "sezen.leblay" Date: Tue, 25 Feb 2025 10:25:55 +0100 Subject: [PATCH 09/12] PR --- .../main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java index 80ca577faf5..a3970604314 100644 --- a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java +++ b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java @@ -538,7 +538,7 @@ public void onDataAvailable( .getLocalRootSpan() .setTag(Tags.PROPAGATED_TRACE_SOURCE, ProductTraceSource.ASM); } else { - // If active span is not available the ASK_KEEP tag will be set in the GatewayBridge + // If active span is not available the ASM_KEEP tag will be set in the GatewayBridge // when the request ends log.debug("There is no active span available"); } From 3fd4da7809ea00bfae2048ef6d7c0c24a686f5e6 Mon Sep 17 00:00:00 2001 From: "sezen.leblay" Date: Tue, 25 Feb 2025 14:21:30 +0100 Subject: [PATCH 10/12] PR --- .../main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java index a3970604314..e0f75569274 100644 --- a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java +++ b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java @@ -542,7 +542,7 @@ public void onDataAvailable( // when the request ends log.debug("There is no active span available"); } - reqCtx.reportEvents(events); + reqCtx.reportEvents(events); } else { log.debug("Rate limited WAF events"); WafMetricCollector.get().wafRequestRateLimited(); From 4a75e653ebfae47254547ee68c9a3c9acacb0a81 Mon Sep 17 00:00:00 2001 From: "sezen.leblay" Date: Tue, 25 Feb 2025 15:02:29 +0100 Subject: [PATCH 11/12] error --- .../trace/api/telemetry/WafMetricCollectorTest.groovy | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy index f42810d8d2b..4eb8e7e6f03 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy @@ -176,7 +176,7 @@ class WafMetricCollectorTest extends DDSpecification { raspTimeout.metricName == 'rasp.timeout' raspTimeout.tags.toSet() == ['rule_type:sql_injection', 'waf_version:waf_ver1'].toSet() - def raspInvalidCode = (WafMetricCollector.RaspError)metrics[10] + def raspInvalidCode = (WafMetricCollector.RaspError)metrics[11] raspInvalidCode.type == 'count' raspInvalidCode.value == 1 raspInvalidCode.namespace == 'appsec' @@ -189,7 +189,7 @@ class WafMetricCollectorTest extends DDSpecification { 'waf_error:' + DD_WAF_RUN_INTERNAL_ERROR ].toSet() - def wafInvalidCode = (WafMetricCollector.WafError)metrics[11] + def wafInvalidCode = (WafMetricCollector.WafError)metrics[12] wafInvalidCode.type == 'count' wafInvalidCode.value == 1 wafInvalidCode.namespace == 'appsec' @@ -202,7 +202,7 @@ class WafMetricCollectorTest extends DDSpecification { 'waf_error:' +DD_WAF_RUN_INTERNAL_ERROR ].toSet() - def raspInvalidObjectCode = (WafMetricCollector.RaspError)metrics[12] + def raspInvalidObjectCode = (WafMetricCollector.RaspError)metrics[13] raspInvalidObjectCode.type == 'count' raspInvalidObjectCode.value == 1 raspInvalidObjectCode.namespace == 'appsec' @@ -214,7 +214,7 @@ class WafMetricCollectorTest extends DDSpecification { ] .toSet() - def wafInvalidObjectCode = (WafMetricCollector.WafError)metrics[13] + def wafInvalidObjectCode = (WafMetricCollector.WafError)metrics[14] wafInvalidObjectCode.type == 'count' wafInvalidObjectCode.value == 1 wafInvalidObjectCode.namespace == 'appsec' From 11680beef7ed85db9fbc8d8fd4fa2d35bd6d5626 Mon Sep 17 00:00:00 2001 From: "sezen.leblay" Date: Wed, 26 Feb 2025 11:48:41 +0100 Subject: [PATCH 12/12] have to skip tests on PowerWAFDataCallback --- dd-java-agent/appsec/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/appsec/build.gradle b/dd-java-agent/appsec/build.gradle index 31d82aec5ff..1d99176ee93 100644 --- a/dd-java-agent/appsec/build.gradle +++ b/dd-java-agent/appsec/build.gradle @@ -68,6 +68,7 @@ ext { excludedClassesCoverage = [ 'com.datadog.appsec.config.MergedAsmData.InvalidAsmDataException', 'com.datadog.appsec.powerwaf.LibSqreenInitialization', + 'com.datadog.appsec.powerwaf.PowerWAFModule.PowerWAFDataCallback', 'com.datadog.appsec.report.*', 'com.datadog.appsec.config.AppSecConfigServiceImpl.SubscribeFleetServiceRunnable.1', 'com.datadog.appsec.util.StandardizedLogging', @@ -87,7 +88,6 @@ ext { 'com.datadog.appsec.config.CurrentAppSecConfig', // equals() / hashCode() are not well covered 'com.datadog.appsec.config.AppSecConfig.Helper', - 'com.datadog.appsec.powerwaf.PowerWAFModule.PowerWAFDataCallback', 'com.datadog.appsec.powerwaf.PowerWAFModule.PowerWAFEventsCallback', // assert never fails 'com.datadog.appsec.util.StandardizedLogging',