From 99bdbe587158e9a8758770073badb8f7c42293dc Mon Sep 17 00:00:00 2001 From: Florentin Labelle Date: Thu, 29 Oct 2026 14:06:48 +0100 Subject: [PATCH 1/3] feat: initialize SCA reachability in the Lambda wrapper --- datadog_lambda/wrapper.py | 12 ++++++++++++ tests/test_wrapper.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 73057983..83e054c2 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -67,6 +67,18 @@ start() +if config.sca_enabled: + try: + from ddtrace.internal.sca.product import start as start_sca + from ddtrace.internal.sca.product import post_preload as instrument_sca + + # The layer patches integrations before loading the wrapper. It does not + # use ddtrace.auto, so run both SCA startup phases before importing user code. + start_sca() + instrument_sca() + except Exception: + logger.debug("Failed to initialize SCA reachability", exc_info=True) + profiler = None if config.profiling_enabled: try: diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index 26a53b0c..ea495917 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -23,6 +23,38 @@ MAX_UINT_64BITS = (1 << 64) - 1 +@pytest.mark.parametrize("sca_enabled", [False, True]) +def test_sca_startup(sca_enabled, monkeypatch): + product = MagicMock() + monkeypatch.setattr("datadog_lambda.config.Config.sca_enabled", sca_enabled) + monkeypatch.setitem(sys.modules, "ddtrace.internal.sca.product", product) + + importlib.reload(wrapper) + + assert product.mock_calls == ( + [call.start(), call.post_preload()] if sca_enabled else [] + ) + + +def test_sca_startup_with_older_tracer(monkeypatch): + monkeypatch.setattr("datadog_lambda.config.Config.sca_enabled", True) + monkeypatch.setitem(sys.modules, "ddtrace.internal.sca.product", None) + + importlib.reload(wrapper) + + +def test_sca_startup_failure_preserves_handler(monkeypatch): + product = MagicMock() + product.start.side_effect = RuntimeError("SCA startup failed") + monkeypatch.setattr("datadog_lambda.config.Config.sca_enabled", True) + monkeypatch.setitem(sys.modules, "ddtrace.internal.sca.product", product) + + importlib.reload(wrapper) + + product.post_preload.assert_not_called() + assert callable(wrapper.datadog_lambda_wrapper) + + class TestDatadogLambdaWrapper(unittest.TestCase): def setUp(self): # Force @datadog_lambda_wrapper to always create a real From 25eb00b5b8d6e7ea5882921b097fe279b498fbc9 Mon Sep 17 00:00:00 2001 From: Florentin Labelle Date: Thu, 29 Oct 2026 14:25:37 +0100 Subject: [PATCH 2/3] test: align SCA startup coverage with other products --- tests/test_wrapper.py | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index ea495917..0bcede0c 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -23,38 +23,23 @@ MAX_UINT_64BITS = (1 << 64) - 1 -@pytest.mark.parametrize("sca_enabled", [False, True]) -def test_sca_startup(sca_enabled, monkeypatch): +@patch("datadog_lambda.config.Config.sca_enabled", True) +def test_sca_enabled(monkeypatch): product = MagicMock() - monkeypatch.setattr("datadog_lambda.config.Config.sca_enabled", sca_enabled) monkeypatch.setitem(sys.modules, "ddtrace.internal.sca.product", product) importlib.reload(wrapper) - assert product.mock_calls == ( - [call.start(), call.post_preload()] if sca_enabled else [] - ) + assert product.mock_calls == [call.start(), call.post_preload()] -def test_sca_startup_with_older_tracer(monkeypatch): - monkeypatch.setattr("datadog_lambda.config.Config.sca_enabled", True) +@patch("datadog_lambda.config.Config.sca_enabled", True) +def test_sca_import_errors_caught(monkeypatch): monkeypatch.setitem(sys.modules, "ddtrace.internal.sca.product", None) importlib.reload(wrapper) -def test_sca_startup_failure_preserves_handler(monkeypatch): - product = MagicMock() - product.start.side_effect = RuntimeError("SCA startup failed") - monkeypatch.setattr("datadog_lambda.config.Config.sca_enabled", True) - monkeypatch.setitem(sys.modules, "ddtrace.internal.sca.product", product) - - importlib.reload(wrapper) - - product.post_preload.assert_not_called() - assert callable(wrapper.datadog_lambda_wrapper) - - class TestDatadogLambdaWrapper(unittest.TestCase): def setUp(self): # Force @datadog_lambda_wrapper to always create a real From 4dafd95121734c18718e11977fe0ff00da7059a5 Mon Sep 17 00:00:00 2001 From: Florentin Labelle Date: Thu, 29 Oct 2026 14:29:35 +0100 Subject: [PATCH 3/3] fix: preserve SCA modules in Lambda layers --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8b8f9a09..57b703f6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,7 +37,6 @@ RUN rm -rf ./python/lib/$runtime/site-packages/jsonschema/tests # Remove unsupported appsec modules RUN rm -rf \ ./python/lib/$runtime/site-packages/ddtrace/appsec/_iast \ - ./python/lib/$runtime/site-packages/ddtrace/appsec/sca \ ./python/lib/$runtime/site-packages/ddtrace/appsec/_shared # CI Visibility paths/integrations