Skip to content

Commit 6731d2e

Browse files
gnufedechristophe-papazianavara1986
authored
chore(asm): don't load appsec modules (iast)... [backport 2.20] (#12298)
The goal is to make sure no appsec module is loaded if appsec is disabled. This PR is the first one of 2, handling IAST. It removes all non guarded IAST import from outside appsec. - ensure we don't load any iast module if iast is disabled - replace `_is_iast_enabled()` by a field `_iast_enabled` in the asm config APPSEC-56626 ## Checklist - [x] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) (cherry picked from commit ea2a9a5) --------- Co-authored-by: Christophe Papazian <[email protected]> Co-authored-by: Alberto Vara <[email protected]>
1 parent 13b5f77 commit 6731d2e

File tree

22 files changed

+155
-171
lines changed

22 files changed

+155
-171
lines changed

ddtrace/_monkey.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77

88
from ddtrace.appsec import load_common_appsec_modules
99
from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE
10+
from ddtrace.settings.asm import config as asm_config
1011

11-
from .appsec._iast._utils import _is_iast_enabled
1212
from .internal import telemetry
1313
from .internal.logger import get_logger
1414
from .internal.utils import formats
@@ -240,7 +240,7 @@ def patch_all(**patch_modules):
240240
modules.update(patch_modules)
241241

242242
patch(raise_errors=False, **modules)
243-
if _is_iast_enabled():
243+
if asm_config._iast_enabled:
244244
from ddtrace.appsec._iast._patch_modules import patch_iast
245245
from ddtrace.appsec.iast import enable_iast_propagation
246246

ddtrace/appsec/_asm_request_context.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from ddtrace.appsec._iast._iast_request_context import is_iast_request_enabled
1919
from ddtrace.appsec._iast._taint_tracking import OriginType
2020
from ddtrace.appsec._iast._taint_tracking._taint_objects import taint_pyobject
21-
from ddtrace.appsec._iast._utils import _is_iast_enabled
2221
from ddtrace.appsec._utils import add_context_log
2322
from ddtrace.appsec._utils import get_triggers
2423
from ddtrace.internal import core
@@ -493,7 +492,7 @@ def _on_wrapped_view(kwargs):
493492

494493
# If IAST is enabled, taint the Flask function kwargs (path parameters)
495494

496-
if _is_iast_enabled() and kwargs:
495+
if asm_config._iast_enabled and kwargs:
497496
if not is_iast_request_enabled():
498497
return return_value
499498

ddtrace/appsec/_iast/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ def wrapped_function(wrapped, instance, args, kwargs):
3838
from ddtrace.settings.asm import config as asm_config
3939

4040
from ._overhead_control_engine import OverheadControl
41-
from ._utils import _is_iast_enabled
4241

4342

