From e4f2ec60c1db5c7f8aea30280fdf8b6620b5316b Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Thu, 28 Nov 2024 10:43:35 +0100 Subject: [PATCH 01/10] security control tests for java springboot --- .github/workflows/run-end-to-end.yml | 3 + manifests/cpp.yml | 1 + manifests/dotnet.yml | 2 + manifests/golang.yml | 2 + manifests/java.yml | 5 + manifests/nodejs.yml | 2 + manifests/php.yml | 2 + manifests/python.yml | 2 + manifests/ruby.yml | 2 + tests/appsec/iast/test_security_controls.py | 134 ++++++++++++++++++ utils/_context/_scenarios/__init__.py | 10 ++ utils/_features.py | 10 ++ .../iast/utils/SecurityControlUtil.java | 37 +++++ .../system_tests/springboot/AppSecIast.java | 72 ++++++++++ 14 files changed, 284 insertions(+) create mode 100644 tests/appsec/iast/test_security_controls.py create mode 100644 utils/build/docker/java/iast-common/src/main/java/com/datadoghq/system_tests/iast/utils/SecurityControlUtil.java diff --git a/.github/workflows/run-end-to-end.yml b/.github/workflows/run-end-to-end.yml index 630a1f56b0..a49826ea8e 100644 --- a/.github/workflows/run-end-to-end.yml +++ b/.github/workflows/run-end-to-end.yml @@ -112,6 +112,9 @@ jobs: - name: Run IAST_DEDUPLICATION scenario if: always() && steps.build.outcome == 'success' && contains(inputs.scenarios, '"IAST_DEDUPLICATION"') run: ./run.sh IAST_DEDUPLICATION + - name: Run IAST_SECURITY_CONTROLS scenario + if: always() && steps.build.outcome == 'success' && contains(inputs.scenarios, '"IAST_SECURITY_CONTROLS"') + run: ./run.sh IAST_SECURITY_CONTROLS - name: Run DEFAULT scenario if: always() && steps.build.outcome == 'success' && contains(inputs.scenarios, '"DEFAULT"') run: ./run.sh DEFAULT diff --git a/manifests/cpp.yml b/manifests/cpp.yml index 10d7f95601..87ad48ea4f 100644 --- a/manifests/cpp.yml +++ b/manifests/cpp.yml @@ -53,6 +53,7 @@ tests/: test_path.py: irrelevant (ASM is not implemented in C++) test_path_parameter.py: irrelevant (ASM is not implemented in C++) test_uri.py: irrelevant (ASM is not implemented in C++) + test_security_controls.py: irrelevant (ASM is not implemented in C++) rasp/: test_lfi.py: irrelevant (ASM is not implemented in C++) test_libddwaf.py: irrelevant (ASM is not implemented in C++) diff --git a/manifests/dotnet.yml b/manifests/dotnet.yml index ebedcc09d8..a76c583fe4 100644 --- a/manifests/dotnet.yml +++ b/manifests/dotnet.yml @@ -147,6 +147,8 @@ tests/: TestPathParameter: missing_feature test_uri.py: TestURI: irrelevant + test_security_controls.py: + TestSecurityControls: missing_feature rasp/: test_lfi.py: Test_Lfi_BodyJson: v2.51.0 diff --git a/manifests/golang.yml b/manifests/golang.yml index 5d3a554ccf..ac1611af3b 100644 --- a/manifests/golang.yml +++ b/manifests/golang.yml @@ -155,6 +155,8 @@ tests/: TestPathParameter: missing_feature test_uri.py: TestURI: missing_feature + test_security_controls.py: + TestSecurityControls: missing_feature rasp/: test_lfi.py: missing_feature test_libddwaf.py: missing_feature diff --git a/manifests/java.yml b/manifests/java.yml index dba8a769cf..69e4cc5201 100644 --- a/manifests/java.yml +++ b/manifests/java.yml @@ -562,6 +562,11 @@ tests/: spring-boot-3-native: missing_feature (GraalVM. Tracing support only) vertx3: missing_feature vertx4: missing_feature + test_security_controls.py: + TestSecurityControls: + '*': missing_feature (No endpoint implemented) + spring-boot: v1.44.0 + spring-boot-3-native: missing_feature (GraalVM. Tracing support only) rasp/: test_lfi.py: Test_Lfi_BodyJson: diff --git a/manifests/nodejs.yml b/manifests/nodejs.yml index 7f3efaaaca..571591ec2e 100644 --- a/manifests/nodejs.yml +++ b/manifests/nodejs.yml @@ -271,6 +271,8 @@ tests/: nextjs: missing_feature test_uri.py: TestURI: missing_feature + test_security_controls.py: + TestSecurityControls: missing_feature rasp/: test_lfi.py: Test_Lfi_BodyJson: diff --git a/manifests/php.yml b/manifests/php.yml index 356224a0e8..bb863a4499 100644 --- a/manifests/php.yml +++ b/manifests/php.yml @@ -146,6 +146,8 @@ tests/: TestPathParameter: missing_feature test_uri.py: TestURI: missing_feature + test_security_controls.py: + TestSecurityControls: missing_feature rasp/: test_lfi.py: missing_feature test_libddwaf.py: missing_feature diff --git a/manifests/python.yml b/manifests/python.yml index 8bba17a4c8..081c2460f1 100644 --- a/manifests/python.yml +++ b/manifests/python.yml @@ -235,6 +235,8 @@ tests/: uwsgi-poc: v2.13.0 test_uri.py: TestURI: missing_feature + test_security_controls.py: + TestSecurityControls: missing_feature rasp/: test_lfi.py: Test_Lfi_BodyJson: v2.10.0 diff --git a/manifests/ruby.yml b/manifests/ruby.yml index ea2e17afc8..c507ba509a 100644 --- a/manifests/ruby.yml +++ b/manifests/ruby.yml @@ -147,6 +147,8 @@ tests/: TestPathParameter: missing_feature test_uri.py: TestURI: missing_feature + test_security_controls.py: + TestSecurityControls: missing_feature rasp/: test_lfi.py: missing_feature test_libddwaf.py: missing_feature diff --git a/tests/appsec/iast/test_security_controls.py b/tests/appsec/iast/test_security_controls.py new file mode 100644 index 0000000000..4b23a51f3d --- /dev/null +++ b/tests/appsec/iast/test_security_controls.py @@ -0,0 +1,134 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the the Apache License Version 2.0. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2021 Datadog, Inc. + +from utils import features, rfc, scenarios, weblog, interfaces +from tests.appsec.iast.utils import BaseSinkTest, assert_iast_vulnerability + + +@features.iast_security_controls +@rfc("https://docs.google.com/document/d/1j1hp87-2wJnXUGADZxzLnvKJmaF_Gd6ZR1hPS3LVguQ/edit?pli=1&tab=t.0") +@scenarios.iast_security_controls +class TestSecurityControls: + @staticmethod + def assert_iast_is_enabled(request): + product_enabled = False + for data, trace, span in interfaces.library.get_spans(request=request): + # Check if the product is enabled in meta + meta = span["meta"] + if "_dd.iast.json" in meta: + product_enabled = True + break + # Check if the product is enabled in meta_struct + meta_struct = span["meta_struct"] + if meta_struct and meta_struct.get("vulnerability"): + product_enabled = True + break + assert product_enabled, f"IAST is not available" + + def setup_iast_is_enabled(self): + self.check_r = weblog.post("/iast/sc/iv/sqli", data={"param": "param"}) + + def setup_vulnerability_suppression_with_an_input_validator_configured_for_a_specific_vulnerability(self): + self.setup_iast_is_enabled() + self.r = weblog.post("/iast/sc/iv/xss", data={"param": "param"}) + + def test_vulnerability_suppression_with_an_input_validator_configured_for_a_specific_vulnerability(self): + self.assert_iast_is_enabled(self.check_r) + BaseSinkTest.assert_no_iast_event(self.r, "XSS") + + def setup_no_vulnerability_suppression_with_an_input_validator_configured_for_a_different_vulnerability(self): + self.setup_iast_is_enabled() + self.r = weblog.post("/iast/sc/iv/sqli", data={"param": "param"}) + + def test_no_vulnerability_suppression_with_an_input_validator_configured_for_a_different_vulnerability(self): + self.assert_iast_is_enabled(self.check_r) + assert_iast_vulnerability( + request=self.r, vulnerability_count=1, vulnerability_type="SQL_INJECTION", + ) + + def setup_vulnerability_suppression_with_an_input_validator_configured_for_all_vulnerabilities(self): + self.setup_iast_is_enabled() + self.r = weblog.post("/iast/sc/iv/all", data={"param": "param"}) + + def test_vulnerability_suppression_with_an_input_validator_configured_for_all_vulnerabilities(self): + self.assert_iast_is_enabled(self.check_r) + BaseSinkTest.assert_no_iast_event(self.r, "SQL_INJECTION") + + def setup_vulnerability_suppression_with_an_input_validator_configured_for_an_overloaded_method_with_specific_signature( + self, + ): + self.setup_iast_is_enabled() + self.r = weblog.post("iast/sc/iv/overloaded/secure", data={"user": "usr1", "password": "pass"}) + + def test_vulnerability_suppression_with_an_input_validator_configured_for_an_overloaded_method_with_specific_signature( + self, + ): + self.assert_iast_is_enabled(self.check_r) + BaseSinkTest.assert_no_iast_event(self.r, "SQL_INJECTION") + + def setup_no_vulnerability_suppression_with_an_input_validator_configured_for_an_overloaded_method_with_specific_signature( + self, + ): + self.setup_iast_is_enabled() + self.r = weblog.post("iast/sc/iv/overloaded/insecure", data={"user": "usr1", "password": "pass"}) + + def test_no_vulnerability_suppression_with_an_input_validator_configured_for_an_overloaded_method_with_specific_signature( + self, + ): + self.assert_iast_is_enabled(self.check_r) + assert_iast_vulnerability( + request=self.r, vulnerability_count=1, vulnerability_type="SQL_INJECTION", + ) + + def setup_vulnerability_suppression_with_a_sanitizer_configured_for_a_specific_vulnerability(self): + self.setup_iast_is_enabled() + self.r = weblog.post("/iast/sc/s/xss", data={"param": "param"}) + + def test_vulnerability_suppression_with_a_sanitizer_configured_for_a_specific_vulnerability(self): + self.assert_iast_is_enabled(self.check_r) + BaseSinkTest.assert_no_iast_event(self.r, "XSS") + + def setup_no_vulnerability_suppression_with_a_sanitizer_configured_for_a_different_vulnerability(self): + self.setup_iast_is_enabled() + self.r = weblog.post("/iast/sc/s/sqli", data={"param": "param"}) + + def test_no_vulnerability_suppression_with_a_sanitizer_configured_for_a_different_vulnerability(self): + self.assert_iast_is_enabled(self.check_r) + assert_iast_vulnerability( + request=self.r, vulnerability_count=1, vulnerability_type="SQL_INJECTION", + ) + + def setup_vulnerability_suppression_with_a_sanitizer_configured_for_all_vulnerabilities(self): + self.setup_iast_is_enabled() + self.r = weblog.post("/iast/sc/s/all", data={"param": "param"}) + + def test_vulnerability_suppression_with_a_sanitizer_configured_for_all_vulnerabilities(self): + self.assert_iast_is_enabled(self.check_r) + BaseSinkTest.assert_no_iast_event(self.r, "SQL_INJECTION") + + def setup_vulnerability_suppression_with_a_sanitizer_configured_for_an_overloaded_method_with_specific_signature( + self, + ): + self.setup_iast_is_enabled() + self.r = weblog.post("iast/sc/s/overloaded/secure", data={"param": "param"}) + + def test_vulnerability_suppression_with_a_sanitizer_configured_for_an_overloaded_method_with_specific_signature( + self, + ): + self.assert_iast_is_enabled(self.check_r) + BaseSinkTest.assert_no_iast_event(self.r, "XSS") + + def setup_no_vulnerability_suppression_with_a_sanitizer_configured_for_an_overloaded_method_with_specific_signature( + self, + ): + self.setup_iast_is_enabled() + self.r = weblog.post("iast/sc/s/overloaded/insecure", data={"param": "param"}) + + def test_no_vulnerability_suppression_with_a_sanitizer_configured_for_an_overloaded_method_with_specific_signature( + self, + ): + self.assert_iast_is_enabled(self.check_r) + assert_iast_vulnerability( + request=self.r, vulnerability_count=1, vulnerability_type="XSS", + ) diff --git a/utils/_context/_scenarios/__init__.py b/utils/_context/_scenarios/__init__.py index a028451805..17155a6eb9 100644 --- a/utils/_context/_scenarios/__init__.py +++ b/utils/_context/_scenarios/__init__.py @@ -387,6 +387,16 @@ def all_endtoend_scenarios(test_object): scenario_groups=[ScenarioGroup.APPSEC], ) + iast_security_controls = EndToEndScenario( + "IAST_SECURITY_CONTROLS", + weblog_env={ + "DD_IAST_ENABLED": "true", + "DD_IAST_SECURITY_CONTROLS_CONFIGURATION": "SANITIZER:XSS:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:sanitize;SANITIZER:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:sanitizeForAllVulns;SANITIZER:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:overloadedSanitize:java.lang.String;INPUT_VALIDATOR:XSS:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:validate;INPUT_VALIDATOR:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:validateForAllVulns;INPUT_VALIDATOR:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:overloadedValidation:java.lang.Object,java.lang.String,java.lang.String:1,2", + }, + doc="Iast scenario with security controls", + scenario_groups=[ScenarioGroup.APPSEC], + ) + remote_config_mocked_backend_asm_features = EndToEndScenario( "REMOTE_CONFIG_MOCKED_BACKEND_ASM_FEATURES", rc_api_enabled=True, diff --git a/utils/_features.py b/utils/_features.py index 7db6614618..af60d0382f 100644 --- a/utils/_features.py +++ b/utils/_features.py @@ -2487,3 +2487,13 @@ def iast_stack_trace(test_object): """ pytest.mark.features(feature_id=329)(test_object) return test_object + + @staticmethod + def iast_security_controls(test_object): + """ + IAST: Security Controls + + https://feature-parity.us1.prod.dog/#/?feature=343 + """ + pytest.mark.features(feature_id=343)(test_object) + return test_object diff --git a/utils/build/docker/java/iast-common/src/main/java/com/datadoghq/system_tests/iast/utils/SecurityControlUtil.java b/utils/build/docker/java/iast-common/src/main/java/com/datadoghq/system_tests/iast/utils/SecurityControlUtil.java new file mode 100644 index 0000000000..254aef0085 --- /dev/null +++ b/utils/build/docker/java/iast-common/src/main/java/com/datadoghq/system_tests/iast/utils/SecurityControlUtil.java @@ -0,0 +1,37 @@ +package com.datadoghq.system_tests.iast.utils; + +public class SecurityControlUtil { + + public static String sanitize(String input) { + return "Sanitized " + input; + } + + public static String sanitizeForAllVulns(String input) { + return "Sanitized for all vulns " + input; + } + + public static String overloadedSanitize(String input) { + return "Sanitized " + input; + } + + public static String overloadedSanitize(String input, Object o) { + return "Sanitized " + input; + } + + public static boolean validate(String input) { + return true; // dummy implementation + } + + public static boolean validateForAllVulns(String input) { + return true; // dummy implementation + } + + public static boolean overloadedValidation(String input, String input2) { + return true; // dummy implementation + } + + public static boolean overloadedValidation(Object o, String input, String input2) { + return true; // dummy implementation + } + +} diff --git a/utils/build/docker/java/spring-boot/src/main/java/com/datadoghq/system_tests/springboot/AppSecIast.java b/utils/build/docker/java/spring-boot/src/main/java/com/datadoghq/system_tests/springboot/AppSecIast.java index 1010a79c97..5fa13580ac 100644 --- a/utils/build/docker/java/spring-boot/src/main/java/com/datadoghq/system_tests/springboot/AppSecIast.java +++ b/utils/build/docker/java/spring-boot/src/main/java/com/datadoghq/system_tests/springboot/AppSecIast.java @@ -380,6 +380,78 @@ public String secureUntrustedDeserialization(final HttpServletRequest request) t return "ok"; } + @PostMapping("/sc/s/xss") + void scSanitizeXSS(final ServletRequest request, final ServletResponse response) throws IOException { + String sanitized = SecurityControlUtil.sanitize(request.getParameter("param")); + xssExamples.insecureXSS(response.getWriter(), sanitized); + } + + @PostMapping("/sc/s/sqli") + Object scSanitizeSqli(final ServletRequest request, final ServletResponse response) throws IOException { + String sanitized = SecurityControlUtil.sanitize(request.getParameter("param")); + return sqlExamples.insecureSql(sanitized, "password"); + } + + @PostMapping("/sc/s/all") + Object scSanitizeForAllVulns(final ServletRequest request, final ServletResponse response) throws IOException { + String sanitized = SecurityControlUtil.sanitizeForAllVulns(request.getParameter("param")); + return sqlExamples.insecureSql(sanitized, "password"); + } + + @PostMapping("/sc/iv/xss") + void scValidateXSS(final ServletRequest request, final ServletResponse response) throws IOException { + String param = request.getParameter("param"); + if (SecurityControlUtil.validate(param)) { + xssExamples.insecureXSS(response.getWriter(), param); + } + } + + @PostMapping("/sc/iv/sqli") + void scValidateSqli(final ServletRequest request, final ServletResponse response) throws IOException { + String param = request.getParameter("param"); + if(SecurityControlUtil.validate(param)) { + sqlExamples.insecureSql(param, "password"); + } + } + + @PostMapping("/sc/iv/all") + void scValidateForAllVulns(final ServletRequest request, final ServletResponse response) throws IOException { + String param = request.getParameter("param"); + if(SecurityControlUtil.validateForAllVulns(param)) { + sqlExamples.insecureSql(param, "password"); + } + } + + @PostMapping("/sc/iv/overloaded/secure") + void scIVOverloadedSecure(final ServletRequest request, final ServletResponse response) throws IOException { + String user = request.getParameter("user"); + String pass = request.getParameter("password"); + if(SecurityControlUtil.overloadedValidation(null, user, pass)) { + sqlExamples.insecureSql(user, pass); + } + } + + @PostMapping("/sc/iv/overloaded/insecure") + void scIVOverloadedInsecure(final ServletRequest request, final ServletResponse response) throws IOException { + String user = request.getParameter("user"); + String pass = request.getParameter("password"); + if(SecurityControlUtil.overloadedValidation(user, pass)) { + sqlExamples.insecureSql(user, pass); + } + } + + @PostMapping("/sc/s/overloaded/secure") + void scSOverloadedSecure(final ServletRequest request, final ServletResponse response) throws IOException { + String sanitized = SecurityControlUtil.overloadedSanitize(request.getParameter("param")); + xssExamples.insecureXSS(response.getWriter(), sanitized); + } + + @PostMapping("/sc/s/overloaded/insecure") + void scSOverloadedInsecure(final ServletRequest request, final ServletResponse response) throws IOException { + String sanitized = SecurityControlUtil.overloadedSanitize(request.getParameter("param"), null); + xssExamples.insecureXSS(response.getWriter(), sanitized); + } + /** * TODO: Ldap is failing to startup in native image this method ensures it's started lazily From bac103c158fe799ed2e979534f72c4d1c48002bb Mon Sep 17 00:00:00 2001 From: Charles de Beauchesne Date: Thu, 28 Nov 2024 13:07:57 +0100 Subject: [PATCH 02/10] Adds a map of properties per languages --- .github/workflows/run-end-to-end.yml | 3 --- tests/appsec/iast/test_security_controls.py | 7 +++---- utils/_context/_scenarios/__init__.py | 10 ---------- utils/_context/_scenarios/default.py | 21 +++++++++++++++++++++ 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/.github/workflows/run-end-to-end.yml b/.github/workflows/run-end-to-end.yml index a49826ea8e..630a1f56b0 100644 --- a/.github/workflows/run-end-to-end.yml +++ b/.github/workflows/run-end-to-end.yml @@ -112,9 +112,6 @@ jobs: - name: Run IAST_DEDUPLICATION scenario if: always() && steps.build.outcome == 'success' && contains(inputs.scenarios, '"IAST_DEDUPLICATION"') run: ./run.sh IAST_DEDUPLICATION - - name: Run IAST_SECURITY_CONTROLS scenario - if: always() && steps.build.outcome == 'success' && contains(inputs.scenarios, '"IAST_SECURITY_CONTROLS"') - run: ./run.sh IAST_SECURITY_CONTROLS - name: Run DEFAULT scenario if: always() && steps.build.outcome == 'success' && contains(inputs.scenarios, '"DEFAULT"') run: ./run.sh DEFAULT diff --git a/tests/appsec/iast/test_security_controls.py b/tests/appsec/iast/test_security_controls.py index 4b23a51f3d..641b663f2a 100644 --- a/tests/appsec/iast/test_security_controls.py +++ b/tests/appsec/iast/test_security_controls.py @@ -2,18 +2,17 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. -from utils import features, rfc, scenarios, weblog, interfaces +from utils import features, rfc, weblog, interfaces from tests.appsec.iast.utils import BaseSinkTest, assert_iast_vulnerability @features.iast_security_controls @rfc("https://docs.google.com/document/d/1j1hp87-2wJnXUGADZxzLnvKJmaF_Gd6ZR1hPS3LVguQ/edit?pli=1&tab=t.0") -@scenarios.iast_security_controls class TestSecurityControls: @staticmethod def assert_iast_is_enabled(request): product_enabled = False - for data, trace, span in interfaces.library.get_spans(request=request): + for _, _, span in interfaces.library.get_spans(request=request): # Check if the product is enabled in meta meta = span["meta"] if "_dd.iast.json" in meta: @@ -24,7 +23,7 @@ def assert_iast_is_enabled(request): if meta_struct and meta_struct.get("vulnerability"): product_enabled = True break - assert product_enabled, f"IAST is not available" + assert product_enabled, "IAST is not available" def setup_iast_is_enabled(self): self.check_r = weblog.post("/iast/sc/iv/sqli", data={"param": "param"}) diff --git a/utils/_context/_scenarios/__init__.py b/utils/_context/_scenarios/__init__.py index 17155a6eb9..a028451805 100644 --- a/utils/_context/_scenarios/__init__.py +++ b/utils/_context/_scenarios/__init__.py @@ -387,16 +387,6 @@ def all_endtoend_scenarios(test_object): scenario_groups=[ScenarioGroup.APPSEC], ) - iast_security_controls = EndToEndScenario( - "IAST_SECURITY_CONTROLS", - weblog_env={ - "DD_IAST_ENABLED": "true", - "DD_IAST_SECURITY_CONTROLS_CONFIGURATION": "SANITIZER:XSS:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:sanitize;SANITIZER:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:sanitizeForAllVulns;SANITIZER:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:overloadedSanitize:java.lang.String;INPUT_VALIDATOR:XSS:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:validate;INPUT_VALIDATOR:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:validateForAllVulns;INPUT_VALIDATOR:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:overloadedValidation:java.lang.Object,java.lang.String,java.lang.String:1,2", - }, - doc="Iast scenario with security controls", - scenario_groups=[ScenarioGroup.APPSEC], - ) - remote_config_mocked_backend_asm_features = EndToEndScenario( "REMOTE_CONFIG_MOCKED_BACKEND_ASM_FEATURES", rc_api_enabled=True, diff --git a/utils/_context/_scenarios/default.py b/utils/_context/_scenarios/default.py index cedb669f0d..e938dea577 100644 --- a/utils/_context/_scenarios/default.py +++ b/utils/_context/_scenarios/default.py @@ -2,6 +2,20 @@ from .endtoend import EndToEndScenario +# TODO : short explaination of the purpose of this value +# a link to the RFC would be perfect +_iast_security_controls_map = { + "cpp": "TODO", + "dotnet": "TODO", + "golang": "TODO", + "java": "SANITIZER:XSS:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:sanitize;SANITIZER:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:sanitizeForAllVulns;SANITIZER:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:overloadedSanitize:java.lang.String;INPUT_VALIDATOR:XSS:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:validate;INPUT_VALIDATOR:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:validateForAllVulns;INPUT_VALIDATOR:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:overloadedValidation:java.lang.Object,java.lang.String,java.lang.String:1,2", + "nodejs": "TODO", + "php": "TODO", + "python": "TODO", + "ruby": "TODO", +} + + class DefaultScenario(EndToEndScenario): def __init__(self, name: str): super().__init__( @@ -18,3 +32,10 @@ def __init__(self, name: str): scenario_groups=[ScenarioGroup.ESSENTIALS], doc="Default scenario, spawn tracer, the Postgres databases and agent, and run most of exisiting tests", ) + + def configure(self, config): + super().configure(config) + + self.weblog_container.environment["DD_IAST_SECURITY_CONTROLS_CONFIGURATION"] = _iast_security_controls_map[ + self.weblog_container.library.library + ] From 90dfa9e7babcb2e210b43856d432d9bc0f8c271b Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Thu, 28 Nov 2024 15:37:11 +0100 Subject: [PATCH 03/10] Add documentation --- docs/weblog/README.md | 44 ++++++++++++++++++++++++++++ utils/_context/_scenarios/default.py | 4 +-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/docs/weblog/README.md b/docs/weblog/README.md index 2ce6d69223..37b60a1be0 100644 --- a/docs/weblog/README.md +++ b/docs/weblog/README.md @@ -305,6 +305,50 @@ A POST request which will receive the following JSON body: Where the value for `value` must be used in the vulnerability. +### POST /iast/sc/* + +These group of endpoints should trigger vulnerabilities detected by IAST with untrusted data coming from certain sources although the data is validated or sanitized by a configured security control + +#### POST /iast/sc/s/xss + +A post request using a parameter 'param' with a value that triggers a XSS vulnerability. The value should be sanitized by a sanitizer security control configured for XSS vulnerabilities. + +#### POST /sc/s/sqli + +A post request using a parameter 'param' with a value that triggers a SQL injection vulnerability. The value should be sanitized by a sanitizer security control that is not configured for SQL injection vulnerabilities. + +#### POST /sc/s/all + +A post request using a parameter 'param' with a value that triggers a SQL injection vulnerability. The value should be sanitized by a sanitizer security control configured for all vulnerabilities. + +#### POST /sc/iv/xss + +A post request using a parameter 'param' with a value that triggers a XSS vulnerability. The value should be validated by an input validator security control configured for XSS vulnerabilities. + +#### POST /sc/iv/sqli + +A post request using a parameter 'param' with a value that triggers a SQL injection vulnerability. The value should be validated by an input validator security control that is not configured for SQL injection vulnerabilities. + +#### POST /sc/iv/all + +A post request using a parameter 'param' with a value that triggers a SQL injection vulnerability. The value should be validated by an input validator security control configured for all vulnerabilities. + +#### POST /sc/iv/overloaded/secure + +A post request using two parameters 'user' and 'password' that triggers a SQL injection vulnerability. The values should be validated by an input validator security control with an overloaded method configured for all vulnerabilities. + +#### POST /sc/iv/overloaded/insecure + +A post request using two parameters 'user' and 'password' that triggers a SQL injection vulnerability. The values should be validated by an input validator security control with an overloaded method configured for other method signature. + +#### POST /sc/s/overloaded/secure + +A post request using a parameter 'param' with a value that triggers a XSS vulnerability. The value should be sanitized by a sanitizer security control with an overloaded method configured for all vulnerabilities. + +#### POST /sc/s/overloaded/insecure + +A post request using a parameter 'param' with a value that triggers a XSS vulnerability. The value should be sanitized by a sanitizer security control with an overloaded method configured for other method signature. + ### GET /make_distant_call This endpoint accept a mandatory parameter `url`. It'll make a call to these url, and should returns a JSON response : diff --git a/utils/_context/_scenarios/default.py b/utils/_context/_scenarios/default.py index e938dea577..2231b139c2 100644 --- a/utils/_context/_scenarios/default.py +++ b/utils/_context/_scenarios/default.py @@ -2,8 +2,8 @@ from .endtoend import EndToEndScenario -# TODO : short explaination of the purpose of this value -# a link to the RFC would be perfect +# When Security Controls configuration is set, tracers must instrument all the designated methods in the configuration as security controls. +# RFC(https://docs.google.com/document/d/1j1hp87-2wJnXUGADZxzLnvKJmaF_Gd6ZR1hPS3LVguQ/edit?pli=1&tab=t.0) _iast_security_controls_map = { "cpp": "TODO", "dotnet": "TODO", From e8b0bac33009738875ab170cd62c4734d6038784 Mon Sep 17 00:00:00 2001 From: Charles de Beauchesne Date: Fri, 29 Nov 2024 10:02:43 +0100 Subject: [PATCH 04/10] Fix scenario configure --- utils/_context/_scenarios/default.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/_context/_scenarios/default.py b/utils/_context/_scenarios/default.py index 2231b139c2..b4ee9fa10d 100644 --- a/utils/_context/_scenarios/default.py +++ b/utils/_context/_scenarios/default.py @@ -36,6 +36,6 @@ def __init__(self, name: str): def configure(self, config): super().configure(config) - self.weblog_container.environment["DD_IAST_SECURITY_CONTROLS_CONFIGURATION"] = _iast_security_controls_map[ - self.weblog_container.library.library - ] + library = self.weblog_container.image.env["SYSTEM_TESTS_LIBRARY"] + value = _iast_security_controls_map[library] + self.weblog_container.environment["DD_IAST_SECURITY_CONTROLS_CONFIGURATION"] = value From 0d6a8b4c6d406409a9efcf2e976cdd69a9429493 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Fri, 29 Nov 2024 10:45:17 +0100 Subject: [PATCH 05/10] Change documentation and XSS for COMMAND_INJECTION --- docs/weblog/README.md | 28 +++++++++---------- tests/appsec/iast/test_security_controls.py | 18 ++++++------ utils/_context/_scenarios/default.py | 2 +- .../system_tests/springboot/AppSecIast.java | 18 ++++++------ 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/docs/weblog/README.md b/docs/weblog/README.md index 37b60a1be0..ea15d90e73 100644 --- a/docs/weblog/README.md +++ b/docs/weblog/README.md @@ -309,45 +309,45 @@ Where the value for `value` must be used in the vulnerability. These group of endpoints should trigger vulnerabilities detected by IAST with untrusted data coming from certain sources although the data is validated or sanitized by a configured security control -#### POST /iast/sc/s/xss +#### POST /iast/sc/s/configured -A post request using a parameter 'param' with a value that triggers a XSS vulnerability. The value should be sanitized by a sanitizer security control configured for XSS vulnerabilities. +A post request using a parameter with a value that triggers a vulnerability. The value should be sanitized by a sanitizer security control configured for this vulnerability. -#### POST /sc/s/sqli +#### POST /sc/s/not-configured -A post request using a parameter 'param' with a value that triggers a SQL injection vulnerability. The value should be sanitized by a sanitizer security control that is not configured for SQL injection vulnerabilities. +A post request using a parameter with a value that triggers a vulnerability. The value should be sanitized by a sanitizer security control that is not configured for this vulnerability. #### POST /sc/s/all -A post request using a parameter 'param' with a value that triggers a SQL injection vulnerability. The value should be sanitized by a sanitizer security control configured for all vulnerabilities. +A post request using a parameter with a value that triggers a vulnerability. The value should be sanitized by a sanitizer security control configured for all vulnerabilities. -#### POST /sc/iv/xss +#### POST /sc/iv/configured -A post request using a parameter 'param' with a value that triggers a XSS vulnerability. The value should be validated by an input validator security control configured for XSS vulnerabilities. +A post request using a parameter with a value that triggers a vulnerability. The value should be validated by an input validator security control configured for this vulnerability. -#### POST /sc/iv/sqli +#### POST /sc/iv/not-configured -A post request using a parameter 'param' with a value that triggers a SQL injection vulnerability. The value should be validated by an input validator security control that is not configured for SQL injection vulnerabilities. +A post request using a parameter with a value that triggers a vulnerability. The value should be validated by an input validator security control that is not configured for this vulnerability. #### POST /sc/iv/all -A post request using a parameter 'param' with a value that triggers a SQL injection vulnerability. The value should be validated by an input validator security control configured for all vulnerabilities. +A post request using a parameter with a value that triggers a vulnerability. The value should be validated by an input validator security control configured for all vulnerabilities. #### POST /sc/iv/overloaded/secure -A post request using two parameters 'user' and 'password' that triggers a SQL injection vulnerability. The values should be validated by an input validator security control with an overloaded method configured for all vulnerabilities. +A post request using two parameters that triggers a vulnerability. The values should be validated by an input validator security control with an overloaded method configured for all vulnerabilities. #### POST /sc/iv/overloaded/insecure -A post request using two parameters 'user' and 'password' that triggers a SQL injection vulnerability. The values should be validated by an input validator security control with an overloaded method configured for other method signature. +A post request using two parameters that triggers a vulnerability. The values should be validated by an input validator security control with an overloaded method configured for other method signature. #### POST /sc/s/overloaded/secure -A post request using a parameter 'param' with a value that triggers a XSS vulnerability. The value should be sanitized by a sanitizer security control with an overloaded method configured for all vulnerabilities. +A post request using a parameter with a value that triggers a vulnerability. The value should be sanitized by a sanitizer security control with an overloaded method configured for all vulnerabilities. #### POST /sc/s/overloaded/insecure -A post request using a parameter 'param' with a value that triggers a XSS vulnerability. The value should be sanitized by a sanitizer security control with an overloaded method configured for other method signature. +A post request using a parameter with a value that triggers a vulnerability. The value should be sanitized by a sanitizer security control with an overloaded method configured for other method signature. ### GET /make_distant_call diff --git a/tests/appsec/iast/test_security_controls.py b/tests/appsec/iast/test_security_controls.py index 641b663f2a..90978e80ed 100644 --- a/tests/appsec/iast/test_security_controls.py +++ b/tests/appsec/iast/test_security_controls.py @@ -26,19 +26,19 @@ def assert_iast_is_enabled(request): assert product_enabled, "IAST is not available" def setup_iast_is_enabled(self): - self.check_r = weblog.post("/iast/sc/iv/sqli", data={"param": "param"}) + self.check_r = weblog.post("/iast/sc/iv/not-configured", data={"param": "param"}) def setup_vulnerability_suppression_with_an_input_validator_configured_for_a_specific_vulnerability(self): self.setup_iast_is_enabled() - self.r = weblog.post("/iast/sc/iv/xss", data={"param": "param"}) + self.r = weblog.post("/iast/sc/iv/configured", data={"param": "param"}) def test_vulnerability_suppression_with_an_input_validator_configured_for_a_specific_vulnerability(self): self.assert_iast_is_enabled(self.check_r) - BaseSinkTest.assert_no_iast_event(self.r, "XSS") + BaseSinkTest.assert_no_iast_event(self.r, "COMMAND_INJECTION") def setup_no_vulnerability_suppression_with_an_input_validator_configured_for_a_different_vulnerability(self): self.setup_iast_is_enabled() - self.r = weblog.post("/iast/sc/iv/sqli", data={"param": "param"}) + self.r = weblog.post("/iast/sc/iv/not-configured", data={"param": "param"}) def test_no_vulnerability_suppression_with_an_input_validator_configured_for_a_different_vulnerability(self): self.assert_iast_is_enabled(self.check_r) @@ -82,15 +82,15 @@ def test_no_vulnerability_suppression_with_an_input_validator_configured_for_an_ def setup_vulnerability_suppression_with_a_sanitizer_configured_for_a_specific_vulnerability(self): self.setup_iast_is_enabled() - self.r = weblog.post("/iast/sc/s/xss", data={"param": "param"}) + self.r = weblog.post("/iast/sc/s/configured", data={"param": "param"}) def test_vulnerability_suppression_with_a_sanitizer_configured_for_a_specific_vulnerability(self): self.assert_iast_is_enabled(self.check_r) - BaseSinkTest.assert_no_iast_event(self.r, "XSS") + BaseSinkTest.assert_no_iast_event(self.r, "COMMAND_INJECTION") def setup_no_vulnerability_suppression_with_a_sanitizer_configured_for_a_different_vulnerability(self): self.setup_iast_is_enabled() - self.r = weblog.post("/iast/sc/s/sqli", data={"param": "param"}) + self.r = weblog.post("/iast/sc/s/not-configured", data={"param": "param"}) def test_no_vulnerability_suppression_with_a_sanitizer_configured_for_a_different_vulnerability(self): self.assert_iast_is_enabled(self.check_r) @@ -116,7 +116,7 @@ def test_vulnerability_suppression_with_a_sanitizer_configured_for_an_overloaded self, ): self.assert_iast_is_enabled(self.check_r) - BaseSinkTest.assert_no_iast_event(self.r, "XSS") + BaseSinkTest.assert_no_iast_event(self.r, "COMMAND_INJECTION") def setup_no_vulnerability_suppression_with_a_sanitizer_configured_for_an_overloaded_method_with_specific_signature( self, @@ -129,5 +129,5 @@ def test_no_vulnerability_suppression_with_a_sanitizer_configured_for_an_overloa ): self.assert_iast_is_enabled(self.check_r) assert_iast_vulnerability( - request=self.r, vulnerability_count=1, vulnerability_type="XSS", + request=self.r, vulnerability_count=1, vulnerability_type="COMMAND_INJECTION", ) diff --git a/utils/_context/_scenarios/default.py b/utils/_context/_scenarios/default.py index b4ee9fa10d..cc118255f6 100644 --- a/utils/_context/_scenarios/default.py +++ b/utils/_context/_scenarios/default.py @@ -8,7 +8,7 @@ "cpp": "TODO", "dotnet": "TODO", "golang": "TODO", - "java": "SANITIZER:XSS:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:sanitize;SANITIZER:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:sanitizeForAllVulns;SANITIZER:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:overloadedSanitize:java.lang.String;INPUT_VALIDATOR:XSS:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:validate;INPUT_VALIDATOR:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:validateForAllVulns;INPUT_VALIDATOR:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:overloadedValidation:java.lang.Object,java.lang.String,java.lang.String:1,2", + "java": "SANITIZER:COMMAND_INJECTION:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:sanitize;SANITIZER:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:sanitizeForAllVulns;SANITIZER:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:overloadedSanitize:java.lang.String;INPUT_VALIDATOR:COMMAND_INJECTION:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:validate;INPUT_VALIDATOR:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:validateForAllVulns;INPUT_VALIDATOR:*:com.datadoghq.system_tests.iast.utils.SecurityControlUtil:overloadedValidation:java.lang.Object,java.lang.String,java.lang.String:1,2", "nodejs": "TODO", "php": "TODO", "python": "TODO", diff --git a/utils/build/docker/java/spring-boot/src/main/java/com/datadoghq/system_tests/springboot/AppSecIast.java b/utils/build/docker/java/spring-boot/src/main/java/com/datadoghq/system_tests/springboot/AppSecIast.java index 5fa13580ac..62754180c8 100644 --- a/utils/build/docker/java/spring-boot/src/main/java/com/datadoghq/system_tests/springboot/AppSecIast.java +++ b/utils/build/docker/java/spring-boot/src/main/java/com/datadoghq/system_tests/springboot/AppSecIast.java @@ -380,13 +380,13 @@ public String secureUntrustedDeserialization(final HttpServletRequest request) t return "ok"; } - @PostMapping("/sc/s/xss") - void scSanitizeXSS(final ServletRequest request, final ServletResponse response) throws IOException { + @PostMapping("/sc/s/configured") + void scSanitizeConfigured(final ServletRequest request, final ServletResponse response) throws IOException { String sanitized = SecurityControlUtil.sanitize(request.getParameter("param")); - xssExamples.insecureXSS(response.getWriter(), sanitized); + cmdExamples.insecureCmd(sanitized); } - @PostMapping("/sc/s/sqli") + @PostMapping("/sc/s/not-configured") Object scSanitizeSqli(final ServletRequest request, final ServletResponse response) throws IOException { String sanitized = SecurityControlUtil.sanitize(request.getParameter("param")); return sqlExamples.insecureSql(sanitized, "password"); @@ -398,15 +398,15 @@ Object scSanitizeForAllVulns(final ServletRequest request, final ServletRespons return sqlExamples.insecureSql(sanitized, "password"); } - @PostMapping("/sc/iv/xss") + @PostMapping("/sc/iv/configured") void scValidateXSS(final ServletRequest request, final ServletResponse response) throws IOException { String param = request.getParameter("param"); if (SecurityControlUtil.validate(param)) { - xssExamples.insecureXSS(response.getWriter(), param); + cmdExamples.insecureCmd(param); } } - @PostMapping("/sc/iv/sqli") + @PostMapping("/sc/iv/not-configured") void scValidateSqli(final ServletRequest request, final ServletResponse response) throws IOException { String param = request.getParameter("param"); if(SecurityControlUtil.validate(param)) { @@ -443,13 +443,13 @@ void scIVOverloadedInsecure(final ServletRequest request, final ServletResponse @PostMapping("/sc/s/overloaded/secure") void scSOverloadedSecure(final ServletRequest request, final ServletResponse response) throws IOException { String sanitized = SecurityControlUtil.overloadedSanitize(request.getParameter("param")); - xssExamples.insecureXSS(response.getWriter(), sanitized); + cmdExamples.insecureCmd(sanitized); } @PostMapping("/sc/s/overloaded/insecure") void scSOverloadedInsecure(final ServletRequest request, final ServletResponse response) throws IOException { String sanitized = SecurityControlUtil.overloadedSanitize(request.getParameter("param"), null); - xssExamples.insecureXSS(response.getWriter(), sanitized); + cmdExamples.insecureCmd(sanitized); } From da57dc61d0bd16191d5a6b325f9a2258d25027d8 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Fri, 29 Nov 2024 11:39:00 +0100 Subject: [PATCH 06/10] Enable for vertx3 --- manifests/java.yml | 1 + .../iast/routes/IastSinkRouteProvider.java | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/manifests/java.yml b/manifests/java.yml index 69e4cc5201..7cbe317cc6 100644 --- a/manifests/java.yml +++ b/manifests/java.yml @@ -566,6 +566,7 @@ tests/: TestSecurityControls: '*': missing_feature (No endpoint implemented) spring-boot: v1.44.0 + vertx3: v1.44.0 spring-boot-3-native: missing_feature (GraalVM. Tracing support only) rasp/: test_lfi.py: diff --git a/utils/build/docker/java/vertx3/src/main/java/com/datadoghq/vertx3/iast/routes/IastSinkRouteProvider.java b/utils/build/docker/java/vertx3/src/main/java/com/datadoghq/vertx3/iast/routes/IastSinkRouteProvider.java index 586299ca8f..46f22b1c45 100644 --- a/utils/build/docker/java/vertx3/src/main/java/com/datadoghq/vertx3/iast/routes/IastSinkRouteProvider.java +++ b/utils/build/docker/java/vertx3/src/main/java/com/datadoghq/vertx3/iast/routes/IastSinkRouteProvider.java @@ -159,5 +159,84 @@ public void accept(final Router router) { final String param = request.getParam("param"); ctx.response().end(reflection.insecureClassForName(param)); }); + router.post("/iast/sc/s/configured").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String sanitized = SecurityControlUtil.sanitize(request.getParam("param")); + cmd.insecureCmd(sanitized); + ctx.response().end(); + }); + + router.post("/iast/sc/s/not-configured").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String sanitized = SecurityControlUtil.sanitize(request.getParam("param")); + ctx.response().end(Json.encodeToBuffer(sql.insecureSql(sanitized, "password"))); + }); + + router.post("/iast/sc/s/all").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String sanitized = SecurityControlUtil.sanitizeForAllVulns(request.getParam("param")); + ctx.response().end(Json.encodeToBuffer(sql.insecureSql(sanitized, "password"))); + }); + + router.post("/iast/sc/iv/configured").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String param = request.getParam("param"); + if (SecurityControlUtil.validate(param)) { + cmd.insecureCmd(param); + } + ctx.response().end(); + }); + + router.post("/iast/sc/iv/not-configured").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String param = request.getParam("param"); + if (SecurityControlUtil.validate(param)) { + sql.insecureSql(param, "password"); + } + ctx.response().end(); + }); + + router.post("/iast/sc/iv/all").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String param = request.getParam("param"); + if (SecurityControlUtil.validateForAllVulns(param)) { + sql.insecureSql(param, "password"); + } + ctx.response().end(); + }); + + router.post("/iast/sc/iv/overloaded/secure").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String user = request.getParam("user"); + String pass = request.getParam("password"); + if (SecurityControlUtil.overloadedValidation(null, user, pass)) { + sql.insecureSql(user, pass); + } + ctx.response().end(); + }); + + router.post("/iast/sc/iv/overloaded/insecure").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String user = request.getParam("user"); + String pass = request.getParam("password"); + if (SecurityControlUtil.overloadedValidation(user, pass)) { + sql.insecureSql(user, pass); + } + ctx.response().end(); + }); + + router.post("/iast/sc/s/overloaded/secure").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String sanitized = SecurityControlUtil.overloadedSanitize(request.getParam("param")); + cmd.insecureCmd(sanitized); + ctx.response().end(); + }); + + router.post("/iast/sc/s/overloaded/insecure").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String sanitized = SecurityControlUtil.overloadedSanitize(request.getParam("param"), null); + cmd.insecureCmd(sanitized); + ctx.response().end(); + }); } } From a4badd51c0d80df083e4b4907f1fcab1b1e866b0 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Fri, 29 Nov 2024 11:53:12 +0100 Subject: [PATCH 07/10] Enable for vertx4 --- manifests/java.yml | 1 + .../iast/routes/IastSinkRouteProvider.java | 78 +++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/manifests/java.yml b/manifests/java.yml index 7cbe317cc6..bf8b03d57b 100644 --- a/manifests/java.yml +++ b/manifests/java.yml @@ -567,6 +567,7 @@ tests/: '*': missing_feature (No endpoint implemented) spring-boot: v1.44.0 vertx3: v1.44.0 + vertx4: v1.44.0 spring-boot-3-native: missing_feature (GraalVM. Tracing support only) rasp/: test_lfi.py: diff --git a/utils/build/docker/java/vertx4/src/main/java/com/datadoghq/vertx4/iast/routes/IastSinkRouteProvider.java b/utils/build/docker/java/vertx4/src/main/java/com/datadoghq/vertx4/iast/routes/IastSinkRouteProvider.java index 79a824f205..3e171d1ae0 100644 --- a/utils/build/docker/java/vertx4/src/main/java/com/datadoghq/vertx4/iast/routes/IastSinkRouteProvider.java +++ b/utils/build/docker/java/vertx4/src/main/java/com/datadoghq/vertx4/iast/routes/IastSinkRouteProvider.java @@ -164,6 +164,84 @@ public void accept(final Router router) { final String param = request.getParam("param"); ctx.response().end(reflection.insecureClassForName(param)); }); + router.post("/iast/sc/s/configured").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String sanitized = SecurityControlUtil.sanitize(request.getParam("param")); + cmd.insecureCmd(sanitized); + ctx.response().end(); + }); + + router.post("/iast/sc/s/not-configured").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String sanitized = SecurityControlUtil.sanitize(request.getParam("param")); + ctx.response().end(Json.encodeToBuffer(sql.insecureSql(sanitized, "password"))); + }); + + router.post("/iast/sc/s/all").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String sanitized = SecurityControlUtil.sanitizeForAllVulns(request.getParam("param")); + ctx.response().end(Json.encodeToBuffer(sql.insecureSql(sanitized, "password"))); + }); + + router.post("/iast/sc/iv/configured").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String param = request.getParam("param"); + if (SecurityControlUtil.validate(param)) { + cmd.insecureCmd(param); + } + ctx.response().end(); + }); + + router.post("/iast/sc/iv/not-configured").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String param = request.getParam("param"); + if (SecurityControlUtil.validate(param)) { + sql.insecureSql(param, "password"); + } + ctx.response().end(); + }); + router.post("/iast/sc/iv/all").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String param = request.getParam("param"); + if (SecurityControlUtil.validateForAllVulns(param)) { + sql.insecureSql(param, "password"); + } + ctx.response().end(); + }); + + router.post("/iast/sc/iv/overloaded/secure").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String user = request.getParam("user"); + String pass = request.getParam("password"); + if (SecurityControlUtil.overloadedValidation(null, user, pass)) { + sql.insecureSql(user, pass); + } + ctx.response().end(); + }); + + router.post("/iast/sc/iv/overloaded/insecure").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String user = request.getParam("user"); + String pass = request.getParam("password"); + if (SecurityControlUtil.overloadedValidation(user, pass)) { + sql.insecureSql(user, pass); + } + ctx.response().end(); + }); + + router.post("/iast/sc/s/overloaded/secure").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String sanitized = SecurityControlUtil.overloadedSanitize(request.getParam("param")); + cmd.insecureCmd(sanitized); + ctx.response().end(); + }); + + router.post("/iast/sc/s/overloaded/insecure").handler(ctx -> { + final HttpServerRequest request = ctx.request(); + String sanitized = SecurityControlUtil.overloadedSanitize(request.getParam("param"), null); + cmd.insecureCmd(sanitized); + ctx.response().end(); + }); } } From 8e161110e0a8b6da15c99ca55303eb25570ef25d Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Fri, 29 Nov 2024 12:51:52 +0100 Subject: [PATCH 08/10] Enable for resteasy-netty3 --- manifests/java.yml | 3 +- .../datadoghq/resteasy/IastSinkResource.java | 83 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/manifests/java.yml b/manifests/java.yml index bf8b03d57b..d7aeff34cc 100644 --- a/manifests/java.yml +++ b/manifests/java.yml @@ -566,9 +566,10 @@ tests/: TestSecurityControls: '*': missing_feature (No endpoint implemented) spring-boot: v1.44.0 + resteasy-netty3: v1.44.0 + spring-boot-3-native: missing_feature (GraalVM. Tracing support only) vertx3: v1.44.0 vertx4: v1.44.0 - spring-boot-3-native: missing_feature (GraalVM. Tracing support only) rasp/: test_lfi.py: Test_Lfi_BodyJson: diff --git a/utils/build/docker/java/resteasy-netty3/src/main/java/com/datadoghq/resteasy/IastSinkResource.java b/utils/build/docker/java/resteasy-netty3/src/main/java/com/datadoghq/resteasy/IastSinkResource.java index c70ab8fc0a..57e8c7610d 100644 --- a/utils/build/docker/java/resteasy-netty3/src/main/java/com/datadoghq/resteasy/IastSinkResource.java +++ b/utils/build/docker/java/resteasy-netty3/src/main/java/com/datadoghq/resteasy/IastSinkResource.java @@ -271,4 +271,87 @@ public String insecureReflection(@FormParam("param") final String className) { reflectionExamples.insecureClassForName(className); return "Insecure"; } + + @POST + @Path("/sc/s/configured") + public String scSanitizeConfigured(@FormParam("param") String param){ + String sanitized = SecurityControlUtil.sanitize(param); + cmd.insecureCmd(sanitized); + return "ok"; + } + + @POST + @Path("/sc/s/not-configured") + public Object scSanitizeSqli(@FormParam("param") String param){ + String sanitized = SecurityControlUtil.sanitize(param); + return sql.insecureSql(sanitized, "password"); + } + + @POST + @Path("/sc/s/all") + public Object scSanitizeForAllVulns(@FormParam("param") String param){ + String sanitized = SecurityControlUtil.sanitizeForAllVulns(param); + return sql.insecureSql(sanitized, "password"); + } + + @POST + @Path("/sc/iv/configured") + public String scValidateXSS(@FormParam("param") String param){ + if (SecurityControlUtil.validate(param)) { + cmd.insecureCmd(param); + } + return "ok"; + } + + @POST + @Path("/sc/iv/not-configured") + public String scValidateSqli(@FormParam("param") String param){ + if (SecurityControlUtil.validate(param)) { + sql.insecureSql(param, "password"); + } + return "ok"; + } + + @POST + @Path("/sc/iv/all") + public String scValidateForAllVulns(@FormParam("param") String param){ + if (SecurityControlUtil.validateForAllVulns(param)) { + sql.insecureSql(param, "password"); + } + return "ok"; + } + + @POST + @Path("/sc/iv/overloaded/secure") + public String scIVOverloadedSecure(@FormParam("user") String user, @FormParam("password") String pass){ + if (SecurityControlUtil.overloadedValidation(null, user, pass)) { + sql.insecureSql(user, pass); + } + return "ok"; + } + + @POST + @Path("/sc/iv/overloaded/insecure") + public String scIVOverloadedInsecure(@FormParam("user") String user, @FormParam("password") String pass){ + if (SecurityControlUtil.overloadedValidation(user, pass)) { + sql.insecureSql(user, pass); + } + return "ok"; + } + + @POST + @Path("/sc/s/overloaded/secure") + public String scSOverloadedSecure(@FormParam("param") String param){ + String sanitized = SecurityControlUtil.overloadedSanitize(param); + cmd.insecureCmd(sanitized); + return "ok"; + } + + @POST + @Path("/sc/s/overloaded/insecure") + public String scSOverloadedInsecure(@FormParam("param") String param){ + String sanitized = SecurityControlUtil.overloadedSanitize(param, null); + cmd.insecureCmd(sanitized); + return "ok"; + } } From ddb1a06c243aa17cfba962b3691b129b3e278735 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Fri, 29 Nov 2024 13:29:02 +0100 Subject: [PATCH 09/10] Enable for jersey-grizzly2 --- manifests/java.yml | 1 + .../datadoghq/jersey/IastSinkResource.java | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/manifests/java.yml b/manifests/java.yml index d7aeff34cc..b98d2ed693 100644 --- a/manifests/java.yml +++ b/manifests/java.yml @@ -565,6 +565,7 @@ tests/: test_security_controls.py: TestSecurityControls: '*': missing_feature (No endpoint implemented) + jersey-grizzly2: v1.44.0 spring-boot: v1.44.0 resteasy-netty3: v1.44.0 spring-boot-3-native: missing_feature (GraalVM. Tracing support only) diff --git a/utils/build/docker/java/jersey-grizzly2/src/main/java/com/datadoghq/jersey/IastSinkResource.java b/utils/build/docker/java/jersey-grizzly2/src/main/java/com/datadoghq/jersey/IastSinkResource.java index c087b7e302..11770f9346 100644 --- a/utils/build/docker/java/jersey-grizzly2/src/main/java/com/datadoghq/jersey/IastSinkResource.java +++ b/utils/build/docker/java/jersey-grizzly2/src/main/java/com/datadoghq/jersey/IastSinkResource.java @@ -271,4 +271,88 @@ public String insecureReflection(@FormParam("param") final String className) { return "Insecure"; } + @POST + @Path("/sc/s/configured") + public String scSanitizeConfigured(@FormParam("param") String param){ + String sanitized = SecurityControlUtil.sanitize(param); + cmd.insecureCmd(sanitized); + return "ok"; + } + + @POST + @Path("/sc/s/not-configured") + public Object scSanitizeSqli(@FormParam("param") String param){ + String sanitized = SecurityControlUtil.sanitize(param); + return sql.insecureSql(sanitized, "password"); + } + + @POST + @Path("/sc/s/all") + public Object scSanitizeForAllVulns(@FormParam("param") String param){ + String sanitized = SecurityControlUtil.sanitizeForAllVulns(param); + sql.insecureSql(sanitized, "password"); + return "ok"; + } + + @POST + @Path("/sc/iv/configured") + public String scValidateXSS(@FormParam("param") String param){ + if (SecurityControlUtil.validate(param)) { + cmd.insecureCmd(param); + } + return "ok"; + } + + @POST + @Path("/sc/iv/not-configured") + public String scValidateSqli(@FormParam("param") String param){ + if (SecurityControlUtil.validate(param)) { + sql.insecureSql(param, "password"); + } + return "ok"; + } + + @POST + @Path("/sc/iv/all") + public String scValidateForAllVulns(@FormParam("param") String param){ + if (SecurityControlUtil.validateForAllVulns(param)) { + sql.insecureSql(param, "password"); + } + return "ok"; + } + + @POST + @Path("/sc/iv/overloaded/secure") + public String scIVOverloadedSecure(@FormParam("user") String user, @FormParam("password") String pass){ + if (SecurityControlUtil.overloadedValidation(null, user, pass)) { + sql.insecureSql(user, pass); + } + return "ok"; + } + + @POST + @Path("/sc/iv/overloaded/insecure") + public String scIVOverloadedInsecure(@FormParam("user") String user, @FormParam("password") String pass){ + if (SecurityControlUtil.overloadedValidation(user, pass)) { + sql.insecureSql(user, pass); + } + return "ok"; + } + + @POST + @Path("/sc/s/overloaded/secure") + public String scSOverloadedSecure(@FormParam("param") String param){ + String sanitized = SecurityControlUtil.overloadedSanitize(param); + cmd.insecureCmd(sanitized); + return "ok"; + } + + @POST + @Path("/sc/s/overloaded/insecure") + public String scSOverloadedInsecure(@FormParam("param") String param){ + String sanitized = SecurityControlUtil.overloadedSanitize(param, null); + cmd.insecureCmd(sanitized); + return "ok"; + } + } From 8f668f2ea2f0e9890cb34fe8ddcc306d347dc32f Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Fri, 29 Nov 2024 14:12:22 +0100 Subject: [PATCH 10/10] Enable for akka-http --- manifests/java.yml | 7 +- .../com/datadoghq/akka_http/IastRoutes.scala | 87 +++++++++++++++++++ 2 files changed, 89 insertions(+), 5 deletions(-) diff --git a/manifests/java.yml b/manifests/java.yml index b98d2ed693..ac84d14f5e 100644 --- a/manifests/java.yml +++ b/manifests/java.yml @@ -565,12 +565,9 @@ tests/: test_security_controls.py: TestSecurityControls: '*': missing_feature (No endpoint implemented) - jersey-grizzly2: v1.44.0 - spring-boot: v1.44.0 - resteasy-netty3: v1.44.0 + play: missing_feature (No endpoint implemented) + ratpack: missing_feature (No endpoint implemented) spring-boot-3-native: missing_feature (GraalVM. Tracing support only) - vertx3: v1.44.0 - vertx4: v1.44.0 rasp/: test_lfi.py: Test_Lfi_BodyJson: 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 024025d323..62da2341ab 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 @@ -196,6 +196,93 @@ object IastRoutes { } } } + pathPrefix("sc") { + pathPrefix("s") { + post { + path("configured") { + formField("param") { param => + val sanitized = SecurityControlUtil.sanitize(param) + cmd.insecureCmd(sanitized) + complete(StatusCodes.OK) + } + } ~ + path("not-configured") { + formField("param") { param => + val sanitized = SecurityControlUtil.sanitize(param) + complete(StatusCodes.OK, sql.insecureSql(sanitized, "password"))(jsonMarshaller) + } + } ~ + path("all") { + formField("param") { param => + val sanitized = SecurityControlUtil.sanitizeForAllVulns(param) + complete(StatusCodes.OK, sql.insecureSql(sanitized, "password"))(jsonMarshaller) + } + } ~ + pathPrefix("overloaded") { + path("secure") { + formField("param") { param => + val sanitized = SecurityControlUtil.overloadedSanitize(param) + cmd.insecureCmd(sanitized) + complete(StatusCodes.OK) + } + } ~ + path("insecure") { + formField("param") { param => + val sanitized = SecurityControlUtil.overloadedSanitize(param, null) + cmd.insecureCmd(sanitized) + complete(StatusCodes.OK) + } + } + } + } + } ~ + pathPrefix("iv") { + post { + path("configured") { + formField("param") { param => + if (SecurityControlUtil.validate(param)) { + cmd.insecureCmd(param) + } + complete(StatusCodes.OK) + } + } ~ + path("not-configured") { + formField("param") { param => + if (SecurityControlUtil.validate(param)) { + sql.insecureSql(param, "password") + } + complete(StatusCodes.OK) + } + } ~ + path("all") { + formField("param") { param => + if (SecurityControlUtil.validateForAllVulns(param)) { + sql.insecureSql(param, "password") + } + complete(StatusCodes.OK) + } + } ~ + pathPrefix("overloaded") { + path("secure") { + formFields("user", "password") { (user, pass) => + if (SecurityControlUtil.overloadedValidation(null, user, pass)) { + sql.insecureSql(user, pass) + } + complete(StatusCodes.OK) + } + } ~ + path("insecure") { + formFields("user", "password") { (user, pass) => + if (SecurityControlUtil.overloadedValidation(user, pass)) { + sql.insecureSql(user, pass) + } + complete(StatusCodes.OK) + } + } + } + } + } + } } private def paramOrFormField(p: String) = {