From 87a54ace75b126bc54f0c9c8887b16ef2bbcd295 Mon Sep 17 00:00:00 2001 From: Florentin Labelle Date: Thu, 29 Oct 2026 14:08:26 +0100 Subject: [PATCH 1/5] Add Python Lambda SCA reachability fixtures --- docs/understand/weblogs/end-to-end_weblog.md | 2 ++ manifests/python_lambda.yml | 2 +- tests/appsec/test_sca_reachability.py | 6 +++++ .../docker/python_lambda/function/handler.py | 22 +++++++++++++++++++ .../python_lambda/function/requirements.txt | 4 ++-- .../scripts/libraries_and_scenarios_rules.yml | 2 +- 6 files changed, 34 insertions(+), 4 deletions(-) diff --git a/docs/understand/weblogs/end-to-end_weblog.md b/docs/understand/weblogs/end-to-end_weblog.md index d5cc0cdf893..d258274ed91 100644 --- a/docs/understand/weblogs/end-to-end_weblog.md +++ b/docs/understand/weblogs/end-to-end_weblog.md @@ -889,6 +889,8 @@ first occurrence is reported in the `reached` array. The Java Lambda weblogs also implement both SCA endpoints, using Junrar 7.5.5 kept as a separate JAR. The vulnerable class is preloaded during handler initialization. +The Python Lambda weblogs implement both SCA endpoints with requests 2.31.0. They call `Session.send` from two distinct handlers against a closed loopback port, so no external service is required. + ### GET /log/library This endpoint facilitates logging a message using a logging library. It is primarily designed for testing log injection functionality. Weblog apps must log using JSON format. diff --git a/manifests/python_lambda.yml b/manifests/python_lambda.yml index ef76c50362c..3affdc315bf 100644 --- a/manifests/python_lambda.yml +++ b/manifests/python_lambda.yml @@ -266,7 +266,7 @@ manifest: tests/appsec/test_reports.py::Test_Info: v7.112.0 tests/appsec/test_reports.py::Test_RequestHeaders: v7.112.0 tests/appsec/test_reports.py::Test_StatusCode: v7.112.0 - tests/appsec/test_sca_reachability.py: incomplete_test_app (Lambda SCA dependency fixtures and vulnerable-call endpoints are not implemented) + tests/appsec/test_sca_reachability.py: missing_feature (requires Lambda SCA reachability support in dd-trace-py and datadog-lambda-python) tests/appsec/test_trace_tagging.py::Test_TraceTaggingRules: v8.113.0 tests/appsec/test_traces.py::Test_AppSecEventSpanTags: v7.112.0 tests/appsec/test_traces.py::Test_AppSecEventSpanTags::test_header_collection: bug (APPSEC-58202) diff --git a/tests/appsec/test_sca_reachability.py b/tests/appsec/test_sca_reachability.py index aef5a54ef42..b2287c63b0e 100644 --- a/tests/appsec/test_sca_reachability.py +++ b/tests/appsec/test_sca_reachability.py @@ -33,6 +33,12 @@ "path": "com.datadoghq.system_tests.springboot.ScaReachability", "symbol": "junrarHit", }, + "python_lambda": { + "cve_id": ["CVE-2024-35195", "GHSA-652x-xj99-gmcc"], + "vulnerable_dep": "requests", + "path": "handler.py", + "symbol": "sca_requests_vulnerable_call", + }, "java_lambda": { "cve_id": ["GHSA-hf5p-q87m-crj7"], "vulnerable_dep": "junrar", diff --git a/utils/build/docker/python_lambda/function/handler.py b/utils/build/docker/python_lambda/function/handler.py index d507f927a20..7227da81760 100644 --- a/utils/build/docker/python_lambda/function/handler.py +++ b/utils/build/docker/python_lambda/function/handler.py @@ -47,6 +47,28 @@ _TRACK_CUSTOM_APPSEC_EVENT_NAME = "system_tests_appsec_event" +@app.get("/sca/vulnerable-call") +def sca_requests_vulnerable_call() -> str: + """Call the vulnerable Session.send target without an external dependency.""" + with requests.Session() as session: + try: + session.send(requests.Request("GET", "http://127.0.0.1:1").prepare(), timeout=1) + except requests.exceptions.ConnectionError: + pass + return "OK" + + +@app.get("/sca/vulnerable-call-alt") +def sca_requests_vulnerable_call_alt() -> str: + """Use a distinct caller for the same target to check first-hit deduplication.""" + with requests.Session() as session: + try: + session.send(requests.Request("GET", "http://127.0.0.1:1").prepare(), timeout=1) + except requests.exceptions.ConnectionError: + pass + return "OK" + + def version_info(): return { "status": "ok", diff --git a/utils/build/docker/python_lambda/function/requirements.txt b/utils/build/docker/python_lambda/function/requirements.txt index b3abb7c5aee..f13f7fc198c 100644 --- a/utils/build/docker/python_lambda/function/requirements.txt +++ b/utils/build/docker/python_lambda/function/requirements.txt @@ -1,3 +1,3 @@ aws-lambda-powertools==3.17.0 -requests==2.32.5 -xmltodict==1.0.2 \ No newline at end of file +requests==2.31.0 +xmltodict==1.0.2 diff --git a/utils/scripts/libraries_and_scenarios_rules.yml b/utils/scripts/libraries_and_scenarios_rules.yml index 806149e4c5d..cb73c0b2b40 100644 --- a/utils/scripts/libraries_and_scenarios_rules.yml +++ b/utils/scripts/libraries_and_scenarios_rules.yml @@ -589,7 +589,7 @@ patterns: tests/appsec/test_sca_reachability.py: scenario_groups: null - libraries: [java, java_lambda, python] + libraries: [java, java_lambda, python, python_lambda] ################################# Default rules ################################ # The rules in this section are only applied if no other rule is applicable. From 655d6829b28d1bba294293f866e6737fa0021773 Mon Sep 17 00:00:00 2001 From: Florentin Labelle Date: Thu, 29 Oct 2026 14:32:22 +0100 Subject: [PATCH 2/5] Use a synthetic advisory for Python Lambda SCA tests --- docs/understand/weblogs/end-to-end_weblog.md | 2 -- tests/appsec/sca_python_lambda.json | 10 ++++++++++ tests/appsec/test_sca_reachability.py | 2 +- utils/_context/_scenarios/aws_lambda.py | 9 +++++++++ utils/build/docker/python_lambda/function/handler.py | 2 +- .../docker/python_lambda/function/requirements.txt | 4 ++-- utils/scripts/libraries_and_scenarios_rules.yml | 5 +++++ 7 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 tests/appsec/sca_python_lambda.json diff --git a/docs/understand/weblogs/end-to-end_weblog.md b/docs/understand/weblogs/end-to-end_weblog.md index d258274ed91..d5cc0cdf893 100644 --- a/docs/understand/weblogs/end-to-end_weblog.md +++ b/docs/understand/weblogs/end-to-end_weblog.md @@ -889,8 +889,6 @@ first occurrence is reported in the `reached` array. The Java Lambda weblogs also implement both SCA endpoints, using Junrar 7.5.5 kept as a separate JAR. The vulnerable class is preloaded during handler initialization. -The Python Lambda weblogs implement both SCA endpoints with requests 2.31.0. They call `Session.send` from two distinct handlers against a closed loopback port, so no external service is required. - ### GET /log/library This endpoint facilitates logging a message using a logging library. It is primarily designed for testing log injection functionality. Weblog apps must log using JSON format. diff --git a/tests/appsec/sca_python_lambda.json b/tests/appsec/sca_python_lambda.json new file mode 100644 index 00000000000..81ebe582fd5 --- /dev/null +++ b/tests/appsec/sca_python_lambda.json @@ -0,0 +1,10 @@ +{ + "targets": [ + { + "id": "system-tests-python-lambda-sca", + "dependency_name": "requests", + "package_versions": [">=0"], + "targets": ["requests.sessions:Session.send"] + } + ] +} diff --git a/tests/appsec/test_sca_reachability.py b/tests/appsec/test_sca_reachability.py index b2287c63b0e..04c5f29494e 100644 --- a/tests/appsec/test_sca_reachability.py +++ b/tests/appsec/test_sca_reachability.py @@ -34,7 +34,7 @@ "symbol": "junrarHit", }, "python_lambda": { - "cve_id": ["CVE-2024-35195", "GHSA-652x-xj99-gmcc"], + "cve_id": ["system-tests-python-lambda-sca"], "vulnerable_dep": "requests", "path": "handler.py", "symbol": "sca_requests_vulnerable_call", diff --git a/utils/_context/_scenarios/aws_lambda.py b/utils/_context/_scenarios/aws_lambda.py index 30919185d00..057ec500540 100644 --- a/utils/_context/_scenarios/aws_lambda.py +++ b/utils/_context/_scenarios/aws_lambda.py @@ -220,6 +220,15 @@ def __init__(self) -> None: scenario_groups=[scenario_groups.appsec, scenario_groups.appsec_lambda], ) + def configure(self, config: pytest.Config) -> None: + super().configure(config) + if self.lambda_weblog.image.labels.get("system-tests-library") == "python_lambda": + # Inject a synthetic advisory without coupling requests upgrades to a real CVE. + self.lambda_weblog.volumes["./tests/appsec/sca_python_lambda.json"] = { + "bind": "/opt/python/lib/python3.13/site-packages/ddtrace/appsec/sca/_cve_data.json", + "mode": "ro", + } + class AppsecLambdaRasp(_LambdaScenario): def __init__(self) -> None: diff --git a/utils/build/docker/python_lambda/function/handler.py b/utils/build/docker/python_lambda/function/handler.py index 7227da81760..875f4be39e4 100644 --- a/utils/build/docker/python_lambda/function/handler.py +++ b/utils/build/docker/python_lambda/function/handler.py @@ -49,7 +49,7 @@ @app.get("/sca/vulnerable-call") def sca_requests_vulnerable_call() -> str: - """Call the vulnerable Session.send target without an external dependency.""" + """Exercise the synthetic advisory's Session.send target.""" with requests.Session() as session: try: session.send(requests.Request("GET", "http://127.0.0.1:1").prepare(), timeout=1) diff --git a/utils/build/docker/python_lambda/function/requirements.txt b/utils/build/docker/python_lambda/function/requirements.txt index f13f7fc198c..b3abb7c5aee 100644 --- a/utils/build/docker/python_lambda/function/requirements.txt +++ b/utils/build/docker/python_lambda/function/requirements.txt @@ -1,3 +1,3 @@ aws-lambda-powertools==3.17.0 -requests==2.31.0 -xmltodict==1.0.2 +requests==2.32.5 +xmltodict==1.0.2 \ No newline at end of file diff --git a/utils/scripts/libraries_and_scenarios_rules.yml b/utils/scripts/libraries_and_scenarios_rules.yml index cb73c0b2b40..3b3c9a11cde 100644 --- a/utils/scripts/libraries_and_scenarios_rules.yml +++ b/utils/scripts/libraries_and_scenarios_rules.yml @@ -591,6 +591,11 @@ patterns: scenario_groups: null libraries: [java, java_lambda, python, python_lambda] + tests/appsec/sca_python_lambda.json: + scenario_groups: null + scenarios: [APPSEC_LAMBDA_SCA] + libraries: python_lambda + ################################# Default rules ################################ # The rules in this section are only applied if no other rule is applicable. # They are considered safe to override in previous rules. They should match a From 0520be985be1a5cfa007421f4fce1023e941a0da Mon Sep 17 00:00:00 2001 From: Florentin Labelle Date: Thu, 29 Oct 2026 14:44:49 +0100 Subject: [PATCH 3/5] Share SCA advisory fixtures across languages --- tests/appsec/sca_advisories.json | 34 +++++++++++++++++++ tests/appsec/sca_python_lambda.json | 10 ------ tests/appsec/test_sca_reachability.py | 2 +- utils/_context/_scenarios/aws_lambda.py | 12 +++++-- .../scripts/libraries_and_scenarios_rules.yml | 2 +- 5 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 tests/appsec/sca_advisories.json delete mode 100644 tests/appsec/sca_python_lambda.json diff --git a/tests/appsec/sca_advisories.json b/tests/appsec/sca_advisories.json new file mode 100644 index 00000000000..3c9d0fdc82e --- /dev/null +++ b/tests/appsec/sca_advisories.json @@ -0,0 +1,34 @@ +{ + "python": { + "targets": [ + { + "id": "system-tests-sca", + "dependency_name": "requests", + "package_versions": [ + ">=0" + ], + "targets": [ + "requests.sessions:Session.send" + ] + } + ] + }, + "java": { + "version": 1, + "entries": [ + { + "vuln_id": "system-tests-sca", + "artifact": "com.github.junrar:junrar", + "version_ranges": [ + ">=0" + ], + "symbols": [ + { + "class": "com/github/junrar/LocalFolderExtractor", + "method": "createDirectory" + } + ] + } + ] + } +} diff --git a/tests/appsec/sca_python_lambda.json b/tests/appsec/sca_python_lambda.json deleted file mode 100644 index 81ebe582fd5..00000000000 --- a/tests/appsec/sca_python_lambda.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "targets": [ - { - "id": "system-tests-python-lambda-sca", - "dependency_name": "requests", - "package_versions": [">=0"], - "targets": ["requests.sessions:Session.send"] - } - ] -} diff --git a/tests/appsec/test_sca_reachability.py b/tests/appsec/test_sca_reachability.py index 04c5f29494e..e494e3eddfc 100644 --- a/tests/appsec/test_sca_reachability.py +++ b/tests/appsec/test_sca_reachability.py @@ -34,7 +34,7 @@ "symbol": "junrarHit", }, "python_lambda": { - "cve_id": ["system-tests-python-lambda-sca"], + "cve_id": ["system-tests-sca"], "vulnerable_dep": "requests", "path": "handler.py", "symbol": "sca_requests_vulnerable_call", diff --git a/utils/_context/_scenarios/aws_lambda.py b/utils/_context/_scenarios/aws_lambda.py index 057ec500540..47d0f2d94b6 100644 --- a/utils/_context/_scenarios/aws_lambda.py +++ b/utils/_context/_scenarios/aws_lambda.py @@ -1,3 +1,6 @@ +import json +from pathlib import Path + import pytest from utils import interfaces from utils.interfaces._core import ProxyBasedInterfaceValidator @@ -222,9 +225,12 @@ def __init__(self) -> None: def configure(self, config: pytest.Config) -> None: super().configure(config) - if self.lambda_weblog.image.labels.get("system-tests-library") == "python_lambda": - # Inject a synthetic advisory without coupling requests upgrades to a real CVE. - self.lambda_weblog.volumes["./tests/appsec/sca_python_lambda.json"] = { + if not self.replay and self.lambda_weblog.image.labels.get("system-tests-library") == "python_lambda": + # Each tracer receives its native payload from the shared catalog. + catalog = json.loads(Path("tests/appsec/sca_advisories.json").read_text(encoding="utf-8")) + payload_path = Path(self.host_log_folder) / "sca_advisories.json" + payload_path.write_text(json.dumps(catalog["python"]), encoding="utf-8") + self.lambda_weblog.volumes[str(payload_path)] = { "bind": "/opt/python/lib/python3.13/site-packages/ddtrace/appsec/sca/_cve_data.json", "mode": "ro", } diff --git a/utils/scripts/libraries_and_scenarios_rules.yml b/utils/scripts/libraries_and_scenarios_rules.yml index 3b3c9a11cde..31f60a3c861 100644 --- a/utils/scripts/libraries_and_scenarios_rules.yml +++ b/utils/scripts/libraries_and_scenarios_rules.yml @@ -591,7 +591,7 @@ patterns: scenario_groups: null libraries: [java, java_lambda, python, python_lambda] - tests/appsec/sca_python_lambda.json: + tests/appsec/sca_advisories.json: scenario_groups: null scenarios: [APPSEC_LAMBDA_SCA] libraries: python_lambda From 67cd20396b6580335778852a5321eadf783e2e3d Mon Sep 17 00:00:00 2001 From: Florentin Labelle Date: Thu, 29 Oct 2026 15:24:50 +0100 Subject: [PATCH 4/5] test: inject Lambda SCA advisories through scenario arguments --- tests/appsec/sca_advisories.json | 56 +++++++++++-------------- utils/_context/_scenarios/aws_lambda.py | 22 +++------- 2 files changed, 30 insertions(+), 48 deletions(-) diff --git a/tests/appsec/sca_advisories.json b/tests/appsec/sca_advisories.json index 3c9d0fdc82e..186d812b022 100644 --- a/tests/appsec/sca_advisories.json +++ b/tests/appsec/sca_advisories.json @@ -1,34 +1,26 @@ { - "python": { - "targets": [ - { - "id": "system-tests-sca", - "dependency_name": "requests", - "package_versions": [ - ">=0" - ], - "targets": [ - "requests.sessions:Session.send" - ] - } - ] - }, - "java": { - "version": 1, - "entries": [ - { - "vuln_id": "system-tests-sca", - "artifact": "com.github.junrar:junrar", - "version_ranges": [ - ">=0" - ], - "symbols": [ - { - "class": "com/github/junrar/LocalFolderExtractor", - "method": "createDirectory" - } - ] - } - ] - } + "targets": [ + { + "id": "system-tests-sca", + "lang": "python", + "dependency_name": "requests", + "package_versions": [ + ">=0" + ], + "targets": [ + "requests.sessions:Session.send" + ] + }, + { + "id": "system-tests-sca", + "lang": "maven", + "dependency_name": "com.github.junrar:junrar", + "package_versions": [ + ">=0" + ], + "targets": [ + "com.github.junrar:LocalFolderExtractor.createDirectory" + ] + } + ] } diff --git a/utils/_context/_scenarios/aws_lambda.py b/utils/_context/_scenarios/aws_lambda.py index 47d0f2d94b6..3f784bdf2cc 100644 --- a/utils/_context/_scenarios/aws_lambda.py +++ b/utils/_context/_scenarios/aws_lambda.py @@ -1,6 +1,3 @@ -import json -from pathlib import Path - import pytest from utils import interfaces from utils.interfaces._core import ProxyBasedInterfaceValidator @@ -218,23 +215,16 @@ class AppsecLambdaSca(_LambdaScenario): def __init__(self) -> None: super().__init__( "APPSEC_LAMBDA_SCA", - weblog_env={"DD_APPSEC_ENABLED": "false", "DD_APPSEC_SCA_ENABLED": "true"}, + weblog_env={ + "DD_APPSEC_ENABLED": "false", + "DD_APPSEC_SCA_ENABLED": "true", + "_DD_APPSEC_SCA_DATA_FILE": "/sca_advisories.json", + }, + weblog_volumes={"./tests/appsec/sca_advisories.json": {"bind": "/sca_advisories.json", "mode": "ro"}}, doc="SCA dependency metadata and runtime reachability in Lambda with AppSec disabled", scenario_groups=[scenario_groups.appsec, scenario_groups.appsec_lambda], ) - def configure(self, config: pytest.Config) -> None: - super().configure(config) - if not self.replay and self.lambda_weblog.image.labels.get("system-tests-library") == "python_lambda": - # Each tracer receives its native payload from the shared catalog. - catalog = json.loads(Path("tests/appsec/sca_advisories.json").read_text(encoding="utf-8")) - payload_path = Path(self.host_log_folder) / "sca_advisories.json" - payload_path.write_text(json.dumps(catalog["python"]), encoding="utf-8") - self.lambda_weblog.volumes[str(payload_path)] = { - "bind": "/opt/python/lib/python3.13/site-packages/ddtrace/appsec/sca/_cve_data.json", - "mode": "ro", - } - class AppsecLambdaRasp(_LambdaScenario): def __init__(self) -> None: From eff08382fe571aa6488521a44d71fb29dd3775ec Mon Sep 17 00:00:00 2001 From: Florentin Labelle Date: Thu, 29 Oct 2026 15:30:39 +0100 Subject: [PATCH 5/5] test: inject SCA fixtures through Lambda test startup --- utils/_context/_scenarios/aws_lambda.py | 1 - utils/build/docker/python_lambda/function/app.sh | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/utils/_context/_scenarios/aws_lambda.py b/utils/_context/_scenarios/aws_lambda.py index 3f784bdf2cc..4a0aad1a59f 100644 --- a/utils/_context/_scenarios/aws_lambda.py +++ b/utils/_context/_scenarios/aws_lambda.py @@ -218,7 +218,6 @@ def __init__(self) -> None: weblog_env={ "DD_APPSEC_ENABLED": "false", "DD_APPSEC_SCA_ENABLED": "true", - "_DD_APPSEC_SCA_DATA_FILE": "/sca_advisories.json", }, weblog_volumes={"./tests/appsec/sca_advisories.json": {"bind": "/sca_advisories.json", "mode": "ro"}}, doc="SCA dependency metadata and runtime reachability in Lambda with AppSec disabled", diff --git a/utils/build/docker/python_lambda/function/app.sh b/utils/build/docker/python_lambda/function/app.sh index eb958bd98f3..96c28071310 100644 --- a/utils/build/docker/python_lambda/function/app.sh +++ b/utils/build/docker/python_lambda/function/app.sh @@ -4,6 +4,22 @@ set -eu export DD_LAMBDA_HANDLER=handler.lambda_handler +# Only the SCA scenario mounts this catalog. Install its Python entries before +# starting the Lambda wrapper, which initializes reachability instrumentation. +if [ -f /sca_advisories.json ]; then + python3 - <<'PY' +import json +from pathlib import Path + +catalog = json.loads(Path("/sca_advisories.json").read_text()) +targets = [entry for entry in catalog["targets"] if entry["lang"] == "python"] +data_paths = list(Path("/opt/python").glob("lib/python*/site-packages/ddtrace/appsec/sca/_cve_data.json")) +if len(data_paths) != 1: + raise RuntimeError(f"Expected one bundled SCA catalog, found {data_paths}") +data_paths[0].write_text(json.dumps({"targets": targets})) +PY +fi + socat TCP-LISTEN:8127,reuseaddr,fork,bind=0.0.0.0 TCP:127.0.0.1:8126 & exec /lambda-entrypoint.sh datadog_lambda.handler.handler