4443
log = get_logger(__name__)
@@ -54,7 +53,7 @@ def ddtrace_iast_flask_patch():
5453
and must be before the `app.run()` call. It also requires `DD_IAST_ENABLED` to be
5554
activated.
5655
"""
57-
if not _is_iast_enabled():
56+
if not asm_config._iast_enabled:
5857
return
5958

6059
from ._ast.ast_patching import astpatch_module

ddtrace/appsec/_iast/_handlers.py

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
from wrapt import when_imported
55
from wrapt import wrap_function_wrapper as _w
66

7-
from ddtrace.appsec._iast import _is_iast_enabled
8-
from ddtrace.appsec._iast._iast_request_context import in_iast_context
97
from ddtrace.appsec._iast._metrics import _set_metric_iast_instrumented_source
108
from ddtrace.appsec._iast._patch import _iast_instrument_starlette_request
119
from ddtrace.appsec._iast._patch import _iast_instrument_starlette_request_body
@@ -17,6 +15,7 @@
1715
from ddtrace.appsec._iast._taint_tracking._taint_objects import is_pyobject_tainted
1816
from ddtrace.appsec._iast._taint_utils import taint_structure
1917
from ddtrace.internal.logger import get_logger
18+
from ddtrace.settings.asm import config as asm_config
2019

2120
from ._iast_request_context import is_iast_request_enabled
2221
from ._taint_tracking._taint_objects import taint_pyobject
@@ -47,7 +46,7 @@ def _on_set_http_meta_iast(
4746
response_headers,
4847
response_cookies,
4948
):
50-
if _is_iast_enabled():
49+
if asm_config._iast_enabled:
5150
from ddtrace.appsec._iast.taint_sinks.insecure_cookie import asm_check_cookies
5251

5352
if response_cookies:
@@ -56,7 +55,7 @@ def _on_set_http_meta_iast(
5655

5756
def _on_request_init(wrapped, instance, args, kwargs):
5857
wrapped(*args, **kwargs)
59-
if _is_iast_enabled() and in_iast_context():
58+
if asm_config._iast_enabled and is_iast_request_enabled():
6059
try:
6160
instance.query_string = taint_pyobject(
6261
pyobject=instance.query_string,
@@ -75,7 +74,7 @@ def _on_request_init(wrapped, instance, args, kwargs):
7574

7675

7776
def _on_flask_patch(flask_version):
78-
if _is_iast_enabled():
77+
if asm_config._iast_enabled:
7978
try_wrap_function_wrapper(
8079
"werkzeug.datastructures",
8180
"Headers.items",
@@ -129,16 +128,21 @@ def _on_flask_patch(flask_version):
129128
)
130129
_set_metric_iast_instrumented_source(OriginType.QUERY)
131130

131+
# Instrumented on _on_set_request_tags_iast
132+
_set_metric_iast_instrumented_source(OriginType.COOKIE_NAME)
133+
_set_metric_iast_instrumented_source(OriginType.COOKIE)
134+
_set_metric_iast_instrumented_source(OriginType.PARAMETER_NAME)
135+
132136

133137
def _on_wsgi_environ(wrapped, _instance, args, kwargs):
134-
if _is_iast_enabled() and args and in_iast_context():
138+
if asm_config._iast_enabled and args and is_iast_request_enabled():
135139
return wrapped(*((taint_structure(args[0], OriginType.HEADER_NAME, OriginType.HEADER),) + args[1:]), **kwargs)
136140

137141
return wrapped(*args, **kwargs)
138142

139143

140144
def _on_django_patch():
141-
if _is_iast_enabled():
145+
if asm_config._iast_enabled:
142146
try:
143147
# we instrument those sources on _on_django_func_wrapped
144148
_set_metric_iast_instrumented_source(OriginType.HEADER_NAME)
@@ -164,8 +168,8 @@ def _on_django_patch():
164168
def _on_django_func_wrapped(fn_args, fn_kwargs, first_arg_expected_type, *_):
165169
# If IAST is enabled, and we're wrapping a Django view call, taint the kwargs (view's
166170
# path parameters)
167-
if _is_iast_enabled() and fn_args and isinstance(fn_args[0], first_arg_expected_type):
168-
if not in_iast_context():
171+
if asm_config._iast_enabled and fn_args and isinstance(fn_args[0], first_arg_expected_type):
172+
if not is_iast_request_enabled():
169173
return
170174

171175
http_req = fn_args[0]
@@ -272,32 +276,30 @@ def _patch_protobuf_class(cls):
272276

273277

274278
def _on_grpc_response(message):
275-
if _is_iast_enabled():
279+
if asm_config._iast_enabled:
276280
msg_cls = type(message)
277281
_patch_protobuf_class(msg_cls)
278282

279283

280284
def if_iast_taint_yield_tuple_for(origins, wrapped, instance, args, kwargs):
281-
if _is_iast_enabled():
282-
if not is_iast_request_enabled():
283-
for key, value in wrapped(*args, **kwargs):
284-
yield key, value
285-
else:
285+
if asm_config._iast_enabled and is_iast_request_enabled():
286+
try:
286287
for key, value in wrapped(*args, **kwargs):
287288
new_key = taint_pyobject(pyobject=key, source_name=key, source_value=key, source_origin=origins[0])
288289
new_value = taint_pyobject(
289290
pyobject=value, source_name=key, source_value=value, source_origin=origins[1]
290291
)
291292
yield new_key, new_value
292-
293+
except Exception:
294+
log.debug("Unexpected exception while tainting pyobject", exc_info=True)
293295
else:
294296
for key, value in wrapped(*args, **kwargs):
295297
yield key, value
296298

297299

298300
def if_iast_taint_returned_object_for(origin, wrapped, instance, args, kwargs):
299301
value = wrapped(*args, **kwargs)
300-
if _is_iast_enabled() and is_iast_request_enabled():
302+
if asm_config._iast_enabled and is_iast_request_enabled():
301303
try:
302304
if not is_pyobject_tainted(value):
303305
name = str(args[0]) if len(args) else "http.request.body"
@@ -311,7 +313,7 @@ def if_iast_taint_returned_object_for(origin, wrapped, instance, args, kwargs):
311313

312314
def if_iast_taint_starlette_datastructures(origin, wrapped, instance, args, kwargs):
313315
value = wrapped(*args, **kwargs)
314-
if _is_iast_enabled() and is_iast_request_enabled():
316+
if asm_config._iast_enabled and is_iast_request_enabled():
315317
try:
316318
res = []
317319
for element in value:
@@ -417,14 +419,7 @@ def _on_pre_tracedrequest_iast(ctx):
417419

418420

419421
def _on_set_request_tags_iast(request, span, flask_config):
420-
if _is_iast_enabled():
421-
_set_metric_iast_instrumented_source(OriginType.COOKIE_NAME)
422-
_set_metric_iast_instrumented_source(OriginType.COOKIE)
423-
_set_metric_iast_instrumented_source(OriginType.PARAMETER_NAME)
424-
425-
if not is_iast_request_enabled():
426-
return
427-
422+
if asm_config._iast_enabled and is_iast_request_enabled():
428423
request.cookies = taint_structure(
429424
request.cookies,
430425
OriginType.COOKIE_NAME,

ddtrace/appsec/_iast/_iast_request_context.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
from ddtrace.appsec._constants import APPSEC
77
from ddtrace.appsec._constants import IAST
8-
from ddtrace.appsec._iast import _is_iast_enabled
98
from ddtrace.appsec._iast import oce
109
from ddtrace.appsec._iast._metrics import _set_metric_iast_request_tainted
1110
from ddtrace.appsec._iast._metrics import _set_span_tag_iast_executed_sink
@@ -17,6 +16,7 @@
1716
from ddtrace.internal import core
1817
from ddtrace.internal.logger import get_logger
1918
from ddtrace.internal.utils.formats import asbool
19+
from ddtrace.settings.asm import config as asm_config
2020
from ddtrace.trace import Span
2121

2222

@@ -57,7 +57,7 @@ def in_iast_context() -> bool:
5757

5858

5959
def start_iast_context():
60-
if _is_iast_enabled():
60+
if asm_config._iast_enabled:
6161
create_propagation_context()
6262
core.set_item(_IAST_CONTEXT, IASTEnvironment())
6363

@@ -150,7 +150,7 @@ def _iast_end_request(ctx=None, span=None, *args, **kwargs):
150150
else:
151151
req_span = ctx.get_item("req_span")
152152

153-
if _is_iast_enabled():
153+
if asm_config._iast_enabled:
154154
existing_data = req_span.get_tag(IAST.JSON)
155155
if existing_data is None:
156156
if req_span.get_metric(IAST.ENABLED) is None:
@@ -173,7 +173,7 @@ def _iast_end_request(ctx=None, span=None, *args, **kwargs):
173173

174174
def _iast_start_request(span=None, *args, **kwargs):
175175
try:
176-
if _is_iast_enabled():
176+
if asm_config._iast_enabled:
177177
start_iast_context()
178178
request_iast_enabled = False
179179
if oce.acquire_request(span):

ddtrace/appsec/_iast/_loader.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
#!/usr/bin/env python3
22

33
from ddtrace.internal.logger import get_logger
4+
from ddtrace.settings.asm import config as asm_config
45

56
from ._ast.ast_patching import astpatch_module
6-
from ._utils import _is_iast_enabled
77

88

99
log = get_logger(__name__)
1010

11-
IS_IAST_ENABLED = _is_iast_enabled()
11+
IS_IAST_ENABLED = asm_config._iast_enabled
1212

1313

1414
def _exec_iast_patched_module(module_watchdog, module):

ddtrace/appsec/_iast/_pytest_plugin.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
from typing import List
55

66
from ddtrace.appsec._constants import IAST
7-
from ddtrace.appsec._iast._utils import _is_iast_enabled
87
from ddtrace.appsec._iast.reporter import Vulnerability
98
from ddtrace.internal.logger import get_logger
9+
from ddtrace.settings.asm import config as asm_config
1010

1111

1212
log = get_logger(__name__)
@@ -20,7 +20,7 @@ class VulnerabilityFoundInTest(Vulnerability):
2020
try:
2121
import pytest
2222

23-
@pytest.fixture(autouse=_is_iast_enabled())
23+
@pytest.fixture(autouse=asm_config._iast_enabled)
2424
def ddtrace_iast(request, ddspan):
2525
"""
2626
Extract the vulnerabilities discovered in tests.
@@ -72,7 +72,7 @@ def extract_code_snippet(filepath, line_number, context=3):
7272

