Skip to content

Commit becbf2a

Browse files
Create metric: appsec.waf.config_errors (#8394)
1 parent d6d3d21 commit becbf2a

File tree

4 files changed

+98
-1
lines changed

4 files changed

+98
-1
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import datadog.trace.api.ProductActivation;
4949
import datadog.trace.api.UserIdCollectionMode;
5050
import datadog.trace.api.telemetry.LogCollector;
51+
import datadog.trace.api.telemetry.WafMetricCollector;
5152
import java.io.ByteArrayInputStream;
5253
import java.io.FileInputStream;
5354
import java.io.FileNotFoundException;
@@ -263,7 +264,9 @@ private void handleWafUpdateResultReport(String configKey, Map<String, Object> r
263264
"Invalid rule during waf config update for config key {}: {}",
264265
configKey,
265266
e.wafDiagnostics);
266-
267+
if (e.wafDiagnostics.getNumConfigError() > 0) {
268+
WafMetricCollector.get().addWafConfigError(e.wafDiagnostics.getNumConfigError());
269+
}
267270
// TODO: Propagate diagostics back to remote config apply_error
268271

269272
initReporter.setReportForPublication(e.wafDiagnostics);

dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/config/AppSecConfigServiceImplSpecification.groovy

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import datadog.remoteconfig.state.ProductListener
1313
import datadog.trace.api.Config
1414
import datadog.trace.api.ProductActivation
1515
import datadog.trace.api.UserIdCollectionMode
16+
import datadog.trace.api.telemetry.WafMetricCollector
1617
import datadog.trace.test.util.DDSpecification
1718

1819
import java.nio.file.Files
@@ -50,6 +51,7 @@ class AppSecConfigServiceImplSpecification extends DDSpecification {
5051
AppSecModuleConfigurer.Reconfiguration reconf = Stub()
5152
AppSecConfigServiceImpl appSecConfigService
5253
SavedListeners listeners
54+
protected static final ORIGINAL_METRIC_COLLECTOR = WafMetricCollector.get()
5355

5456
void cleanup() {
5557
appSecConfigService?.close()
@@ -712,6 +714,51 @@ class AppSecConfigServiceImplSpecification extends DDSpecification {
712714
AppSecSystem.active = true
713715
}
714716

717+
void 'InvalidRuleSetException is thrown when rules are not configured correctly' () {
718+
setup:
719+
// Mock WafMetricCollector
720+
WafMetricCollector wafMetricCollector = Mock(WafMetricCollector)
721+
WafMetricCollector.INSTANCE = wafMetricCollector
722+
723+
// Create a temporary file with invalid WAF configuration
724+
Path p = Files.createTempFile('appsec', '.json')
725+
p.toFile() << '''{
726+
"version": "2.2",
727+
"rules": [
728+
{
729+
"id": "invalid-rule",
730+
"name": "Invalid Rule",
731+
"tags": {
732+
"type": "invalid_type",
733+
"category": "invalid_category"
734+
},
735+
"conditions": [
736+
{
737+
"operator": "invalid_operator",
738+
"parameters": {
739+
"invalid_param": "invalid_value"
740+
}
741+
}
742+
],
743+
"type": "invalid_type",
744+
"data": []
745+
}
746+
]
747+
}'''
748+
749+
when:
750+
appSecConfigService.init()
751+
752+
then:
753+
1 * config.getAppSecRulesFile() >> (p as String)
754+
1 * wafMetricCollector.addWafConfigError(_ as Integer)
755+
thrown RuntimeException
756+
757+
cleanup:
758+
WafMetricCollector.INSTANCE = ORIGINAL_METRIC_COLLECTOR
759+
p.toFile().delete()
760+
}
761+
715762
private static AppSecFeatures autoUserInstrum(String mode) {
716763
return new AppSecFeatures().tap { features ->
717764
features.autoUserInstrum = new AppSecFeatures.AutoUserInstrum().tap { instrum ->

internal-api/src/main/java/datadog/trace/api/telemetry/WafMetricCollector.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ private WafMetricCollector() {
5959
new AtomicLongArray(LoginFramework.getNumValues());
6060
private static final AtomicLongArray appSecSdkEventQueue =
6161
new AtomicLongArray(LoginEvent.getNumValues() * LoginVersion.getNumValues());
62+
private static final AtomicInteger wafConfigErrorCounter = new AtomicInteger();
6263

6364
/** WAF version that will be initialized with wafInit and reused for all metrics. */
6465
private static String wafVersion = "";
@@ -353,6 +354,16 @@ public void prepareMetrics() {
353354
}
354355
}
355356
}
357+
358+
// WAF config errors
359+
int configErrors = wafConfigErrorCounter.getAndSet(0);
360+
if (configErrors > 0) {
361+
if (!rawMetricsQueue.offer(
362+
new WafConfigError(
363+
configErrors, WafMetricCollector.wafVersion, WafMetricCollector.rulesVersion))) {
364+
return;
365+
}
366+
}
356367
}
357368

358369
public abstract static class WafMetric extends MetricCollector.Metric {
@@ -448,6 +459,20 @@ public WafRequestsRawMetric(
448459
}
449460
}
450461

462+
public void addWafConfigError(int nbErrors) {
463+
wafConfigErrorCounter.addAndGet(nbErrors);
464+
}
465+
466+
public static class WafConfigError extends WafMetric {
467+
public WafConfigError(final long counter, final String wafVersion, final String rulesVersion) {
468+
super(
469+
"waf.config_errors",
470+
counter,
471+
"waf_version:" + wafVersion,
472+
"event_rules_version:" + rulesVersion);
473+
}
474+
}
475+
451476
public static class RaspRuleEval extends WafMetric {
452477
public RaspRuleEval(final long counter, final RuleType ruleType, final String wafVersion) {
453478
super(

internal-api/src/test/groovy/datadog/trace/api/telemetry/WafMetricCollectorTest.groovy

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,28 @@ class WafMetricCollectorTest extends DDSpecification {
485485
[stringTooLong, listMapTooLarge, objectTooDeep] << allBooleanCombinations(3)
486486
}
487487

488+
void 'test waf config error metrics'() {
489+
given:
490+
def collector = WafMetricCollector.get()
491+
492+
when:
493+
collector.wafInit('waf_ver1', 'rules.1', true)
494+
collector.addWafConfigError(5)
495+
collector.addWafConfigError(3)
496+
collector.prepareMetrics()
497+
498+
then:
499+
def metrics = collector.drain()
500+
def configErrorMetrics = metrics.findAll { it.metricName == 'waf.config_errors' }
501+
502+
final metric = configErrorMetrics[0]
503+
metric.type == 'count'
504+
metric.metricName == 'waf.config_errors'
505+
metric.namespace == 'appsec'
506+
metric.value == 8
507+
metric.tags.toSet() == ['waf_version:waf_ver1', 'event_rules_version:rules.1'].toSet()
508+
}
509+
488510
/**
489511
* Helper method to generate all combinations of n boolean values.
490512
*/

0 commit comments

Comments
 (0)