diff --git a/manifests/dotnet.yml b/manifests/dotnet.yml index a2e1031e6ab..989ae48ed33 100644 --- a/manifests/dotnet.yml +++ b/manifests/dotnet.yml @@ -206,6 +206,7 @@ tests/: Test_API10_downstream_request_tag: missing_feature Test_API10_downstream_ssrf_telemetry: missing_feature Test_API10_redirect: missing_feature + Test_API10_redirect_status: missing_feature Test_API10_request_body: missing_feature Test_API10_request_headers: missing_feature Test_API10_request_method: missing_feature diff --git a/manifests/golang.yml b/manifests/golang.yml index 8868fd808e7..358cee3c385 100644 --- a/manifests/golang.yml +++ b/manifests/golang.yml @@ -217,6 +217,7 @@ tests/: Test_API10_downstream_request_tag: v2.5.0-dev Test_API10_downstream_ssrf_telemetry: v2.4.0 Test_API10_redirect: missing_feature + Test_API10_redirect_status: missing_feature Test_API10_request_body: v2.4.0 Test_API10_request_headers: v2.4.0 Test_API10_request_method: v2.4.0 diff --git a/manifests/java.yml b/manifests/java.yml index 47e15d53326..85c1309e19a 100644 --- a/manifests/java.yml +++ b/manifests/java.yml @@ -784,6 +784,7 @@ tests/: vertx3: v1.54.0 Test_API10_downstream_ssrf_telemetry: missing_feature Test_API10_redirect: missing_feature + Test_API10_redirect_status: missing_feature Test_API10_request_body: '*': missing_feature vertx3: v1.54.0 diff --git a/manifests/nodejs.yml b/manifests/nodejs.yml index 20233033ab3..a7ac911f566 100644 --- a/manifests/nodejs.yml +++ b/manifests/nodejs.yml @@ -526,6 +526,7 @@ tests/: Test_API10_downstream_request_tag: missing_feature Test_API10_downstream_ssrf_telemetry: missing_feature Test_API10_redirect: missing_feature + Test_API10_redirect_status: missing_feature Test_API10_request_body: missing_feature Test_API10_request_headers: missing_feature Test_API10_request_method: missing_feature diff --git a/manifests/php.yml b/manifests/php.yml index ec38e5842af..8779cdd2067 100644 --- a/manifests/php.yml +++ b/manifests/php.yml @@ -216,6 +216,7 @@ tests/: Test_API10_downstream_request_tag: missing_feature Test_API10_downstream_ssrf_telemetry: missing_feature Test_API10_redirect: missing_feature + Test_API10_redirect_status: missing_feature Test_API10_request_body: missing_feature Test_API10_request_headers: missing_feature Test_API10_request_method: missing_feature diff --git a/manifests/python.yml b/manifests/python.yml index b33a9f7f149..4bceb90b975 100644 --- a/manifests/python.yml +++ b/manifests/python.yml @@ -308,6 +308,7 @@ tests/: Test_API10_redirect: '*': v3.18.0 (with urllib support) 'fastapi': v4.1.0 (with requests/urllib3 support) + Test_API10_redirect_status: v4.1.0 Test_API10_request_body: '*': v3.14.0.rc 'fastapi': v3.15.0.dev (with requests support) diff --git a/manifests/python_lambda.yml b/manifests/python_lambda.yml index 63a06701a69..49f40828409 100644 --- a/manifests/python_lambda.yml +++ b/manifests/python_lambda.yml @@ -56,6 +56,10 @@ tests/: rasp/: test_api10.py: Test_API10_all: v8.117.0.dev + Test_API10_downstream_request_tag: missing_feature + Test_API10_downstream_ssrf_telemetry: missing_feature + Test_API10_redirect: missing_feature + Test_API10_redirect_status: missing_feature Test_API10_request_body: v8.117.0.dev Test_API10_request_headers: v8.117.0.dev Test_API10_request_method: v8.117.0.dev diff --git a/manifests/ruby.yml b/manifests/ruby.yml index 9014959559b..722f23f639f 100644 --- a/manifests/ruby.yml +++ b/manifests/ruby.yml @@ -226,6 +226,7 @@ tests/: Test_API10_downstream_request_tag: missing_feature Test_API10_downstream_ssrf_telemetry: missing_feature Test_API10_redirect: missing_feature + Test_API10_redirect_status: missing_feature Test_API10_request_body: missing_feature Test_API10_request_headers: missing_feature Test_API10_request_method: missing_feature diff --git a/tests/appsec/rasp/rasp_non_blocking_ruleset.json b/tests/appsec/rasp/rasp_non_blocking_ruleset.json index 29116831777..b498050a32b 100644 --- a/tests/appsec/rasp/rasp_non_blocking_ruleset.json +++ b/tests/appsec/rasp/rasp_non_blocking_ruleset.json @@ -33,7 +33,6 @@ "stack_trace" ] }, - { "id": "rasp-930-100", "name": "Local file inclusion exploit", @@ -277,6 +276,46 @@ "on_match": [ "stack_trace" ] - } + }, + { + "id": "api-010-100", + "name": "Monitor redirections to GET targets", + "enabled": true, + "tags": { + "type": "api10", + "category": "api_security", + "confidence": "0", + "module": "business-logic" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.io.net.response.status" + } + ], + "list": [ + "301", + "302" + ] + }, + "operator": "exact_match" + } + ], + "transformers": [], + "output": { + "event": false, + "keep": true, + "attributes": { + "appsec.api.redirection.move_target": { + "address": "server.io.net.response.headers", + "key_path": [ + "location" + ] + } + } + } + } ] } diff --git a/tests/appsec/rasp/test_api10.py b/tests/appsec/rasp/test_api10.py index d8b926b5f87..7f91423627d 100644 --- a/tests/appsec/rasp/test_api10.py +++ b/tests/appsec/rasp/test_api10.py @@ -375,3 +375,32 @@ def test_api10_redirect(self): assert self.r.status_code == 200 interfaces.library.validate_one_span(self.r, validator=self.validate) interfaces.library.validate_one_span(self.r, validator=self.validate_metric) + + +@rfc("https://docs.google.com/document/d/1gCXU3LvTH9en3Bww0AC2coSJWz1m7HcavZjvMLuDCWg/edit#heading=h.giijrtyn1fdx") +@features.api10 +@scenarios.appsec_rasp_non_blocking +class Test_API10_redirect_status(API10): + """API 10 for multiple redirect responses. Check status code analysis.""" + + TAGS_EXPECTED = [ + ("_dd.appsec.trace.req_headers", "TAG_API10_REQ_HEADERS"), + ] + + TAGS_EXPECTED_METRIC = [ + ("_dd.appsec.downstream_request", "5"), + ] + + PARAMS = {"Witness": "pwq3ojtropiw3hjtowir", "totalRedirects": "3"} + + def setup_api10_redirect(self): + self.r = weblog.get("/external_request/redirect", params=self.PARAMS) + + def test_api10_redirect(self): + assert self.r.status_code == 200 + # interfaces.library.validate_one_span(self.r, validator=self.validate) + interfaces.library.validate_one_span(self.r, validator=self.validate_metric) + for _, _trace, span in interfaces.library.get_spans(request=self.r): + meta = span.get("meta", {}) + assert isinstance(meta.get("appsec.api.redirection.move_target", None), str), f"missing tag in {meta}" + assert "/redirect?totalRedirects=2" in meta["appsec.api.redirection.move_target"] diff --git a/utils/_context/_scenarios/__init__.py b/utils/_context/_scenarios/__init__.py index c4a1c9c752c..4bd4162a754 100644 --- a/utils/_context/_scenarios/__init__.py +++ b/utils/_context/_scenarios/__init__.py @@ -1030,18 +1030,15 @@ class _Scenarios: }, ) - appsec_rasp_non_blocking = EndToEndScenario( + appsec_rasp_non_blocking = AppsecRaspScenario( "APPSEC_RASP_NON_BLOCKING", - weblog_env={"DD_APPSEC_RASP_ENABLED": "true", "DD_APPSEC_RULES": "/appsec_rasp_non_blocking_ruleset.json"}, + weblog_env={"DD_APPSEC_RULES": "/appsec_rasp_non_blocking_ruleset.json"}, weblog_volumes={ "./tests/appsec/rasp/rasp_non_blocking_ruleset.json": { "bind": "/appsec_rasp_non_blocking_ruleset.json", "mode": "ro", } }, - doc="Enable APPSEC RASP", - github_workflow="endtoend", - scenario_groups=[scenario_groups.appsec], ) appsec_ato_sdk = EndToEndScenario( diff --git a/utils/_context/_scenarios/appsec_rasp.py b/utils/_context/_scenarios/appsec_rasp.py index 64a177486e8..af978c6498e 100644 --- a/utils/_context/_scenarios/appsec_rasp.py +++ b/utils/_context/_scenarios/appsec_rasp.py @@ -7,9 +7,16 @@ class AppsecRaspScenario(EndToEndScenario): - def __init__(self, name: str, weblog_env: dict[str, str | None] | None = None): + def __init__( + self, + name: str, + weblog_env: dict[str, str | None] | None = None, + weblog_volumes: dict[str, dict[str, str]] | None = None, + ): if weblog_env is None: weblog_env = {} + if weblog_volumes is None: + weblog_volumes = {} default_env: dict[str, str | None] = { "DD_APPSEC_RASP_ENABLED": "true", @@ -25,7 +32,8 @@ def __init__(self, name: str, weblog_env: dict[str, str | None] | None = None): weblog_env=merged_env, weblog_volumes={ "./tests/appsec/rasp/rasp_ruleset.json": {"bind": "/appsec_rasp_ruleset.json", "mode": "ro"} - }, + } + | weblog_volumes, doc="Enable APPSEC RASP", github_workflow="endtoend", scenario_groups=[scenario_groups.appsec, scenario_groups.appsec_rasp, scenario_groups.appsec_rasp_scenario],