7373

7474
def print_iast_report(terminalreporter):
75-
if not _is_iast_enabled():
75+
if not asm_config._iast_enabled:
7676
return
7777

7878
if not vuln_data:

ddtrace/appsec/_iast/_utils.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,8 @@
1-
from functools import lru_cache
2-
import sys
31
from typing import List
42

5-
from ddtrace.internal.logger import get_logger
63
from ddtrace.settings.asm import config as asm_config
74

85

9-
@lru_cache(maxsize=1)
10-
def _is_python_version_supported() -> bool:
11-
# IAST supports Python versions 3.6 to 3.13
12-
return (3, 6, 0) <= sys.version_info < (3, 14, 0)
13-
14-
15-
def _is_iast_enabled():
16-
if not asm_config._iast_enabled:
17-
return False
18-
19-
if not _is_python_version_supported():
20-
log = get_logger(__name__)
21-
log.info("IAST is not compatible with the current Python version")
22-
return False
23-
24-
return True
25-
26-
276
def _get_source_index(sources: List, source) -> int:
287
i = 0
298
for source_ in sources:

ddtrace/appsec/_iast/taint_sinks/insecure_cookie.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from typing import Dict
22
from typing import Optional
33

4+
from ddtrace.settings.asm import config as asm_config
5+
46
from ..._constants import IAST_SPAN_TAGS
5-
from .. import _is_iast_enabled
67
from .. import oce
78
from .._iast_request_context import is_iast_request_enabled
89
from .._metrics import _set_metric_iast_executed_sink
@@ -36,7 +37,7 @@ class NoSameSite(VulnerabilityBase):
3637
def asm_check_cookies(cookies: Optional[Dict[str, str]]) -> None:
3738
if not cookies:
3839
return
39-
if _is_iast_enabled() and is_iast_request_enabled():
40+
if asm_config._iast_enabled and is_iast_request_enabled():
4041
try:
4142
for cookie_key, cookie_value in cookies.items():
4243
lvalue = cookie_value.lower().replace(" ", "")

