diff --git a/.github/workflows/run-end-to-end.yml b/.github/workflows/run-end-to-end.yml index 357485f677..1be3d48de8 100644 --- a/.github/workflows/run-end-to-end.yml +++ b/.github/workflows/run-end-to-end.yml @@ -272,6 +272,9 @@ jobs: - name: Run APPSEC_RASP scenario if: always() && steps.build.outcome == 'success' && contains(inputs.scenarios, '"APPSEC_RASP"') run: ./run.sh APPSEC_RASP + - name: Run APPSEC_RASP_NON_BLOCKING scenario + if: always() && steps.build.outcome == 'success' && contains(inputs.scenarios, '"APPSEC_RASP_NON_BLOCKING"') + run: ./run.sh APPSEC_RASP_NON_BLOCKING - name: Run APPSEC_META_STRUCT_DISABLED scenario if: always() && steps.build.outcome == 'success' && contains(inputs.scenarios, '"APPSEC_META_STRUCT_DISABLED"') run: ./run.sh APPSEC_META_STRUCT_DISABLED diff --git a/docs/weblog/README.md b/docs/weblog/README.md index 97a80fe6a2..fc5bac8417 100644 --- a/docs/weblog/README.md +++ b/docs/weblog/README.md @@ -768,6 +768,17 @@ Examples: - `GET`: `/rasp/ssrf?user_id="' OR 1 = 1 --"` - `POST`: `{"user_id": "' OR 1 = 1 --"}` +### \[GET\] /rasp/multiple +The idea of this endpoint is to have an endpoint where multiple rasp operation take place. All of them will generate a MATCH on the WAF but none of them will block. The goal of this endpoint is to verify that the `rasp.rule.match` telemetry entry is updated properly. While this seems easy, the WAF requires that data given on `call` is passed as ephemeral and not as persistent. + +In order to make the test easier, the operation used here need to generate LFI matches. The request will have two get parameters(`file1`, `file2`) which will contain a path that needs to be used as the parameters of the choosen lfi function. Then there will be another call to the lfi function with a harcoded parameter `'../etc/passwd'`. This will make `rasp.rule.match` to be equal to 3. A code example look like: + +``` +lfi_operation($request->get('file1')) +lfi_operation($request->get('file2')) +lfi_operation('../etc/passwd') //This one is harcoded +``` + ### GET /dsm/inject This endpoint is used to validate DSM context injection injects the correct encoding to a headers carrier. diff --git a/manifests/dotnet.yml b/manifests/dotnet.yml index 973eed9a75..5406f10e79 100644 --- a/manifests/dotnet.yml +++ b/manifests/dotnet.yml @@ -183,6 +183,7 @@ tests/: Test_Lfi_Rules_Version: v3.7.0 Test_Lfi_StackTrace: v2.51.0 Test_Lfi_Telemetry: v2.51.0 + Test_Lfi_Telemetry_Multiple_Exploits: missing_feature Test_Lfi_UrlQuery: v2.51.0 Test_Lfi_Waf_Version: v3.4.1 test_shi.py: diff --git a/manifests/java.yml b/manifests/java.yml index 0da8ae8e9e..07d1f2b91c 100644 --- a/manifests/java.yml +++ b/manifests/java.yml @@ -696,6 +696,7 @@ tests/: Test_Lfi_Telemetry: '*': v1.40.0 spring-boot-3-native: missing_feature (GraalVM. Tracing support only) + Test_Lfi_Telemetry_Multiple_Exploits: missing_feature Test_Lfi_UrlQuery: '*': v1.40.0 spring-boot-3-native: missing_feature (GraalVM. Tracing support only) diff --git a/manifests/nodejs.yml b/manifests/nodejs.yml index 81ab624f94..904b3e8432 100644 --- a/manifests/nodejs.yml +++ b/manifests/nodejs.yml @@ -388,6 +388,7 @@ tests/: Test_Lfi_Rules_Version: *ref_5_26_0 Test_Lfi_StackTrace: *ref_5_24_0 Test_Lfi_Telemetry: *ref_5_24_0 + Test_Lfi_Telemetry_Multiple_Exploits: missing_feature Test_Lfi_UrlQuery: '*': *ref_5_24_0 express5: *ref_5_29_0 diff --git a/manifests/php.yml b/manifests/php.yml index d8970f942e..0e77b2f96b 100644 --- a/manifests/php.yml +++ b/manifests/php.yml @@ -177,6 +177,7 @@ tests/: Test_Lfi_Rules_Version: v1.6.2 Test_Lfi_StackTrace: v1.6.2 Test_Lfi_Telemetry: v1.7.0 + Test_Lfi_Telemetry_Multiple_Exploits: v1.7.0 Test_Lfi_UrlQuery: v1.6.2 Test_Lfi_Waf_Version: v1.6.2 test_shi.py: diff --git a/manifests/python.yml b/manifests/python.yml index f303b2e3e9..30d8867a8b 100644 --- a/manifests/python.yml +++ b/manifests/python.yml @@ -259,6 +259,7 @@ tests/: Test_Lfi_Rules_Version: v2.18.0.dev Test_Lfi_StackTrace: v2.10.0 Test_Lfi_Telemetry: v2.10.0 + Test_Lfi_Telemetry_Multiple_Exploits: missing_feature Test_Lfi_UrlQuery: v2.10.0 Test_Lfi_Waf_Version: v2.15.0 test_shi.py: diff --git a/tests/appsec/rasp/rasp_non_blocking_ruleset.json b/tests/appsec/rasp/rasp_non_blocking_ruleset.json new file mode 100644 index 0000000000..2911683177 --- /dev/null +++ b/tests/appsec/rasp/rasp_non_blocking_ruleset.json @@ -0,0 +1,282 @@ +{ + "version": "2.1", + "metadata": { + "rules_version": "1.99.0" + }, + "rules": [ + { + "id": "rasp-930-111", + "name": "Local file inclusion exploit", + "tags": { + "type": "lfi", + "category": "vulnerability_trigger", + "cwe": "22", + "capec": "1000/255/153/126", + "confidence": "0", + "module": "rasp" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + } + ], + "regex": "asdnjakslnbdklasbdkasbdkl" + }, + "operator": "match_regex" + } + ], + "transformers": [], + "on_match": [ + "stack_trace" + ] + }, + + { + "id": "rasp-930-100", + "name": "Local file inclusion exploit", + "tags": { + "type": "lfi", + "category": "vulnerability_trigger", + "cwe": "22", + "capec": "1000/255/153/126", + "confidence": "0", + "module": "rasp" + }, + "conditions": [ + { + "parameters": { + "resource": [ + { + "address": "server.io.fs.file" + } + ], + "params": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ] + }, + "operator": "lfi_detector" + } + ], + "transformers": [], + "on_match": [ + "stack_trace" + ] + }, + { + "id": "rasp-934-100", + "name": "Server-side request forgery exploit", + "tags": { + "type": "ssrf", + "category": "vulnerability_trigger", + "cwe": "918", + "capec": "1000/225/115/664", + "confidence": "0", + "module": "rasp" + }, + "conditions": [ + { + "parameters": { + "resource": [ + { + "address": "server.io.net.url" + } + ], + "params": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ] + }, + "operator": "ssrf_detector" + } + ], + "transformers": [], + "on_match": [ + "stack_trace" + ] + }, + { + "id": "rasp-942-100", + "name": "SQL injection exploit", + "tags": { + "type": "sql_injection", + "category": "vulnerability_trigger", + "cwe": "89", + "capec": "1000/152/248/66", + "confidence": "0", + "module": "rasp" + }, + "conditions": [ + { + "parameters": { + "resource": [ + { + "address": "server.db.statement" + } + ], + "params": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "db_type": [ + { + "address": "server.db.system" + } + ] + }, + "operator": "sqli_detector" + } + ], + "transformers": [], + "on_match": [ + "stack_trace" + ] + }, + { + "id": "rasp-932-100", + "name": "Shell injection exploit", + "enabled": true, + "tags": { + "type": "command_injection", + "category": "vulnerability_trigger", + "cwe": "77", + "capec": "1000/152/248/88", + "confidence": "0", + "module": "rasp" + }, + "conditions": [ + { + "parameters": { + "resource": [ + { + "address": "server.sys.shell.cmd" + } + ], + "params": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ] + }, + "operator": "shi_detector" + } + ], + "transformers": [], + "on_match": [ + "stack_trace" + ] + }, + { + "id": "rasp-932-110", + "name": "OS command injection exploit", + "enabled": true, + "tags": { + "type": "command_injection", + "category": "vulnerability_trigger", + "cwe": "77", + "capec": "1000/152/248/88", + "confidence": "0", + "module": "rasp" + }, + "conditions": [ + { + "parameters": { + "resource": [ + { + "address": "server.sys.exec.cmd" + } + ], + "params": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ] + }, + "operator": "cmdi_detector" + } + ], + "transformers": [], + "on_match": [ + "stack_trace" + ] + } + ] +} diff --git a/tests/appsec/rasp/test_lfi.py b/tests/appsec/rasp/test_lfi.py index 3af0533e8e..dceaf99e11 100644 --- a/tests/appsec/rasp/test_lfi.py +++ b/tests/appsec/rasp/test_lfi.py @@ -134,6 +134,22 @@ def test_lfi_span_tags(self): validate_span_tags(self.r, expected_metrics=["_dd.appsec.rasp.duration_ext", "_dd.appsec.rasp.rule.eval"]) +@rfc("https://docs.google.com/document/d/1vmMqpl8STDk7rJnd3YBsa6O9hCls_XHHdsodD61zr_4/edit#heading=h.96mezjnqf46y") +@features.rasp_span_tags +@features.rasp_local_file_inclusion +@scenarios.appsec_rasp_non_blocking +class Test_Lfi_Telemetry_Multiple_Exploits: + """Validate rasp match telemetry metric works""" + + def setup_rasp_match_tag(self): + self.r = weblog.get("/rasp/multiple", params={"file1": "../etc/passwd", "file2": "../etc/group"}) + + def test_rasp_match_tag(self): + series_eval = find_series(True, "appsec", "rasp.rule.match") + assert series_eval + assert series_eval[0]["points"][0][1] == 3.0 + + @rfc("https://docs.google.com/document/d/1vmMqpl8STDk7rJnd3YBsa6O9hCls_XHHdsodD61zr_4/edit#heading=h.enmf90juqidf") @features.rasp_stack_trace @features.rasp_local_file_inclusion diff --git a/utils/_context/_scenarios/__init__.py b/utils/_context/_scenarios/__init__.py index 86c5528d3f..602df0a764 100644 --- a/utils/_context/_scenarios/__init__.py +++ b/utils/_context/_scenarios/__init__.py @@ -832,6 +832,20 @@ class _Scenarios: scenario_groups=[ScenarioGroup.APPSEC, ScenarioGroup.APPSEC_RASP], ) + appsec_rasp_non_blocking = EndToEndScenario( + "APPSEC_RASP_NON_BLOCKING", + weblog_env={"DD_APPSEC_RASP_ENABLED": "true", "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=[ScenarioGroup.APPSEC], + ) + agent_not_supporting_span_events = EndToEndScenario( "AGENT_NOT_SUPPORTING_SPAN_EVENTS", span_events=False, diff --git a/utils/build/docker/php/common/rasp/multiple.php b/utils/build/docker/php/common/rasp/multiple.php new file mode 100644 index 0000000000..a258366cee --- /dev/null +++ b/utils/build/docker/php/common/rasp/multiple.php @@ -0,0 +1,6 @@ +