diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 414e5198c3b..5040e247139 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -128,6 +128,8 @@ jobs:
weblog: spring-boot-payara
- library: java
weblog: akka-http
+ - library: java
+ weblog: play
- library: nodejs
weblog: express4
- library: nodejs
diff --git a/manifests/java.yml b/manifests/java.yml
index 313860e3058..ace4bd61824 100644
--- a/manifests/java.yml
+++ b/manifests/java.yml
@@ -25,6 +25,7 @@ tests/:
'*': v1.1.0
akka-http: v1.12.0
jersey-grizzly2: v1.11.0
+ play: missing_feature
ratpack: missing_feature
resteasy-netty3: v1.11.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -42,6 +43,7 @@ tests/:
TestInsecureCookie:
'*': v1.18.0
akka-http: missing_feature
+ play: missing_feature
ratpack: missing_feature
spring-boot-3-native: missing_feature
test_ldap_injection.py:
@@ -49,6 +51,7 @@ tests/:
'*': v1.7.0
akka-http: v1.12.0
jersey-grizzly2: v1.11.0
+ play: missing_feature
ratpack: missing_feature
resteasy-netty3: v1.11.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -58,12 +61,14 @@ tests/:
TestNoHttponlyCookie:
'*': v1.18.0
akka-http: missing_feature
+ play: missing_feature
ratpack: missing_feature
spring-boot-3-native: missing_feature
test_no_samesite_cookie.py:
TestNoSamesiteCookie:
'*': v1.18.0
akka-http: missing_feature
+ play: missing_feature
ratpack: missing_feature
spring-boot-3-native: missing_feature
test_nosql_mongodb_injection.py:
@@ -73,6 +78,7 @@ tests/:
'*': v1.1.0
akka-http: v1.12.0
jersey-grizzly2: v1.11.0
+ play: missing_feature
ratpack: missing_feature
resteasy-netty3: v1.11.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -82,6 +88,7 @@ tests/:
'*': v1.1.0
akka-http: v1.12.0
jersey-grizzly2: v1.11.0
+ play: missing_feature
ratpack: missing_feature
resteasy-netty3: v1.11.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -90,6 +97,7 @@ tests/:
TestSSRF:
'*': v1.14.0
akka-http: missing_feature (No endpoint implemented)
+ play: missing_feature (No endpoint implemented)
ratpack: missing_feature (No endpoint implemented)
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
vertx4: missing_feature (No endpoint implemented)
@@ -106,6 +114,7 @@ tests/:
TestUnvalidatedRedirect:
'*': v1.16.0
akka-http: missing_feature
+ play: missing_feature
ratpack: missing_feature
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-jetty: v1.17.0
@@ -115,6 +124,7 @@ tests/:
'*': v1.16.0
akka-http: irrelevant (No forward)
jersey-grizzly2: irrelevant (No forward)
+ play: missing_feature (No endpoint implemented)
ratpack: irrelevant (No forward)
resteasy-netty3: irrelevant (No forward)
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -122,14 +132,17 @@ tests/:
test_weak_cipher.py:
TestWeakCipher:
'*': v0.108.0
+ play: missing_feature (no endpoint)
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
test_weak_hash.py:
TestWeakHash:
'*': v0.108.0
+ play: missing_feature (no endpoint)
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
test_weak_randomness.py:
TestWeakRandomness:
'*': v1.15.0
+ play: missing_feature (no endpoint)
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
test_xcontent_sniffing.py:
Test_XContentSniffing:
@@ -142,6 +155,7 @@ tests/:
test_xpath_injection.py:
TestXPathInjection:
'*': v1.18.0
+ play: missing_feature
ratpack: missing_feature
spring-boot-3-native: missing_feature
test_xss.py:
@@ -160,6 +174,7 @@ tests/:
'*': v1.7.0
akka-http: v1.12.0
jersey-grizzly2: missing_feature
+ play: missing_feature
ratpack: missing_feature
resteasy-netty3: missing_feature
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -169,6 +184,7 @@ tests/:
TestCookieName:
'*': v1.5.0
akka-http: v1.12.0
+ play: missing_feature
ratpack: missing_feature
resteasy-netty3: missing_feature
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -179,6 +195,7 @@ tests/:
'*': v1.5.0
akka-http: v1.12.0
jersey-grizzly2: v1.11.0
+ play: missing_feature
ratpack: missing_feature
resteasy-netty3: v1.11.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -189,6 +206,7 @@ tests/:
'*': v1.5.0
akka-http: v1.12.0
jersey-grizzly2: v1.15.0
+ play: missing_feature
ratpack: missing_feature
resteasy-netty3: missing_feature
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -199,6 +217,7 @@ tests/:
'*': v1.5.0
akka-http: v1.12.0
jersey-grizzly2: v1.11.0
+ play: missing_feature
ratpack: missing_feature
resteasy-netty3: v1.11.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -218,6 +237,7 @@ tests/:
'*': v1.5.0
akka-http: v1.12.0
jersey-grizzly2: v1.15.0
+ play: missing_feature
ratpack: missing_feature
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
vertx3: v1.12.0
@@ -227,6 +247,7 @@ tests/:
'*': v1.5.0
akka-http: v1.12.0
jersey-grizzly2: v1.11.0
+ play: missing_feature
ratpack: missing_feature
resteasy-netty3: v1.11.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -246,7 +267,8 @@ tests/:
test_addresses.py:
Test_BodyJson:
'*': v0.95.1
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
ratpack: v0.99.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
@@ -255,12 +277,14 @@ tests/:
4.0.0)
Test_BodyRaw:
'*': missing_feature
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_BodyUrlEncoded:
'*': v0.95.1
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
ratpack: v0.99.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
@@ -268,7 +292,8 @@ tests/:
vertx3: v0.99.0
Test_BodyXml:
'*': v0.95.1
- akka-http: missing_feature (No AppSec support)
+ akka-http: missing_feature (no built-in XML unmarshalling to instrument)
+ play: v1.22.0
ratpack: v0.99.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
@@ -277,22 +302,24 @@ tests/:
4.0.0)
Test_ClientIP: missing_feature
Test_Cookies:
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_FullGrpc: missing_feature
Test_GraphQL: missing_feature
Test_Headers:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_Lambda: missing_feature
Test_Method: missing_feature
Test_PathParams:
'*': v0.95.1
- akka-http: missing_feature (No AppSec support)
+ akka-http: missing_feature (unclear how to implement; matching doesn't happen in one go)
jersey-grizzly2: missing_feature
+ play: v1.22.0
ratpack: v0.99.0
resteasy-netty3: missing_feature
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -300,28 +327,28 @@ tests/:
vertx3: v0.99.0
Test_ResponseStatus:
'*': v0.88.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_UrlQuery:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_UrlQueryKey:
'*': v0.100.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_UrlRaw:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_gRPC:
'*': v0.96.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: irrelevant
jersey-grizzly2: irrelevant
+ play: irrelevant
ratpack: irrelevant
resteasy-netty3: irrelevant
spring-boot: v0.109.0 # APPSEC-5426
@@ -332,7 +359,9 @@ tests/:
test_blocking.py:
Test_Blocking:
'*': missing_feature
+ akka-http: v1.22.0
jersey-grizzly2: v1.7.0
+ play: v1.22.0
ratpack: v1.7.0
resteasy-netty3: v1.7.0
spring-boot: v0.112.0
@@ -346,7 +375,8 @@ tests/:
vertx3: v1.7.0
Test_CustomBlockingResponse:
'*': v1.11.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
test_custom_rules.py:
@@ -354,111 +384,112 @@ tests/:
test_exclusions.py:
Test_Exclusions:
'*': v1.6.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
test_miscs.py:
Test_404:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_CorrectOptionProcessing:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_MultipleAttacks:
'*': v0.92.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_MultipleHighlight:
'*': v0.95.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_NoWafTimeout:
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-payara: missing_feature (No AppSec support)
test_reports.py:
Test_Monitoring:
'*': v0.100.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
test_rules.py:
Test_CommandInjection:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_DiscoveryScan:
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_HttpProtocol:
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_JavaCodeInjection:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_JsInjection:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_LFI:
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_NoSqli:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_PhpCodeInjection:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_RFI:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_SQLI:
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_SSRF:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_Scanners:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_XSS:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
test_telemetry.py:
Test_TelemetryMetrics:
'*': v1.12.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
test_PII.py:
Test_Scrubbing: missing_feature
test_alpha.py:
Test_Basic:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
test_automated_login_events.py:
@@ -467,8 +498,9 @@ tests/:
test_blocking_addresses.py:
Test_BlockingAddresses:
'*': missing_feature
- akka-http: missing_feature (Missing support)
+ akka-http: v1.22.0
jersey-grizzly2: v1.7.0
+ play: v1.22.0
ratpack: v1.6.0
resteasy-netty3: v1.7.0
spring-boot: v0.111.0 # initially v0.110.0, but was bugged
@@ -484,13 +516,15 @@ tests/:
Test_BlockingGraphqlResolvers: missing_feature
Test_Blocking_request_body:
'*': v1.15.0
- akka-http: missing_feature (Missing support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (Missing support)
Test_Blocking_request_cookies:
'*': missing_feature
- akka-http: missing_feature (Missing support)
+ akka-http: v1.22.0
jersey-grizzly2: v1.7.0
+ play: v1.22.0
ratpack: v1.6.0
resteasy-netty3: v1.7.0
spring-boot: v0.110.0
@@ -503,8 +537,9 @@ tests/:
vertx4: v1.7.0
Test_Blocking_request_headers:
'*': missing_feature
- akka-http: missing_feature (Missing support)
+ akka-http: v1.22.0
jersey-grizzly2: v1.7.0
+ play: v1.22.0
ratpack: v1.6.0
resteasy-netty3: v1.7.0
spring-boot: v0.110.0
@@ -517,8 +552,9 @@ tests/:
vertx4: v1.7.0
Test_Blocking_request_method:
'*': missing_feature
- akka-http: missing_feature (Missing support)
+ akka-http: v1.22.0
jersey-grizzly2: v1.7.0
+ play: v1.22.0
ratpack: v1.6.0
resteasy-netty3: v1.7.0
spring-boot: v0.110.0
@@ -530,13 +566,15 @@ tests/:
vertx4: v1.7.0
Test_Blocking_request_path_params:
'*': v1.15.0
- akka-http: missing_feature (Missing support)
+ akka-http: missing_feature (path parameters not suported)
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (Missing support)
Test_Blocking_request_query:
'*': missing_feature
- akka-http: missing_feature (Missing support)
+ akka-http: v1.22.0
jersey-grizzly2: v1.7.0
+ play: v1.22.0
ratpack: v1.6.0
resteasy-netty3: v1.7.0
spring-boot: v0.110.0
@@ -549,8 +587,9 @@ tests/:
vertx4: v1.7.0
Test_Blocking_request_uri:
'*': missing_feature
- akka-http: missing_feature (Missing support)
+ akka-http: v1.22.0
jersey-grizzly2: v1.7.0
+ play: v1.22.0
ratpack: v1.6.0
resteasy-netty3: v1.7.0
spring-boot: v0.110.0
@@ -562,12 +601,14 @@ tests/:
vertx4: v1.7.0
Test_Blocking_response_headers:
'*': missing_feature
- akka-http: missing_feature (Missing support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (Missing support)
Test_Blocking_response_status:
'*': missing_feature
- akka-http: missing_feature (Missing support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (Missing support)
test_client_ip.py:
@@ -575,35 +616,39 @@ tests/:
test_conf.py:
Test_ConfigurationVariables:
'*': v0.100.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_RuleSet_1_3_1:
'*': v0.99.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_StaticRuleSet:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
test_customconf.py:
Test_ConfRuleSet:
'*': v0.93.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
Test_CorruptedRules:
'*': v0.93.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
Test_MissingRules:
'*': v0.93.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
Test_NoLimitOnWafRules:
'*': v0.97.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
test_docs.py:
Test_InstallationDebugProcedure:
@@ -616,6 +661,7 @@ tests/:
Test_Ognl:
akka-http: missing_feature (Need to build endpoint on weblog)
jersey-grizzly2: missing_feature (Need to build endpoint on weblog)
+ play: missing_feature (Need to build endpoint on weblog)
ratpack: missing_feature (Need to build endpoint on weblog)
resteasy-netty3: missing_feature (Need to build endpoint on weblog)
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -625,6 +671,7 @@ tests/:
Test_Sqli:
akka-http: missing_feature (Need to build endpoint on weblog)
jersey-grizzly2: missing_feature (Need to build endpoint on weblog)
+ play: missing_feature (Need to build endpoint on weblog)
ratpack: missing_feature (Need to build endpoint on weblog)
resteasy-netty3: missing_feature (Need to build endpoint on weblog)
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
@@ -635,14 +682,17 @@ tests/:
test_event_tracking.py:
Test_CustomEvent:
'*': v1.8.0
+ play: v1.22.0
spring-boot-3-native: irrelevant (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_UserLoginFailureEvent:
'*': v1.8.0
+ play: v1.22.0
spring-boot-3-native: irrelevant (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_UserLoginSuccessEvent:
'*': v1.8.0
+ play: v1.22.0
spring-boot-3-native: irrelevant (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
test_identify.py:
@@ -660,45 +710,49 @@ tests/:
# vertx3: v1.7.0
test_logs.py:
Test_Standardization:
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_StandardizationBlockMode: missing_feature
test_rate_limiter.py:
Test_Main:
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
test_reports.py:
Test_AttackTimestamp:
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-payara: missing_feature (No AppSec support)
Test_ExtraTagsFromRule: v1.22.0 # Supported since v1.22.0
Test_HttpClientIP:
'*': v0.98.1
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_Info:
'*': v0.87.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_RequestHeaders:
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_StatusCode:
'*': v0.92.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_TagsFromRule:
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-payara: missing_feature (No AppSec support)
test_request_blocking.py:
Test_AppSecRequestBlocking:
'*': missing_feature
+ akka-http: v1.22.0
jersey-grizzly2: v1.9.0
+ play: v1.22.0
ratpack: v1.9.0
resteasy-netty3: v1.9.0
spring-boot: v1.9.0
@@ -709,27 +763,30 @@ tests/:
test_runtime_activation.py:
Test_RuntimeActivation:
'*': v0.115.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
test_traces.py:
Test_AppSecEventSpanTags:
'*': v0.104.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_AppSecObfuscator:
'*': v0.113.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_CollectRespondHeaders:
'*': v0.102.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
Test_RetainTraces:
'*': v0.92.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
spring-boot-payara: missing_feature (No AppSec support)
test_user_blocking_full_denylist.py:
@@ -844,7 +901,7 @@ tests/:
Test_HeaderTagsShortFormat: v0.102.0
test_profiling.py:
Test_Profile:
- akka-http: missing_feature (Endpoint not implemented)
+ akka-http: v1.22.0
spring-boot-3-native: missing_feature (Tracing-only)
vertx3: missing_feature (Endpoint not implemented)
vertx4: missing_feature (Endpoint not implemented)
@@ -852,6 +909,7 @@ tests/:
Test_SamplingDecisions:
akka-http: missing_feature (Endpoint /sample_rate_route not implemented)
jersey-grizzly2: missing_feature (Endpoint /sample_rate_route not implemented)
+ play: missing_feature (Endpoint /sample_rate_route not implemented)
ratpack: missing_feature (Endpoint /sample_rate_route not implemented)
resteasy-netty3: missing_feature (Endpoint /sample_rate_route not implemented)
vertx3: missing_feature (Endpoint /sample_rate_route not implemented)
@@ -859,11 +917,14 @@ tests/:
test_scrubbing.py:
Test_UrlQuery: v0.107.1
test_semantic_conventions.py:
+ Test_Meta:
+ play: irrelevant (top span is akka-http-server)
Test_MetricsStandardTags: v1.6.0
test_standard_tags.py:
Test_StandardTagsClientIp:
'*': v0.114.0
- akka-http: missing_feature (No AppSec support)
+ akka-http: v1.22.0
+ play: v1.22.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
Test_StandardTagsMethod: v0.102.0
Test_StandardTagsRoute:
@@ -886,4 +947,3 @@ tests/:
'*': v0.108.1
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
Test_TelemetryV2: missing_feature
-
diff --git a/tests/appsec/test_blocking_addresses.py b/tests/appsec/test_blocking_addresses.py
index a601c6c8d4c..e6db3835c40 100644
--- a/tests/appsec/test_blocking_addresses.py
+++ b/tests/appsec/test_blocking_addresses.py
@@ -65,6 +65,9 @@ def setup_path_params(self):
context.library < "java@1.15.0", reason="When supported, path parameter detection happens on subsequent WAF run"
)
@missing_feature(library="nodejs", reason="Not supported yet")
+ @missing_feature(
+ context.library == "java" and context.weblog_variant == "akka-http", reason="path parameters not supported"
+ )
@irrelevant(context.library == "ruby" and context.weblog_variant == "rack")
@irrelevant(context.library == "golang" and context.weblog_variant == "net-http")
def test_path_params(self):
@@ -142,7 +145,15 @@ def setup_response_status(self):
@missing_feature(
context.library == "java"
and context.weblog_variant
- not in ("spring-boot", "uds-spring-boot", "spring-boot-jetty", "spring-boot-undertow", "spring-boot-wildfly")
+ not in (
+ "akka-http",
+ "play",
+ "spring-boot",
+ "uds-spring-boot",
+ "spring-boot-jetty",
+ "spring-boot-undertow",
+ "spring-boot-wildfly",
+ )
)
@missing_feature(context.library == "golang", reason="No blocking on server.response.*")
@missing_feature(context.library < "ruby@1.10.0")
@@ -156,7 +167,10 @@ def test_response_status(self):
def setup_not_found(self):
self.rnf_req = weblog.get(path="/finger_print")
- @missing_feature(context.library == "java", reason="Happens on a subsequent WAF run")
+ @missing_feature(
+ context.library == "java" and context.weblog_variant not in ("akka-http", "play"),
+ reason="Happens on a subsequent WAF run",
+ )
@missing_feature(context.library == "ruby", reason="Not working")
@missing_feature(library="nodejs", reason="Not supported yet")
@missing_feature(context.library == "golang", reason="No blocking on server.response.*")
@@ -170,7 +184,10 @@ def setup_response_header(self):
self.rsh_req = weblog.get(path="/headers")
@missing_feature(context.library < "dotnet@2.32.0")
- @missing_feature(context.library == "java", reason="Happens on a subsequent WAF run")
+ @missing_feature(
+ context.library == "java" and context.weblog_variant not in ("akka-http", "play"),
+ reason="Happens on a subsequent WAF run",
+ )
@missing_feature(context.library == "ruby")
@missing_feature(context.library == "php", reason="Headers already sent at this stage")
@missing_feature(library="nodejs", reason="Not supported yet")
@@ -502,7 +519,7 @@ def setup_non_blocking_plain_text(self):
)
@irrelevant(
- context.weblog_variant in ("jersey-grizzly2", "resteasy-netty3"),
+ context.weblog_variant in ("akka-http", "play", "jersey-grizzly2", "resteasy-netty3"),
reason="Blocks on text/plain if parsed to a String",
)
def test_non_blocking_plain_text(self):
diff --git a/tests/appsec/waf/test_blocking.py b/tests/appsec/waf/test_blocking.py
index 4b8aa73a539..bf486432313 100644
--- a/tests/appsec/waf/test_blocking.py
+++ b/tests/appsec/waf/test_blocking.py
@@ -128,7 +128,7 @@ def setup_accept_partial_html(self):
def test_accept_partial_html(self):
"""Blocking with Accept: text/*"""
assert self.r_aph.status_code == 403
- assert self.r_aph.headers.get("content-type", "") in HTML_CONTENT_TYPES
+ assert self.r_aph.headers.get("content-type", "").lower() in HTML_CONTENT_TYPES
assert self.r_aph.text in BLOCK_TEMPLATE_HTML_ANY
def setup_accept_full_json(self):
@@ -145,7 +145,7 @@ def setup_accept_full_json(self):
def test_accept_full_json(self):
"""Blocking with Accept: application/json"""
assert self.r_afj.status_code == 403
- assert self.r_afj.headers.get("content-type", "") in JSON_CONTENT_TYPES
+ assert self.r_afj.headers.get("content-type", "").lower() in JSON_CONTENT_TYPES
assert self.r_afj.text in BLOCK_TEMPLATE_JSON_ANY
def setup_accept_full_html(self):
@@ -165,7 +165,7 @@ def setup_accept_full_html(self):
def test_accept_full_html(self):
"""Blocking with Accept: text/html"""
assert self.r_afh.status_code == 403
- assert self.r_afh.headers.get("content-type", "") in HTML_CONTENT_TYPES
+ assert self.r_afh.headers.get("content-type", "").lower() in HTML_CONTENT_TYPES
assert self.r_afh.text in BLOCK_TEMPLATE_HTML_ANY
def setup_json_template_v1(self):
@@ -181,7 +181,7 @@ def setup_json_template_v1(self):
def test_json_template_v1(self):
"""HTML block template is v1 minified"""
assert self.r_json_v1.status_code == 403
- assert self.r_json_v1.headers.get("content-type", "") in JSON_CONTENT_TYPES
+ assert self.r_json_v1.headers.get("content-type", "").lower() in JSON_CONTENT_TYPES
assert self.r_json_v1.text.rstrip() == BLOCK_TEMPLATE_JSON_MIN_V1.rstrip()
def setup_html_template_v2(self):
@@ -197,7 +197,7 @@ def setup_html_template_v2(self):
def test_html_template_v2(self):
"""HTML block template is v2 minified"""
assert self.r_html_v2.status_code == 403
- assert self.r_html_v2.headers.get("content-type", "") in HTML_CONTENT_TYPES
+ assert self.r_html_v2.headers.get("content-type", "").lower() in HTML_CONTENT_TYPES
assert self.r_html_v2.text == BLOCK_TEMPLATE_HTML_MIN_V2
@@ -226,7 +226,10 @@ def test_custom_redirect(self):
def setup_custom_redirect_wrong_status_code(self):
self.r_cr = weblog.get("/waf/", headers={"User-Agent": "Canary/v3"}, allow_redirects=False)
- @bug(context.library == "java", reason="Do not check the configured redirect status code")
+ @bug(
+ context.library == "java" and context.weblog_variant not in ("akka-http", "play"),
+ reason="Do not check the configured redirect status code",
+ )
@bug(context.library == "golang", reason="Do not check the configured redirect status code")
def test_custom_redirect_wrong_status_code(self):
"""Block with an HTTP redirection but default to 303 status code, because the configured status code is not a valid redirect status code"""
diff --git a/tests/appsec/waf/test_rules.py b/tests/appsec/waf/test_rules.py
index ba75c8c0cbd..e276a453101 100644
--- a/tests/appsec/waf/test_rules.py
+++ b/tests/appsec/waf/test_rules.py
@@ -79,6 +79,7 @@ def setup_lfi_in_path(self):
@bug(context.weblog_variant == "uwsgi-poc" and context.library == "python")
@irrelevant(library="python", weblog_variant="django-poc")
@irrelevant(library="dotnet", reason="lfi patterns are always filtered by the host web-server")
+ @irrelevant(context.weblog_variant == "akka-http" and context.library == "java", reason="path is normalized to /")
def test_lfi_in_path(self):
""" AppSec catches LFI attacks in URL path like /.."""
interfaces.library.assert_waf_attack(self.r_5, rules.lfi.crs_930_110)
diff --git a/utils/build/docker/java/akka-http/pom.xml b/utils/build/docker/java/akka-http/pom.xml
index 4a68e88601a..41b3644a73f 100644
--- a/utils/build/docker/java/akka-http/pom.xml
+++ b/utils/build/docker/java/akka-http/pom.xml
@@ -10,7 +10,7 @@
1.0.0
- 2.13.5
+ 2.13.10
2.8.0
10.5.0
@@ -26,6 +26,16 @@
akka-http-jackson_2.13
${akka.http.version}
+
+ com.fasterxml.jackson.module
+ jackson-module-scala_2.13
+ 2.13.4
+
+
+ com.typesafe.akka
+ akka-http-xml_2.13
+ ${akka.http.version}
+
com.typesafe.akka
akka-actor_2.13
@@ -140,6 +150,7 @@
*:*
+ META-INF/*.MF
META-INF/*.SF
META-INF/*.DSA
META-INF/*.RSA
diff --git a/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/AppSecRoutes.scala b/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/AppSecRoutes.scala
new file mode 100644
index 00000000000..2e5a92f2ec3
--- /dev/null
+++ b/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/AppSecRoutes.scala
@@ -0,0 +1,181 @@
+package com.datadoghq.akka_http
+
+import akka.http.scaladsl.Http
+import akka.http.scaladsl.marshalling.Marshaller
+import akka.http.scaladsl.model.Uri.Path
+import akka.http.scaladsl.model._
+import akka.http.scaladsl.model.headers._
+import akka.http.scaladsl.server.Directives._
+import akka.http.scaladsl.server.Route
+import akka.http.scaladsl.unmarshalling._
+
+import java.util
+import scala.concurrent.Future
+import scala.xml.{Elem, XML}
+
+object AppSecRoutes {
+ val route: Route =
+ path("") {
+ get {
+ val span = tracer.buildSpan("test-span").start
+ span.setTag("test-tag", "my value")
+ withSpan(span) {
+ complete("Hello world!")
+ }
+ }
+ } ~
+ path("headers") {
+ get {
+ val entity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, "012345678901234567890123456789012345678901")
+ respondWithHeaders(RawHeader("Content-Language", "en-US")) {
+ complete(entity)
+ }
+ }
+ } ~
+ path("tag_value" / Segment / """\d{3}""".r) { (value, code) =>
+ get {
+ parameter("content-language".?) { clo =>
+ setRootSpanTag("appsec.events.system_tests_appsec_event.value", value)
+
+ val resp = complete(
+ HttpResponse(
+ status = StatusCodes.custom(code.toInt, "some reason"),
+ entity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, "Value tagged")
+ )
+ )
+
+ clo match {
+ case Some(cl) => respondWithHeaders(RawHeader("Content-Language", cl)) { resp }
+ case None => resp
+ }
+ }
+ } ~
+ post {
+ formFieldMap { _ =>
+ setRootSpanTag("appsec.events.system_tests_appsec_event.value", value)
+ complete(
+ HttpResponse(
+ status = StatusCodes.custom(code.toInt, "some reason"),
+ entity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, "Value tagged")
+ )
+ )
+ }
+ }
+ } ~
+ path("params" / Segments) { segments: Seq[String] =>
+ get {
+ complete(segments.toString())
+ }
+ } ~
+ path("waf") {
+ get {
+ complete("Hello world!")
+ } ~
+ post {
+ formFieldMultiMap { fields: Map[String, List[String]] =>
+ complete(fields.toString)
+ } ~
+ entity(Unmarshaller.messageUnmarshallerFromEntityUnmarshaller(generalizedJsonUnmarshaller)) { value =>
+ complete(value.toString)
+ } ~ entity(as[XmlObject]) { xmlObj =>
+ complete(xmlObj.toString)
+ } ~
+ entity(Unmarshaller.messageUnmarshallerFromEntityUnmarshaller(
+ Unmarshaller.byteArrayUnmarshaller.forContentTypes(
+ MediaTypes.`application/octet-stream`))) { arr: Array[Byte] =>
+ complete(s"Hello world (${arr.length})")
+ } ~
+ entity(as[String]) { s: String =>
+ // interpret as string as fallback, regardless of content-type
+ complete(s)
+ }
+ }
+ } ~
+ path("waf" / RemainingPath) { remaining: Path =>
+ get {
+ complete(remaining.toString())
+ }
+ } ~
+ path("make_distant_call") {
+ get {
+ parameter("url") { url =>
+ complete(StatusCodes.OK, makeDistantCall(url))(Marshaller.futureMarshaller(jsonMarshaller))
+ }
+ }
+ } ~
+ path("status") {
+ get {
+ parameter("code".as[Int]) { code =>
+ complete(StatusCodes.custom(code, "whatever reason"))
+ }
+ }
+ } ~
+ path("user_login_success_event") {
+ get {
+ parameter("event_user_id".?("system_tests_user")) { userId =>
+ eventTracker.trackLoginSuccessEvent(userId, metadata)
+ complete("ok")
+ }
+ }
+ } ~
+ path("user_login_failure_event") {
+ get {
+ parameters("event_user_id".?("system_tests_user"),
+ "event_user_exists".as[Boolean].?(true)) { (userId, userExists) =>
+ eventTracker.trackLoginFailureEvent(userId, userExists, metadata)
+ complete("ok")
+ }
+ }
+ } ~
+ path("custom_event") {
+ get {
+ parameter("event_name".?("system_tests_event")) { eventName =>
+ eventTracker.trackCustomEvent(eventName, metadata)
+ complete("ok")
+ }
+ }
+ }
+
+ case class XmlObject(value: String, attack: String)
+
+ implicit val xmlObjectUnmarshaller: FromEntityUnmarshaller[XmlObject] =
+ Unmarshaller.stringUnmarshaller.forContentTypes(MediaTypes.`text/xml`, MediaTypes.`application/xml`).map { string =>
+ val xmlData: Elem = XML.loadString(string)
+ val value = (xmlData \ "value").text
+ val attack = (xmlData \ "attack").text
+ XmlObject(value, attack)
+ }
+
+ case class DistantCallResponse(
+ url: String,
+ status_code: Int,
+ request_headers: Map[String, String],
+ response_headers: Map[String, String]
+ )
+
+ private def makeDistantCall(url: String): Future[DistantCallResponse] = {
+ val request = HttpRequest(uri = url)
+ val requestHeaders = request.headers.map(h => (h.name(), h.value())).toMap
+
+ Http().singleRequest(request).map { response =>
+ val statusCode = response.status.intValue()
+ val responseHeaders = response.headers.map(h => (h.name(), h.value())).toMap
+
+ response.discardEntityBytes()
+
+ DistantCallResponse(
+ url = url,
+ status_code = statusCode,
+ request_headers = requestHeaders,
+ response_headers = responseHeaders,
+ )
+ }
+ }
+
+ private val metadata: util.Map[String, String] = {
+ val h = new util.HashMap[String, String]
+ h.put("metadata0", "value0")
+ h.put("metadata1", "value1")
+ h
+ }
+}
diff --git a/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/IastRoutes.scala b/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/IastRoutes.scala
index ea61bc7ee1c..61d64587452 100644
--- a/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/IastRoutes.scala
+++ b/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/IastRoutes.scala
@@ -2,10 +2,9 @@ package com.datadoghq.akka_http
import akka.http.javadsl.marshallers.jackson.Jackson
import akka.http.scaladsl.marshalling.Marshaller
-import akka.http.scaladsl.model.{HttpEntity, RequestEntity, StatusCodes}
+import akka.http.scaladsl.model.{RequestEntity, StatusCodes}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
-import akka.http.scaladsl.unmarshalling.Unmarshaller
import com.datadoghq.system_tests.iast.infra.{LdapServer, SqlServer}
import com.datadoghq.system_tests.iast.utils._
@@ -203,12 +202,6 @@ object IastRoutes {
}
}
- private val jsonMarshaller : Marshaller[Object, RequestEntity] =
- Jackson.marshaller().asScala.map(_.asInstanceOf[RequestEntity] /* just downcast */)
-
- implicit val mapJsonUnmarshaller : Unmarshaller[HttpEntity, java.util.Map[String, Object]] =
- Jackson.unmarshaller(classOf[java.util.Map[String, Object]]).asScala
-
private def paramOrFormField(p: String) = {
parameter(p) | formField(p)
}
diff --git a/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/Main.scala b/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/Main.scala
index 921d574b549..1996f6348de 100644
--- a/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/Main.scala
+++ b/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/Main.scala
@@ -1,106 +1,14 @@
package com.datadoghq.akka_http
-import akka.http.javadsl.marshallers.jackson.Jackson
import akka.http.scaladsl.Http
-import akka.http.scaladsl.model._
-import akka.http.scaladsl.model.headers.{RawHeader, `Content-Length`}
import akka.http.scaladsl.server.Directives._
-import akka.http.scaladsl.unmarshalling.Unmarshaller
-import datadog.trace.api.{GlobalTracer => DDGlobalTracer}
import org.slf4j.LoggerFactory
import scala.concurrent.Future
object Main extends App {
- private def testSpan = {
- val span = tracer.buildSpan("test-span").start()
- span.setTag("test-tag", "my value")
- span
- }
-
- private val entityToJsonUnmarshaller : Unmarshaller[HttpEntity, Object] = Jackson.unmarshaller(classOf[Object]).asScala
- private val jsonUnmarshaller = Unmarshaller.strict[HttpRequest, HttpEntity](_.entity).andThen(entityToJsonUnmarshaller)
-
- private val route =
- path("") {
- get {
- val span = testSpan
- complete {
- try "Hello World!" finally span.finish()
- }
- }
- } ~
- path("headers") {
- get {
- respondWithHeader(RawHeader("Content-Language", "en-US")) {
- complete(
- "012345678901234567890123456789012345678901"
- )
- }
- }
- } ~
- path("params" / Segments) { pathSegments =>
- get {
- complete {
- pathSegments.toString()
- }
- }
- } ~
- path("waf") {
- post {
- entity(as[FormData]) { formData =>
- complete(formData.fields.toMultiMap.toString)
- } ~
- entity(as[Object](jsonUnmarshaller)) { json =>
- complete(json.toString)
- }
- }
- } ~
- path("status") {
- get {
- parameter("code".as[Int]) { code =>
- complete(StatusCode.int2StatusCode(code), code.toString)
- }
- }
- } ~
- path("user_login_success_event") {
- get {
- parameter("event_user_id".?("system_tests_user")) { uid =>
- DDGlobalTracer.getEventTracker.trackLoginSuccessEvent(uid, metadata)
- complete("ok")
- }
- }
- } ~
- path("user_login_failure_event") {
- get {
- parameters(
- "event_user_id".?("system_tests_user"),
- "event_user_exists".as[Boolean].?(true)
- ) { (uid, event_user_exists) =>
- DDGlobalTracer.getEventTracker.trackLoginFailureEvent(
- uid, event_user_exists, metadata)
- complete("ok")
- }
- }
- } ~
- path("custom_event") {
- get {
- parameter("event_name".?("system_tests_event")) { eventName =>
- DDGlobalTracer.getEventTracker.trackCustomEvent(eventName, metadata)
- complete("ok")
- }
- }
- }
-
- private val metadata : java.util.Map[String, String] = {
- val h = new java.util.HashMap[String, String]()
- h.put("metadata0", "value0")
- h.put("metadata1", "value1")
- h
- }
-
private val bindingFuture: Future[Http.ServerBinding] =
- Http().newServerAt("0.0.0.0", 7777).bindFlow(route ~ IastRoutes.route)
+ Http().newServerAt("0.0.0.0", 7777).bindFlow(AppSecRoutes.route ~ IastRoutes.route)
LoggerFactory.getLogger(this.getClass).info("Server online at port 7777")
}
diff --git a/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/package.scala b/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/package.scala
index 41002168692..c9b210a184b 100644
--- a/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/package.scala
+++ b/utils/build/docker/java/akka-http/src/main/scala/com/datadoghq/akka_http/package.scala
@@ -1,14 +1,53 @@
package com.datadoghq
import akka.actor.ActorSystem
+import akka.http.javadsl.marshallers.jackson.Jackson
+import akka.http.scaladsl.marshalling.Marshaller
+import akka.http.scaladsl.model.{HttpEntity, MediaTypes, RequestEntity}
+import akka.http.scaladsl.unmarshalling.Unmarshaller
import akka.stream.SystemMaterializer
-import io.opentracing.Tracer
+import com.fasterxml.jackson.databind.MapperFeature
+import com.fasterxml.jackson.databind.json.JsonMapper
+import com.fasterxml.jackson.module.scala.{DefaultScalaModule, ScalaObjectMapper}
+import datadog.trace.api.interceptor.MutableSpan
+import io.opentracing.{Span, Tracer}
import io.opentracing.util.GlobalTracer
package object akka_http {
val tracer : Tracer = GlobalTracer.get()
+ val eventTracker = datadog.trace.api.GlobalTracer.getEventTracker
implicit val system = ActorSystem("my-system")
implicit val materializer = SystemMaterializer.get(system).materializer
implicit val executionContext = system.dispatcher
+
+ implicit def withSpan[A](span: Span)(f: => A): A = try f finally span.finish()
+
+ implicit def setRootSpanTag(key: String, value: String): Unit = {
+ val span = tracer.activeSpan
+ if (span.isInstanceOf[MutableSpan]) {
+ val rootSpan = span.asInstanceOf[MutableSpan].getLocalRootSpan
+ if (rootSpan != null) rootSpan.setTag(key, value)
+ }
+ }
+
+ implicit val mapJsonUnmarshaller : Unmarshaller[HttpEntity, java.util.Map[String, Object]] =
+ Jackson.unmarshaller(classOf[java.util.Map[String, Object]])
+ .asScala
+ .forContentTypes(MediaTypes.`application/json`)
+
+ val generalizedJsonUnmarshaller : Unmarshaller[HttpEntity, Object] = {
+ Jackson.unmarshaller(classOf[Object])
+ .asScala
+ .forContentTypes(MediaTypes.`application/json`)
+ }
+
+ private val jsonMapper: JsonMapper =
+ JsonMapper.builder.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
+ .addModule(DefaultScalaModule)
+ .build
+
+ val jsonMarshaller : Marshaller[Object, RequestEntity] =
+ Jackson.marshaller(jsonMapper).asScala.map(_.asInstanceOf[RequestEntity] /* just downcast */)
+
}
diff --git a/utils/build/docker/java/app-play.sh b/utils/build/docker/java/app-play.sh
new file mode 100644
index 00000000000..1b16bd1a82b
--- /dev/null
+++ b/utils/build/docker/java/app-play.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+set -eu
+# shellcheck disable=SC2086
+exec java -Xmx362m -javaagent:/app/dd-java-agent.jar -cp "/app/lib/*" play.core.server.ProdServerStart /app/ ${APP_EXTRA_ARGS:-}
+
diff --git a/utils/build/docker/java/play.Dockerfile b/utils/build/docker/java/play.Dockerfile
new file mode 100644
index 00000000000..b286cb8f0fc
--- /dev/null
+++ b/utils/build/docker/java/play.Dockerfile
@@ -0,0 +1,32 @@
+FROM maven:3.6-jdk-11 as build
+
+RUN apt-get update && \
+ apt-get install -y libarchive-tools
+
+WORKDIR /app
+
+COPY ./utils/build/docker/java/play/pom.xml .
+RUN mkdir /maven && mvn -Dmaven.repo.local=/maven -B dependency:go-offline
+
+COPY ./utils/build/docker/java/play/app ./app
+COPY ./utils/build/docker/java/play/conf ./conf
+RUN mvn -Dmaven.repo.local=/maven play2:routes-compile package play2:dist-exploded
+
+COPY ./utils/build/docker/java/install_ddtrace.sh binaries* /binaries/
+RUN /binaries/install_ddtrace.sh
+
+FROM eclipse-temurin:11-jre
+
+WORKDIR /app
+COPY --from=build /binaries/SYSTEM_TESTS_LIBRARY_VERSION SYSTEM_TESTS_LIBRARY_VERSION
+COPY --from=build /binaries/SYSTEM_TESTS_LIBDDWAF_VERSION SYSTEM_TESTS_LIBDDWAF_VERSION
+COPY --from=build /binaries/SYSTEM_TESTS_APPSEC_EVENT_RULES_VERSION SYSTEM_TESTS_APPSEC_EVENT_RULES_VERSION
+COPY --from=build /app/target/dist/play-app-1.0.0 .
+COPY --from=build /dd-tracer/dd-java-agent.jar .
+
+COPY ./utils/build/docker/java/app-play.sh /app/app.sh
+RUN chmod +x /app/app.sh
+
+ENV DD_TRACE_HEADER_TAGS='user-agent:http.request.headers.user-agent'
+
+CMD [ "/app/app.sh" ]
diff --git a/utils/build/docker/java/play/app/Binders.scala b/utils/build/docker/java/play/app/Binders.scala
new file mode 100644
index 00000000000..7f31d52e7d4
--- /dev/null
+++ b/utils/build/docker/java/play/app/Binders.scala
@@ -0,0 +1,8 @@
+import play.api.mvc.PathBindable
+
+package object Binders {
+ implicit def seqPathBindable(implicit stringBinder: PathBindable[String]): PathBindable[Seq[String]] = new PathBindable[Seq[String]] {
+ override def bind(key: String, value: String): Either[String, Seq[String]] = Right(value.split("/").toSeq)
+ override def unbind(key: String, values: Seq[String]): String = values.mkString("/")
+ }
+}
diff --git a/utils/build/docker/java/play/app/Module.scala b/utils/build/docker/java/play/app/Module.scala
new file mode 100644
index 00000000000..d2fb5c253e5
--- /dev/null
+++ b/utils/build/docker/java/play/app/Module.scala
@@ -0,0 +1,10 @@
+import com.google.inject.AbstractModule
+import play.inject.Module.bindClass
+import play.libs.ws.WSClient
+
+class Module extends AbstractModule {
+ override def configure() = {
+// bind(classOf[WSClient]).toInstance()
+ }
+
+}
diff --git a/utils/build/docker/java/play/app/controllers/AppSecController.scala b/utils/build/docker/java/play/app/controllers/AppSecController.scala
new file mode 100644
index 00000000000..620fd6435ed
--- /dev/null
+++ b/utils/build/docker/java/play/app/controllers/AppSecController.scala
@@ -0,0 +1,166 @@
+package controllers
+
+import akka.stream.Materializer
+import akka.stream.javadsl.Sink
+import akka.util.ByteString
+import play.api.libs.json.{Json, Writes}
+import play.api.libs.ws.ahc.{AhcWSClient, AhcWSRequest, StandaloneAhcWSResponse}
+import play.api.libs.ws.{WSClient, WSRequest}
+import play.api.mvc._
+import play.shaded.ahc.org.asynchttpclient.{AsyncCompletionHandler, AsyncHttpClient, Request => AHCRequest, Response => AHCResponse}
+
+import java.util
+import javax.inject.{Inject, Singleton}
+import scala.concurrent.{ExecutionContext, Future, Promise}
+
+@Singleton
+class AppSecController @Inject()(cc: MessagesControllerComponents, ws: WSClient, mat: Materializer)
+ (implicit ec: ExecutionContext) extends AbstractController(cc) {
+ def index = Action {
+ val span = tracer.buildSpan("test-span").start
+ span.setTag("test-tag", "my value")
+ withSpan(span) {
+ Results.Ok("Hello world!")
+ }
+ }
+
+ def headers = Action {
+ Results.Ok("012345678901234567890123456789012345678901")
+ .as("text/plain; charset=utf-8")
+ .withHeaders("Content-Language" -> "en-US")
+ }
+
+
+ def tagValue(value: String, code: Int) = Action { request =>
+ setRootSpanTag("appsec.events.system_tests_appsec_event.value", value)
+
+ val result = Results.Status(code)("Value tagged")
+ .as("text/plain; charset=utf-8")
+
+ request.queryString.get("content-language").flatMap(_.headOption) match {
+ case Some(cl) => result.withHeaders(CONTENT_LANGUAGE -> cl)
+ case None => result
+ }
+ }
+
+ def tagValuePost(value: String, code: Int) = Action { request =>
+ request.body.asFormUrlEncoded // needs to be read, though we do nothing with it
+
+ setRootSpanTag("appsec.events.system_tests_appsec_event.value", value)
+
+ Results.Status(code)("Value tagged")
+ .as("text/plain; charset=utf-8")
+ }
+
+ def params(segments: Seq[String]) = Action {
+ Results.Ok(segments.toString())
+ }
+
+ def waf = Action {
+ Results.Ok("Hello world!")
+ }
+
+ def wafPost = Action { request =>
+ request.body match {
+ case AnyContentAsFormUrlEncoded(data) =>
+ Results.Ok(data.toString())
+ case AnyContentAsMultipartFormData(mpfd) =>
+ Results.Ok(mpfd.dataParts.toString())
+ case AnyContentAsJson(data) =>
+ Results.Ok(Json.stringify(data))
+ case AnyContentAsXml(data) =>
+ Results.Ok(data.toString())
+ case AnyContentAsRaw(data) =>
+ Results.Ok(s"Hello world (${data.size})")
+ case AnyContentAsText(data) =>
+ Results.Ok(data)
+ case anything =>
+ Results.Ok(anything.toString)
+ }
+ }
+
+ def distantCall(url: String) = Action.async {
+ val remoteReq: WSRequest = ws.url(url).withMethod("GET")
+
+ // we need to break the abstraction to be able to get to the request headers
+ val ahcRequest: AHCRequest = remoteReq.asInstanceOf[AhcWSRequest].underlying.buildRequest()
+
+ executeAHCRequest(ahcRequest).map { resp: StandaloneAhcWSResponse =>
+ resp.bodyAsSource.runWith(Sink.ignore[ByteString]())(mat)
+ val dcr = DistantCallResponse.create(url, resp.status, ahcRequest.getHeaders, resp.headers)
+ Results.Ok(Json.toJson(dcr))
+ }
+ }
+
+ private def executeAHCRequest(request: AHCRequest): Future[StandaloneAhcWSResponse] = {
+ val result = Promise[StandaloneAhcWSResponse]()
+ val handler = new AsyncCompletionHandler[AHCResponse]() {
+ override def onCompleted(response: AHCResponse): AHCResponse = {
+ result.success(StandaloneAhcWSResponse(response))
+ response
+ }
+
+ override def onThrowable(t: Throwable): Unit = {
+ result.failure(t)
+ }
+ }
+
+ ws.asInstanceOf[AhcWSClient].underlying[AsyncHttpClient].executeRequest(request, handler)
+ result.future
+ }
+
+ def status(code: Int) = Action {
+ Results.Status(code)
+ }
+
+ def loginSuccess(event_user_id: Option[String]) = Action {
+ eventTracker.trackLoginSuccessEvent(event_user_id.getOrElse("system_tests_user"), metadata)
+ Results.Ok("ok")
+ }
+
+ def loginFailure(event_user_id: Option[String], event_user_exists: Option[Boolean]) = Action {
+ eventTracker.trackLoginFailureEvent(
+ event_user_id.getOrElse("system_tests_user"), event_user_exists.getOrElse(true), metadata)
+ Results.Ok("ok")
+ }
+
+ def customEvent(event_name: Option[String]) = Action {
+ eventTracker.trackCustomEvent(event_name.getOrElse("system_tests_event"), metadata)
+ Results.Ok("ok")
+ }
+
+ case class DistantCallResponse(
+ url: String,
+ status_code: Int,
+ request_headers: Map[String, String],
+ response_headers: Map[String, String]
+ )
+
+ object DistantCallResponse {
+ def create(url: String, status_code: Int, request_headers: java.lang.Iterable[java.util.Map.Entry[String, String]],
+ response_headers: Map[String, scala.collection.Seq[String]]): DistantCallResponse = {
+ apply(url, status_code, convertIterable(request_headers), convertMap(response_headers.view.mapValues(_.toSeq).toMap))
+ }
+
+ private def convertMap(m: Map[String, Seq[String]]) : Map[String, String] =
+ m.flatMap {
+ case (key, Seq(value, _*)) => Some(key, value)
+ case _ => None
+ }
+
+ private def convertIterable(it: java.lang.Iterable[java.util.Map.Entry[String, String]]) : Map[String, String] = {
+ import scala.jdk.CollectionConverters._
+ it.asScala.map(entry => entry.getKey -> entry.getValue).toMap
+ }
+
+ }
+
+ implicit val distantCallRespWrites: Writes[DistantCallResponse] = Json.writes[DistantCallResponse]
+
+ private val metadata: util.Map[String, String] = {
+ val h = new util.HashMap[String, String]
+ h.put("metadata0", "value0")
+ h.put("metadata1", "value1")
+ h
+ }
+}
diff --git a/utils/build/docker/java/play/app/controllers/controllers.scala b/utils/build/docker/java/play/app/controllers/controllers.scala
new file mode 100644
index 00000000000..d7c76f194ec
--- /dev/null
+++ b/utils/build/docker/java/play/app/controllers/controllers.scala
@@ -0,0 +1,18 @@
+import datadog.trace.api.interceptor.MutableSpan
+import io.opentracing.util.GlobalTracer
+import io.opentracing.{Span, Tracer}
+
+package object controllers {
+ val tracer : Tracer = GlobalTracer.get()
+ val eventTracker = datadog.trace.api.GlobalTracer.getEventTracker
+
+ def withSpan[A](span: Span)(f: => A): A = try f finally span.finish()
+
+ def setRootSpanTag(key: String, value: String): Unit = {
+ val span = tracer.activeSpan
+ if (span.isInstanceOf[MutableSpan]) {
+ val rootSpan = span.asInstanceOf[MutableSpan].getLocalRootSpan
+ if (rootSpan != null) rootSpan.setTag(key, value)
+ }
+ }
+}
diff --git a/utils/build/docker/java/play/conf/application.conf b/utils/build/docker/java/play/conf/application.conf
new file mode 100644
index 00000000000..a666bea4a67
--- /dev/null
+++ b/utils/build/docker/java/play/conf/application.conf
@@ -0,0 +1,2 @@
+play.http.secret.key="7Yf2WqExHc0LqpDBYzVeHnns656b6Iw6ekgApxvWXOLyLZzaAGsepCecumbk6qh"
+http.port=7777
diff --git a/utils/build/docker/java/play/conf/logback.xml b/utils/build/docker/java/play/conf/logback.xml
new file mode 100644
index 00000000000..293d5aeb855
--- /dev/null
+++ b/utils/build/docker/java/play/conf/logback.xml
@@ -0,0 +1,11 @@
+
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
diff --git a/utils/build/docker/java/play/conf/routes b/utils/build/docker/java/play/conf/routes
new file mode 100644
index 00000000000..bc58eb636c9
--- /dev/null
+++ b/utils/build/docker/java/play/conf/routes
@@ -0,0 +1,13 @@
+GET / controllers.AppSecController.index
+GET /headers controllers.AppSecController.headers
+GET /tag_value/:value/:code controllers.AppSecController.tagValue(value: String, code: Int)
+POST /tag_value/:value/:code controllers.AppSecController.tagValuePost(value: String, code: Int)
+GET /params/*segments controllers.AppSecController.params(segments: Seq[String])
+GET /waf controllers.AppSecController.waf
+GET /waf/*segments controllers.AppSecController.params(segments: Seq[String])
+POST /waf controllers.AppSecController.wafPost
+GET /make_distant_call controllers.AppSecController.distantCall(url: String)
+GET /status controllers.AppSecController.status(code: Int)
+GET /user_login_success_event controllers.AppSecController.loginSuccess(event_user_id: Option[String])
+GET /user_login_failure_event controllers.AppSecController.loginFailure(event_user_id: Option[String], event_user_exists: Option[Boolean])
+GET /custom_event controllers.AppSecController.customEvent(event_name: Option[String])
diff --git a/utils/build/docker/java/play/pom.xml b/utils/build/docker/java/play/pom.xml
new file mode 100644
index 00000000000..54b8465e676
--- /dev/null
+++ b/utils/build/docker/java/play/pom.xml
@@ -0,0 +1,143 @@
+
+
+ 4.0.0
+
+ com.datadoghq.play
+ play-app
+ 1.0.0
+
+
+ UTF-8
+ UTF-8
+ 2.13.8
+ 2.8.20
+
+
+
+
+ org.scala-lang
+ scala-library
+ ${scala.version}
+
+
+ com.typesafe.play
+ play_2.13
+ ${play2.version}
+
+
+ com.typesafe.play
+ play-guice_2.13
+ ${play2.version}
+
+
+ com.typesafe.play
+ play-ahc-ws_2.13
+ ${play2.version}
+
+
+
+ com.typesafe.play
+ play-logback_2.13
+ ${play2.version}
+ runtime
+
+
+
+ com.typesafe.play
+ play-akka-http-server_2.13
+ ${play2.version}
+ runtime
+
+
+
+ io.opentracing
+ opentracing-api
+ 0.33.0
+
+
+ io.opentracing
+ opentracing-util
+ 0.33.0
+
+
+ com.datadoghq
+ dd-trace-api
+ 1.14.0
+
+
+
+
+ ${basedir}/app
+
+
+ ${basedir}/conf
+
+
+ ${basedir}/public
+ public
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+
+ 11
+
+
+
+ default-compile
+ compile
+
+ compile
+
+
+
+
+
+ com.google.code.sbt-compiler-maven-plugin
+ sbt-compiler-maven-plugin
+ 1.0.0
+
+ -feature -deprecation -Xfatal-warnings
+
+
+
+ default-sbt-compile
+
+ compile
+ testCompile
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ true
+ true
+
+
+
+ com.google.code.play2-maven-plugin
+ play2-maven-plugin
+ 1.0.0-rc5
+ true
+
+ ${play2.version}
+ Binders._
+
+
+
+ routes-compile
+
+
+
+
+
+