@@ -53,6 +53,7 @@ import spock.lang.Unroll
5353import java.util.concurrent.CountDownLatch
5454import java.util.concurrent.atomic.AtomicLong
5555
56+ import static datadog.trace.api.config.AppSecConfig.APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP
5657import static datadog.trace.api.config.AppSecConfig.APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP
5758import static org.hamcrest.Matchers.hasSize
5859
@@ -688,6 +689,31 @@ class WAFModuleSpecification extends DDSpecification {
688689 wafModule. name == ' ddwaf'
689690 }
690691
692+ void ' report waf stats on first span' () {
693+ TraceSegment segment = Mock ()
694+ TraceSegmentPostProcessor pp
695+ initialRuleAdd()
696+ readyToHandle()
697+
698+ when :
699+ pp = service. traceSegmentPostProcessors. first()
700+ pp. processTraceSegment(segment, ctx, [])
701+
702+ then :
703+ 1 * segment. setTagTop(' _dd.appsec.waf.version' , _ as String )
704+ 1 * segment. setTagTop(' _dd.appsec.event_rules.loaded' , 117 )
705+ 1 * segment. setTagTop(' _dd.appsec.event_rules.error_count' , 1 )
706+ 1 * segment. setTagTop(' _dd.appsec.event_rules.errors' , { it =~ / \{ "[^"]+":\[ "bad rule"\]\} / })
707+ 1 * segment. setTagTop(' asm.keep' , true )
708+ 0 * segment. _(* _)
709+
710+ when :
711+ pp. processTraceSegment(segment, ctx, [])
712+
713+ then :
714+ 0 * segment. _(* _)
715+ }
716+
691717 void ' triggers a rule through the user agent header' () {
692718 initialRuleAdd()
693719 ChangeableFlow flow = new ChangeableFlow ()
@@ -712,7 +738,6 @@ class WAFModuleSpecification extends DDSpecification {
712738 }
713739
714740 void ' no metrics are set if waf metrics are off' () {
715- setup :
716741 metrics = null
717742 injectSysConfig(' appsec.waf.metrics' , ' false' )
718743 wafModule = new WAFModule () // replace the one created too soon
@@ -736,6 +761,39 @@ class WAFModuleSpecification extends DDSpecification {
736761 metrics == null
737762 }
738763
764+ void ' reports waf metrics' () {
765+ setup :
766+ TraceSegment segment = Mock ()
767+ TraceSegmentPostProcessor pp
768+ Flow flow = new ChangeableFlow ()
769+ initialRuleAdd()
770+ readyToHandle()
771+
772+ when :
773+ pp = service. traceSegmentPostProcessors[1 ]
774+ dataListener. onDataAvailable(flow, ctx, ATTACK_BUNDLE , gwCtx)
775+ ctx. closeWafContext()
776+ pp. processTraceSegment(segment, ctx, [])
777+
778+ then :
779+ 1 * ctx. getOrCreateWafContext(_, false )
780+ 1 * ctx. closeWafContext()
781+ 3 * ctx. getWafMetrics() >> {
782+ metrics. with {
783+ totalDdwafRunTimeNs = new AtomicLong (1000 )
784+ totalRunTimeNs = new AtomicLong (2000 )
785+ truncatedStringTooLongCount = new AtomicLong (0 )
786+ truncatedListMapTooLargeCount = new AtomicLong (0 )
787+ truncatedObjectTooDeepCount = new AtomicLong (0 )
788+ it } }
789+
790+ 1 * segment. setTagTop(' _dd.appsec.waf.duration' , 1 )
791+ 1 * segment. setTagTop(' _dd.appsec.waf.duration_ext' , 2 )
792+ 1 * segment. setTagTop(' _dd.appsec.event_rules.version' , ' 0.42.0' )
793+
794+ 0 * segment. _(* _)
795+ }
796+
739797 void ' can trigger a nonwafContext waf run' () {
740798 initialRuleAdd()
741799 ChangeableFlow flow = new ChangeableFlow ()
@@ -756,7 +814,6 @@ class WAFModuleSpecification extends DDSpecification {
756814 }
757815
758816 void ' reports events' () {
759- setup :
760817 initialRuleAdd()
761818 AppSecEvent event
762819 StackTraceEvent stackTrace
@@ -816,6 +873,27 @@ class WAFModuleSpecification extends DDSpecification {
816873 event. ruleMatches[0 ]. parameters[0 ]. value == ' <Redacted>'
817874 }
818875
876+ void ' disabling of key regex' () {
877+ injectSysConfig(APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP , ' ' )
878+ initialRuleAdd()
879+ readyToHandle()
880+ AppSecEvent event
881+
882+ when :
883+ def bundle = MapDataBundle . of(KnownAddresses . HEADERS_NO_COOKIES ,
884+ new CaseInsensitiveMap<List<String > > ([' user-agent' : [password : ' Arachni/v0' ]]))
885+ dataListener. onDataAvailable(Stub (ChangeableFlow ), ctx, bundle, gwCtx)
886+ ctx. closeWafContext()
887+
888+ then :
889+ ctx. reportEvents(_ as Collection<AppSecEvent > ) >> { event = it[0 ]. iterator(). next() }
890+
891+ event. ruleMatches[0 ]. parameters[0 ]. address == ' server.request.headers.no_cookies'
892+ event. ruleMatches[0 ]. parameters[0 ]. highlight == [' Arachni/v' ]
893+ event. ruleMatches[0 ]. parameters[0 ]. key_path == [' user-agent' , ' password' ]
894+ event. ruleMatches[0 ]. parameters[0 ]. value == ' Arachni/v0'
895+ }
896+
819897 void ' redaction of values' () {
820898 injectSysConfig(APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP , ' Arachni' )
821899
@@ -880,21 +958,43 @@ class WAFModuleSpecification extends DDSpecification {
880958 ! flow. blocking
881959 }
882960
883- void ' powerwaf exceptions do not propagate' () {
961+ void ' waf exceptions do not propagate' () {
884962 initialRuleAdd()
963+ readyToHandle()
885964 ChangeableFlow flow = new ChangeableFlow ()
886965 DataBundle db = MapDataBundle . of(KnownAddresses . HEADERS_NO_COOKIES , [get : { null }] as List )
887966
888967 when :
968+ dataListener. onDataAvailable(flow, ctx, db, gwCtx)
969+
970+ then :
971+ 1 * ctx. getOrCreateWafContext(_, false )
972+ 2 * ctx. getWafMetrics()
973+ 1 * ctx. setWafErrors()
974+ 1 * wafMetricCollector. wafErrorCode(-127 )
975+ 2 * ctx. isWafContextClosed()
976+ assert ! flow. blocking
977+ }
978+
979+ void 'timeout is honored (waf )' () {
980+ injectSysConfig(' appsec. waf. timeout' , ' 1 ' )
981+ WAFModule.createLimitsObject()
982+ initialRuleAdd()
889983 readyToHandle()
984+ DataBundle db = MapDataBundle.of(KnownAddresses.HEADERS_NO_COOKIES,
985+ new CaseInsensitiveMap<List<String>>([' user- agent' : ' Arachni / v' + (' a' * 4000)]))
986+ ChangeableFlow flow = new ChangeableFlow()
987+
988+ TraceSegment segment = Mock()
989+ TraceSegmentPostProcessor pp = service.traceSegmentPostProcessors.last()
990+
991+ when:
890992 dataListener.onDataAvailable(flow, ctx, db, gwCtx)
891993
892994 then:
893- 1 * ctx. getOrCreateWafContext(_, _)
894995 assert !flow.blocking
895996 1 * ctx.isWafContextClosed()
896- 1 * ctx. getOrCreateWafContext(_, true , false ) >> {
897- wafContext = it[0 ]. openContext() }
997+ 1 * ctx.getOrCreateWafContext(_, false)
898998 2 * ctx.getWafMetrics()
899999 1 * ctx.increaseWafTimeouts()
9001000 0 * _
@@ -912,12 +1012,12 @@ class WAFModuleSpecification extends DDSpecification {
9121012 }
9131013
9141014 void ' timeout is honored (rasp)' () {
915- setup:
9161015 injectSysConfig(' appsec. waf. timeout' , ' 1 ' )
9171016 WAFModule.createLimitsObject()
918- setupWithStubConfigService()
1017+ initialRuleAdd()
1018+ readyToHandle()
9191019 DataBundle db = MapDataBundle.of(KnownAddresses.HEADERS_NO_COOKIES,
920- new CaseInsensitiveMap<List<String>>([' user- agent' : ' Arachni / v' + (' a' * 4000)]))
1020+ new CaseInsensitiveMap<List<String>>([' user- agent' : ' Arachni / v' + (' a' * 4000)]))
9211021 ChangeableFlow flow = new ChangeableFlow()
9221022
9231023 TraceSegment segment = Mock()
@@ -929,12 +1029,9 @@ class WAFModuleSpecification extends DDSpecification {
9291029 dataListener.onDataAvailable(flow, ctx, db, gwCtx)
9301030
9311031 then:
932- ctx.getOrCreateWafContext(_, true) >> {
933- wafContext = it[0].openContext() }
9341032 assert !flow.blocking
9351033 1 * ctx.isWafContextClosed()
936- 1 * ctx.getOrCreateWafContext(_, true, true) >> {
937- wafContext = it[0].openContext() }
1034+ 1 * ctx.getOrCreateWafContext(_, true)
9381035 1 * ctx.getRaspMetrics()
9391036 1 * ctx.getRaspMetricsCounter()
9401037 1 * ctx.increaseRaspTimeouts()
@@ -1412,7 +1509,6 @@ class WAFModuleSpecification extends DDSpecification {
14121509 }
14131510
14141511 void ' honors appsec.trace.rate.limit' () {
1415- setup :
14161512 injectSysConfig(' dd.appsec.trace.rate.limit' , ' 5' )
14171513 def monitoring = Mock (Monitoring )
14181514
@@ -1579,7 +1675,6 @@ class WAFModuleSpecification extends DDSpecification {
15791675 }
15801676
15811677 void ' raspRuleSkipped if rasp available and WAF context is closed' () {
1582- setup :
15831678 ChangeableFlow flow = Mock ()
15841679 GatewayContext gwCtxMock = new GatewayContext (false , RuleType . SQL_INJECTION )
15851680
@@ -1603,7 +1698,6 @@ class WAFModuleSpecification extends DDSpecification {
16031698 }
16041699
16051700 void ' test raspErrorCode metric is increased when waf call throws #wafErrorCode ' () {
1606- setup :
16071701 ChangeableFlow flow = Mock ()
16081702 GatewayContext gwCtxMock = new GatewayContext (false , RuleType . SQL_INJECTION )
16091703 WafContext wafContext = Mock ()
@@ -1634,7 +1728,6 @@ class WAFModuleSpecification extends DDSpecification {
16341728 }
16351729
16361730 void ' test wafErrorCode metric is increased when waf call throws #wafErrorCode ' () {
1637- setup :
16381731 ChangeableFlow flow = Mock ()
16391732 WafContext wafContext = Mock ()
16401733
@@ -1672,11 +1765,11 @@ class WAFModuleSpecification extends DDSpecification {
16721765 }
16731766
16741767 void ' report waf stats on first span' () {
1675- setup :
16761768 TraceSegment segment = Mock ()
1769+ initialRuleAdd()
1770+ readyToHandle()
16771771
16781772 when :
1679- initialRuleAdd()
16801773 service. traceSegmentPostProcessors. first(). processTraceSegment(segment, ctx, [])
16811774
16821775 then :
0 commit comments