From 81f1c9c9b35db99013b4924cd183e37b14aa71fe Mon Sep 17 00:00:00 2001 From: Alberto Vara Date: Fri, 7 Nov 2025 14:37:39 +0100 Subject: [PATCH 1/5] chore(iast): fix iast in the ci --- ddtrace/appsec/_iast/__init__.py | 6 ++++-- ddtrace/settings/asm.py | 5 ----- tests/appsec/iast/aspects/conftest.py | 8 +------- tests/appsec/iast/taint_tracking/conftest.py | 8 +------- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/ddtrace/appsec/_iast/__init__.py b/ddtrace/appsec/_iast/__init__.py index 85e340cb2b3..ded074acdaa 100644 --- a/ddtrace/appsec/_iast/__init__.py +++ b/ddtrace/appsec/_iast/__init__.py @@ -186,18 +186,20 @@ def _iast_pytest_activation(): global _iast_propagation_enabled if _iast_propagation_enabled: return + from ._iast_request_context_base import _iast_start_request + os.environ["DD_IAST_ENABLED"] = os.environ.get("DD_IAST_ENABLED") or "1" os.environ["DD_IAST_REQUEST_SAMPLING"] = os.environ.get("DD_IAST_REQUEST_SAMPLING") or "100.0" os.environ["_DD_APPSEC_DEDUPLICATION_ENABLED"] = os.environ.get("_DD_APPSEC_DEDUPLICATION_ENABLED") or "false" os.environ["DD_IAST_VULNERABILITIES_PER_REQUEST"] = os.environ.get("DD_IAST_VULNERABILITIES_PER_REQUEST") or "1000" - os.environ["DD_IAST_MAX_CONCURRENT_REQUESTS"] = os.environ.get("DD_IAST_MAX_CONCURRENT_REQUESTS") or "1000" asm_config._iast_request_sampling = 100.0 asm_config._deduplication_enabled = False asm_config._iast_max_vulnerabilities_per_requests = 1000 - asm_config._iast_max_concurrent_requests = 1000 oce.reconfigure() + _iast_start_request() + def disable_iast_propagation(): """Remove IAST AST patching from the ModuleWatchdog. Only for testing proposes""" diff --git a/ddtrace/settings/asm.py b/ddtrace/settings/asm.py index d4ff3f10b49..0854eaaae77 100644 --- a/ddtrace/settings/asm.py +++ b/ddtrace/settings/asm.py @@ -140,11 +140,6 @@ class ASMConfig(DDConfig): + r"ey[I-L][\w=-]+\.ey[I-L][\w=-]+(\.[\w.+\/=-]+)?|[\-]{5}BEGIN[a-z\s]+PRIVATE\sKEY" + r"[\-]{5}[^\-]+[\-]{5}END[a-z\s]+PRIVATE\sKEY|ssh-rsa\s*[a-z0-9\/\.+]{100,}", ) - _iast_max_concurrent_requests = DDConfig.var( - int, - IAST.DD_IAST_MAX_CONCURRENT_REQUESTS, - default=2, - ) _iast_max_vulnerabilities_per_requests = DDConfig.var( int, IAST.DD_IAST_VULNERABILITIES_PER_REQUEST, diff --git a/tests/appsec/iast/aspects/conftest.py b/tests/appsec/iast/aspects/conftest.py index 39ddf97f3a4..20531169538 100644 --- a/tests/appsec/iast/aspects/conftest.py +++ b/tests/appsec/iast/aspects/conftest.py @@ -8,13 +8,7 @@ @pytest.fixture(autouse=True) def iast_request(): with override_global_config( - dict( - _iast_enabled=True, - _iast_is_testing=True, - _iast_deduplication_enabled=False, - _iast_request_sampling=100.0, - _iast_max_concurrent_requests=100, - ) + dict(_iast_enabled=True, _iast_is_testing=True, _iast_deduplication_enabled=False, _iast_request_sampling=100.0) ): context_id = _start_iast_context_and_oce() assert context_id is not None diff --git a/tests/appsec/iast/taint_tracking/conftest.py b/tests/appsec/iast/taint_tracking/conftest.py index 4e1ca3aff7a..918d09ec4df 100644 --- a/tests/appsec/iast/taint_tracking/conftest.py +++ b/tests/appsec/iast/taint_tracking/conftest.py @@ -9,13 +9,7 @@ @pytest.fixture(autouse=True) def iast_request(): with override_global_config( - dict( - _iast_enabled=True, - _iast_is_testing=True, - _iast_deduplication_enabled=False, - _iast_request_sampling=100.0, - _iast_max_concurrent_requests=100, - ) + dict(_iast_enabled=True, _iast_is_testing=True, _iast_deduplication_enabled=False, _iast_request_sampling=100.0) ): assert debug_context_array_size() == 2 _start_iast_context_and_oce() From 3be413aa90ecf2c1fbc5cb238c89dc8c559c1c12 Mon Sep 17 00:00:00 2001 From: Alberto Vara Date: Fri, 7 Nov 2025 16:11:26 +0100 Subject: [PATCH 2/5] chore(iast): fix iast in the ci --- ddtrace/settings/asm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ddtrace/settings/asm.py b/ddtrace/settings/asm.py index 0854eaaae77..57233e88d70 100644 --- a/ddtrace/settings/asm.py +++ b/ddtrace/settings/asm.py @@ -227,7 +227,6 @@ class ASMConfig(DDConfig): "_iast_redaction_enabled", "_iast_redaction_name_pattern", "_iast_redaction_value_pattern", - "_iast_max_concurrent_requests", "_iast_max_vulnerabilities_per_requests", "_iast_lazy_taint", "_iast_deduplication_enabled", From a668fb05b2b7a56e69b6a15437bbce5b1cf5a274 Mon Sep 17 00:00:00 2001 From: Alberto Vara Date: Fri, 7 Nov 2025 16:31:33 +0100 Subject: [PATCH 3/5] chore(iast): fix iast in the ci --- ddtrace/settings/asm.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ddtrace/settings/asm.py b/ddtrace/settings/asm.py index 57233e88d70..8e649452ee1 100644 --- a/ddtrace/settings/asm.py +++ b/ddtrace/settings/asm.py @@ -140,6 +140,13 @@ class ASMConfig(DDConfig): + r"ey[I-L][\w=-]+\.ey[I-L][\w=-]+(\.[\w.+\/=-]+)?|[\-]{5}BEGIN[a-z\s]+PRIVATE\sKEY" + r"[\-]{5}[^\-]+[\-]{5}END[a-z\s]+PRIVATE\sKEY|ssh-rsa\s*[a-z0-9\/\.+]{100,}", ) + # We never use `asm_config._iast_max_concurrent_requests` directly, + # but we define it so it can be reported through telemetry, since it’s used from the C files. + _iast_max_concurrent_requests = DDConfig.var( + int, + IAST.DD_IAST_MAX_CONCURRENT_REQUESTS, + default=2, + ) _iast_max_vulnerabilities_per_requests = DDConfig.var( int, IAST.DD_IAST_VULNERABILITIES_PER_REQUEST, @@ -227,6 +234,7 @@ class ASMConfig(DDConfig): "_iast_redaction_enabled", "_iast_redaction_name_pattern", "_iast_redaction_value_pattern", + "_iast_max_concurrent_requests", "_iast_max_vulnerabilities_per_requests", "_iast_lazy_taint", "_iast_deduplication_enabled", From 151c615be834a89ce257fde0f8cf56d9139496bf Mon Sep 17 00:00:00 2001 From: Alberto Vara Date: Fri, 7 Nov 2025 17:59:57 +0100 Subject: [PATCH 4/5] chore(iast): fix iast in the ci --- ddtrace/appsec/_iast/__init__.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/ddtrace/appsec/_iast/__init__.py b/ddtrace/appsec/_iast/__init__.py index ded074acdaa..97aa18a36cc 100644 --- a/ddtrace/appsec/_iast/__init__.py +++ b/ddtrace/appsec/_iast/__init__.py @@ -46,6 +46,7 @@ def wrapped_function(wrapped, instance, args, kwargs): _IAST_TO_BE_LOADED = True _iast_propagation_enabled = False _fork_handler_registered = False +_iast_in_pytest_mode = False def _disable_iast_after_fork(): @@ -87,6 +88,15 @@ def _disable_iast_after_fork(): from ddtrace.appsec._iast._iast_request_context_base import is_iast_request_enabled from ddtrace.appsec._iast._taint_tracking._context import clear_all_request_context_slots + # In pytest mode, always disable IAST in child processes to avoid segfaults + # when tests create multiprocesses (e.g., for testing fork behavior) + if _iast_in_pytest_mode: + log.debug("IAST fork handler: Pytest mode detected, disabling IAST in child process") + clear_all_request_context_slots() + IAST_CONTEXT.set(None) + asm_config._iast_enabled = False + return + if not is_iast_request_enabled(): # No active context - this is an early fork (web framework worker) # IAST can be safely initialized fresh in this child process @@ -168,6 +178,7 @@ def enable_iast_propagation(): """Add IAST AST patching in the ModuleWatchdog""" # DEV: These imports are here to avoid _ast.ast_patching import in the top level # because they are slow and affect serverless startup time + if asm_config._iast_propagation_enabled: from ddtrace.appsec._iast._ast.ast_patching import _should_iast_patch from ddtrace.appsec._iast._loader import _exec_iast_patched_module @@ -183,12 +194,23 @@ def enable_iast_propagation(): def _iast_pytest_activation(): - global _iast_propagation_enabled - if _iast_propagation_enabled: + """Configure IAST settings for pytest execution. + + This function sets up IAST configuration but does NOT create a request context. + Request contexts should be created per-test or per-request to avoid threading issues. + + Also sets a global flag to indicate we're in pytest mode, which ensures IAST is + disabled in forked child processes to prevent segfaults when tests use multiprocessing. + """ + global _iast_in_pytest_mode + + if not asm_config._iast_enabled: return - from ._iast_request_context_base import _iast_start_request - os.environ["DD_IAST_ENABLED"] = os.environ.get("DD_IAST_ENABLED") or "1" + # Mark that we're running in pytest mode + # This flag is checked by the fork handler to disable IAST in child processes + _iast_in_pytest_mode = True + os.environ["DD_IAST_REQUEST_SAMPLING"] = os.environ.get("DD_IAST_REQUEST_SAMPLING") or "100.0" os.environ["_DD_APPSEC_DEDUPLICATION_ENABLED"] = os.environ.get("_DD_APPSEC_DEDUPLICATION_ENABLED") or "false" os.environ["DD_IAST_VULNERABILITIES_PER_REQUEST"] = os.environ.get("DD_IAST_VULNERABILITIES_PER_REQUEST") or "1000" @@ -198,8 +220,6 @@ def _iast_pytest_activation(): asm_config._iast_max_vulnerabilities_per_requests = 1000 oce.reconfigure() - _iast_start_request() - def disable_iast_propagation(): """Remove IAST AST patching from the ModuleWatchdog. Only for testing proposes""" From b8456e48be767e0e1a6a04579c082b9776f35db8 Mon Sep 17 00:00:00 2001 From: Alberto Vara Date: Fri, 7 Nov 2025 18:17:15 +0100 Subject: [PATCH 5/5] chore(iast): fix iast in the ci --- ddtrace/appsec/_iast/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ddtrace/appsec/_iast/__init__.py b/ddtrace/appsec/_iast/__init__.py index 97aa18a36cc..e105bf5f60f 100644 --- a/ddtrace/appsec/_iast/__init__.py +++ b/ddtrace/appsec/_iast/__init__.py @@ -195,15 +195,15 @@ def enable_iast_propagation(): def _iast_pytest_activation(): """Configure IAST settings for pytest execution. - + This function sets up IAST configuration but does NOT create a request context. Request contexts should be created per-test or per-request to avoid threading issues. - + Also sets a global flag to indicate we're in pytest mode, which ensures IAST is disabled in forked child processes to prevent segfaults when tests use multiprocessing. """ global _iast_in_pytest_mode - + if not asm_config._iast_enabled: return