Skip to content

Commit

Permalink
security control tests for java
Browse files Browse the repository at this point in the history
  • Loading branch information
jandro996 committed Jan 9, 2025
1 parent 036d7af commit 3b8962f
Show file tree
Hide file tree
Showing 19 changed files with 751 additions and 0 deletions.
44 changes: 44 additions & 0 deletions docs/weblog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,50 @@ A POST request which will receive the following JSON body:

An empty GET request that will execute two database queries, one to get a username and another to do a vulnerable SELECT using the obtained username.

### 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/configured

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/not-configured

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 with a value that triggers a vulnerability. The value should be sanitized by a sanitizer security control configured for all vulnerabilities.

#### POST /sc/iv/configured

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/not-configured

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 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 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 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 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 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

This endpoint accept a mandatory parameter `url`. It'll make a call to these url, and should returns a JSON response :
Expand Down
1 change: 1 addition & 0 deletions manifests/cpp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ tests/:
test_path_parameter.py: irrelevant (ASM is not implemented in C++)
test_sql_row.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_cmdi.py: irrelevant (ASM is not implemented in C++)
test_lfi.py: irrelevant (ASM is not implemented in C++)
Expand Down
2 changes: 2 additions & 0 deletions manifests/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ tests/:
TestSqlRow: missing_feature
test_uri.py:
TestURI: irrelevant
test_security_controls.py:
TestSecurityControls: missing_feature
rasp/:
test_cmdi.py:
Test_Cmdi_BodyJson: v3.7.0
Expand Down
2 changes: 2 additions & 0 deletions manifests/golang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ tests/:
TestSqlRow: missing_feature
test_uri.py:
TestURI: missing_feature
test_security_controls.py:
TestSecurityControls: missing_feature
rasp/:
test_cmdi.py: missing_feature
test_lfi.py: missing_feature
Expand Down
11 changes: 11 additions & 0 deletions manifests/java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,17 @@ tests/:
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
vertx3: missing_feature
vertx4: missing_feature
test_security_controls.py:
TestSecurityControls:
'*': v1.45.0
akka-http: missing_feature
jersey-grizzly2: missing_feature
play: missing_feature
ratpack: missing_feature
resteasy-netty3: missing_feature
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
vertx3: missing_feature
vertx4: missing_feature
rasp/:
test_cmdi.py:
Test_Cmdi_BodyJson:
Expand Down
2 changes: 2 additions & 0 deletions manifests/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,8 @@ tests/:
nextjs: missing_feature
test_uri.py:
TestURI: missing_feature
test_security_controls.py:
TestSecurityControls: missing_feature
rasp/:
test_cmdi.py:
Test_Cmdi_BodyJson:
Expand Down
2 changes: 2 additions & 0 deletions manifests/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ tests/:
TestSqlRow: missing_feature
test_uri.py:
TestURI: missing_feature
test_security_controls.py:
TestSecurityControls: missing_feature
rasp/:
test_cmdi.py: missing_feature
test_lfi.py: missing_feature
Expand Down
2 changes: 2 additions & 0 deletions manifests/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ tests/:
TestSqlRow: missing_feature
test_uri.py:
TestURI: missing_feature
test_security_controls.py:
TestSecurityControls: missing_feature
rasp/:
test_cmdi.py:
Test_Cmdi_BodyJson: missing_feature
Expand Down
2 changes: 2 additions & 0 deletions manifests/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ tests/:
TestSqlRow: missing_feature
test_uri.py:
TestURI: missing_feature
test_security_controls.py:
TestSecurityControls: missing_feature
rasp/:
test_cmdi.py: missing_feature
test_lfi.py: missing_feature
Expand Down
133 changes: 133 additions & 0 deletions tests/appsec/iast/test_security_controls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# 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, 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")
class TestSecurityControls:
@staticmethod
def assert_iast_is_enabled(request):
product_enabled = False
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:
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, "IAST is not available"

def setup_iast_is_enabled(self):
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/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, "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/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)
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/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, "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/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)
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, "COMMAND_INJECTION")

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="COMMAND_INJECTION",
)
21 changes: 21 additions & 0 deletions utils/_context/_scenarios/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@
from .endtoend import EndToEndScenario


# 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",
"golang": "TODO",
"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",
"ruby": "TODO",
}


class DefaultScenario(EndToEndScenario):
def __init__(self, name: str):
super().__init__(
Expand All @@ -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)

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
9 changes: 9 additions & 0 deletions utils/_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -2293,5 +2293,14 @@ def agent_host_ipv6(test_object):
pytest.mark.features(feature_id=347)(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

features = _Features()
Loading

0 comments on commit 3b8962f

Please sign in to comment.