ddtrace/contrib/dbapi/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@
44
import wrapt
55

66
from ddtrace import config
7-
from ddtrace.appsec._iast._utils import _is_iast_enabled
7+
from ddtrace.appsec._constants import IAST_SPAN_TAGS
88
from ddtrace.internal import core
99
from ddtrace.internal.constants import COMPONENT
1010
from ddtrace.internal.logger import get_logger
1111
from ddtrace.internal.utils import ArgumentError
1212
from ddtrace.internal.utils import get_argument_value
13+
from ddtrace.settings.asm import config as asm_config
1314

14-
from ...appsec._constants import IAST_SPAN_TAGS
15-
from ...appsec._iast._metrics import increment_iast_span_metric
1615
from ...constants import _ANALYTICS_SAMPLE_RATE_KEY
1716
from ...constants import _SPAN_MEASURED_KEY
1817
from ...constants import SPAN_KIND
@@ -103,9 +102,10 @@ def _trace_method(self, method, name, resource, extra_tags, dbm_propagator, *arg
103102
# set span.kind to the type of request being performed
104103
s.set_tag_str(SPAN_KIND, SpanKind.CLIENT)
105104

106-
if _is_iast_enabled():
105+
if asm_config._iast_enabled:
107106
try:
108107
from ddtrace.appsec._iast._metrics import _set_metric_iast_executed_sink
108+
from ddtrace.appsec._iast._metrics import increment_iast_span_metric
109109
from ddtrace.appsec._iast._taint_utils import check_tainted_dbapi_args
110110
from ddtrace.appsec._iast.taint_sinks.sql_injection import SqlInjection
111111

0 commit comments

Comments
 (0)