Skip to content

Commit 1e3ceeb

Browse files
authored
chore(iast): refactor is_iast_request_enabled (#14555)
This PR doesn't modify the logic, it's only to simplify and reduce the number of modification of the main PR IAST Context refactor #14513: - move asm_config.is_iast_request_enabled inside the is_iast_request_enabled() - move _num_objects_tainted_in_request to _iast_request_context_base.py ## 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)
1 parent 3c5c3fe commit 1e3ceeb

30 files changed

+149
-85
lines changed

ddtrace/appsec/_common_module_patches.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ def wrapped_open_ED4CF71136E15EBF(original_open_callable, instance, args, kwargs
219219
# API10, doing all request calls in HTTPConnection.request
220220
try:
221221
response = original_open_callable(*args, **kwargs)
222-
# api10 response handler for regular reponses
222+
# api10 response handler for regular responses
223223
if response.__class__.__name__ == "HTTPResponse":
224224
addresses = {
225225
"DOWN_RES_STATUS": str(response.status),
@@ -230,7 +230,7 @@ def wrapped_open_ED4CF71136E15EBF(original_open_callable, instance, args, kwargs
230230
call_waf_callback(addresses, rule_type=EXPLOIT_PREVENTION.TYPE.SSRF)
231231
return response
232232
except Exception as e:
233-
# api10 response handler for error reponses
233+
# api10 response handler for error responses
234234
if e.__class__.__name__ == "HTTPError":
235235
try:
236236
status_code = e.code
@@ -256,10 +256,13 @@ def wrapped_request_D8CB81E472AF98A2(original_request_callable, instance, args,
256256
wrapper for third party requests.request function
257257
https://requests.readthedocs.io
258258
"""
259-
if asm_config._iast_enabled and asm_config.is_iast_request_enabled:
260-
from ddtrace.appsec._iast.taint_sinks.ssrf import _iast_report_ssrf
259+
if asm_config._iast_enabled:
260+
from ddtrace.appsec._iast._iast_request_context_base import is_iast_request_enabled
261+
262+
if is_iast_request_enabled():
263+
from ddtrace.appsec._iast.taint_sinks.ssrf import _iast_report_ssrf
261264

262-
_iast_report_ssrf(original_request_callable, *args, **kwargs)
265+
_iast_report_ssrf(original_request_callable, *args, **kwargs)
263266

264267
if _get_rasp_capability("ssrf"):
265268
try:

ddtrace/appsec/_iast/_handlers.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from ddtrace.appsec._constants import IAST
88
from ddtrace.appsec._iast._iast_request_context_base import get_iast_stacktrace_reported
9+
from ddtrace.appsec._iast._iast_request_context_base import is_iast_request_enabled
910
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_endpoint
1011
from ddtrace.appsec._iast._iast_request_context_base import set_iast_stacktrace_reported
1112
from ddtrace.appsec._iast._logs import iast_instrumentation_wrapt_debug_log
@@ -36,7 +37,7 @@
3637

3738
def _on_request_init(wrapped, instance, args, kwargs):
3839
wrapped(*args, **kwargs)
39-
if asm_config._iast_enabled and asm_config.is_iast_request_enabled:
40+
if is_iast_request_enabled():
4041
try:
4142
instance.query_string = taint_pyobject(
4243
pyobject=instance.query_string,
@@ -137,7 +138,7 @@ def _on_flask_patch(flask_version):
137138
def _iast_on_wrapped_view(kwargs):
138139
# If IAST is enabled, taint the Flask function kwargs (path parameters)
139140
if kwargs and asm_config._iast_enabled:
140-
if not asm_config.is_iast_request_enabled:
141+
if not is_iast_request_enabled():
141142
return kwargs
142143

143144
_kwargs = {}
@@ -150,7 +151,7 @@ def _iast_on_wrapped_view(kwargs):
150151

151152

152153
def _on_wsgi_environ(wrapped, _instance, args, kwargs):
153-
if asm_config._iast_enabled and args and asm_config.is_iast_request_enabled:
154+
if is_iast_request_enabled():
154155
return wrapped(*((taint_structure(args[0], OriginType.HEADER_NAME, OriginType.HEADER),) + args[1:]), **kwargs)
155156

156157
return wrapped(*args, **kwargs)
@@ -187,7 +188,7 @@ def _on_django_patch():
187188

188189

189190
def _on_django_middleware(ctx: core.ExecutionContext, call_trace: bool = True, **kwargs) -> None:
190-
if not asm_config._iast_enabled or not asm_config.is_iast_request_enabled:
191+
if not asm_config._iast_enabled or not is_iast_request_enabled():
191192
return
192193

193194
request = ctx.find_item("request")
@@ -200,7 +201,7 @@ def _on_django_func_wrapped(fn_args, fn_kwargs, first_arg_expected_type, *_):
200201
# If IAST is enabled, and we're wrapping a Django view call, taint the kwargs (view's
201202
# path parameters)
202203
if asm_config._iast_enabled and fn_args and isinstance(fn_args[0], first_arg_expected_type):
203-
if not asm_config.is_iast_request_enabled:
204+
if not is_iast_request_enabled():
204205
return
205206

206207
_taint_django_func_call(fn_args[0], fn_args, fn_kwargs)
@@ -321,7 +322,7 @@ def _on_grpc_response(message):
321322

322323

323324
def if_iast_taint_yield_tuple_for(origins, wrapped, instance, args, kwargs):
324-
if asm_config._iast_enabled and asm_config.is_iast_request_enabled:
325+
if is_iast_request_enabled():
325326
try:
326327
for key, value in wrapped(*args, **kwargs):
327328
new_key = taint_pyobject(pyobject=key, source_name=key, source_value=key, source_origin=origins[0])
@@ -338,7 +339,7 @@ def if_iast_taint_yield_tuple_for(origins, wrapped, instance, args, kwargs):
338339

339340
def if_iast_taint_returned_object_for(origin, wrapped, instance, args, kwargs):
340341
value = wrapped(*args, **kwargs)
341-
if asm_config._iast_enabled and asm_config.is_iast_request_enabled:
342+
if is_iast_request_enabled():
342343
try:
343344
if not is_pyobject_tainted(value):
344345
name = str(args[0]) if len(args) else "http.request.body"
@@ -352,7 +353,7 @@ def if_iast_taint_returned_object_for(origin, wrapped, instance, args, kwargs):
352353

353354
def if_iast_taint_starlette_datastructures(origin, wrapped, instance, args, kwargs):
354355
value = wrapped(*args, **kwargs)
355-
if asm_config._iast_enabled and asm_config.is_iast_request_enabled:
356+
if is_iast_request_enabled():
356357
try:
357358
res = []
358359
for element in value:
@@ -464,7 +465,7 @@ def _on_pre_tracedrequest_iast(ctx):
464465

465466

466467
def _on_set_request_tags_iast(request, span, flask_config):
467-
if asm_config.is_iast_request_enabled:
468+
if is_iast_request_enabled():
468469
try:
469470
if request.url_rule is not None:
470471
set_iast_request_endpoint(request.method, request.url_rule.rule)
@@ -493,7 +494,7 @@ def _on_set_request_tags_iast(request, span, flask_config):
493494

494495

495496
def _on_django_finalize_response_pre(ctx, after_request_tags, request, response):
496-
if not response or not asm_config.is_iast_request_enabled or get_iast_stacktrace_reported():
497+
if not response or not is_iast_request_enabled() or get_iast_stacktrace_reported():
497498
return
498499

499500
try:
@@ -506,7 +507,7 @@ def _on_django_finalize_response_pre(ctx, after_request_tags, request, response)
506507

507508

508509
def _on_django_technical_500_response(request, response, exc_type, exc_value, tb):
509-
if not exc_value or not asm_config._iast_enabled or not asm_config.is_iast_request_enabled:
510+
if not exc_value or not asm_config._iast_enabled or not is_iast_request_enabled():
510511
return
511512

512513
try:
@@ -522,7 +523,7 @@ def _on_django_technical_500_response(request, response, exc_type, exc_value, tb
522523

523524

524525
def _on_flask_finalize_request_post(response, _):
525-
if not response or not asm_config.is_iast_request_enabled or get_iast_stacktrace_reported():
526+
if not response or not is_iast_request_enabled() or get_iast_stacktrace_reported():
526527
return
527528

528529
try:
@@ -536,7 +537,7 @@ def _on_flask_finalize_request_post(response, _):
536537

537538

538539
def _on_asgi_finalize_response(body, _):
539-
if not body or not asm_config.is_iast_request_enabled:
540+
if not body or not is_iast_request_enabled():
540541
return
541542

542543
try:
@@ -549,7 +550,7 @@ def _on_asgi_finalize_response(body, _):
549550

550551

551552
def _on_werkzeug_render_debugger_html(html):
552-
# we don't check asm_config.is_iast_request_enabled due to werkzeug.render_debugger_html works outside the request
553+
# we don't check is_iast_request_enabled() due to werkzeug.render_debugger_html works outside the request
553554
if not html or not asm_config._iast_enabled:
554555
return
555556

@@ -587,7 +588,7 @@ async def _iast_instrument_starlette_request_body(wrapped, instance, args, kwarg
587588

588589
def _iast_instrument_starlette_scope(scope, route):
589590
try:
590-
if asm_config.is_iast_request_enabled:
591+
if is_iast_request_enabled():
591592
set_iast_request_endpoint(scope.get("method"), route)
592593
if scope.get("path_params"):
593594
for k, v in scope["path_params"].items():

ddtrace/appsec/_iast/_iast_env.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1+
from typing import TYPE_CHECKING
12
from typing import Dict
23
from typing import Optional
34

45
from ddtrace._trace.span import Span
56
from ddtrace.appsec._constants import IAST
6-
from ddtrace.appsec._iast.reporter import IastSpanReporter
77
from ddtrace.internal import core
88

99

10+
if TYPE_CHECKING: # pragma: no cover - type checking only
11+
from ddtrace.appsec._iast.reporter import IastSpanReporter
12+
13+
1014
class IASTEnvironment:
1115
"""
1216
an object of this class contains all iast data
@@ -18,7 +22,7 @@ def __init__(self, span: Optional[Span] = None):
1822
self.span = span or core.get_span()
1923

2024
self.request_enabled: bool = False
21-
self.iast_reporter: Optional[IastSpanReporter] = None
25+
self.iast_reporter: Optional["IastSpanReporter"] = None
2226
self.iast_span_metrics: Dict[str, int] = {}
2327
self.iast_stack_trace_reported: bool = False
2428
self.vulnerability_copy_global_limit: Dict[str, int] = {}

ddtrace/appsec/_iast/_iast_request_context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def _iast_end_request(ctx=None, span=None, *args, **kwargs):
9292
existing_data = req_span.get_tag(IAST.JSON) or req_span.get_struct_tag(IAST.STRUCT)
9393
if existing_data is None:
9494
if req_span.get_metric(IAST.ENABLED) is None:
95-
if not asm_config.is_iast_request_enabled:
95+
if not base.is_iast_request_enabled():
9696
req_span.set_metric(IAST.ENABLED, 0.0)
9797
base.end_iast_context(req_span)
9898
oce.release_request()

ddtrace/appsec/_iast/_iast_request_context_base.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
from ddtrace.appsec._iast._iast_env import IASTEnvironment
88
from ddtrace.appsec._iast._iast_env import _get_iast_env
99
from ddtrace.appsec._iast._overhead_control_engine import oce
10+
from ddtrace.appsec._iast._taint_tracking import num_objects_tainted
1011
from ddtrace.appsec._iast._taint_tracking._context import create_context as create_propagation_context
1112
from ddtrace.appsec._iast._taint_tracking._context import reset_context as reset_propagation_context
12-
from ddtrace.appsec._iast._utils import _num_objects_tainted_in_request
1313
from ddtrace.appsec._iast.sampling.vulnerability_detection import update_global_vulnerability_limit
1414
from ddtrace.internal import core
1515
from ddtrace.internal.logger import get_logger
@@ -69,6 +69,11 @@ def set_iast_request_enabled(request_enabled) -> None:
6969
log.debug("iast::propagation::context::Trying to set IAST reporter but no context is present")
7070

7171

72+
def is_iast_request_enabled() -> bool:
73+
"""Check whether IAST is currently operating within an active request context."""
74+
return asm_config.is_iast_request_enabled
75+
76+
7277
def set_iast_request_endpoint(method, route) -> None:
7378
if asm_config._iast_enabled:
7479
env = _get_iast_env()
@@ -91,3 +96,7 @@ def _iast_start_request(span=None, *args, **kwargs):
9196
set_iast_request_enabled(request_iast_enabled)
9297
except Exception:
9398
log.debug("iast::propagation::context::Error starting IAST context", exc_info=True)
99+
100+
101+
def _num_objects_tainted_in_request():
102+
return num_objects_tainted()

ddtrace/appsec/_iast/_langchain.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from ddtrace.appsec._iast._iast_request_context_base import is_iast_request_enabled
12
from ddtrace.appsec._iast._logs import iast_error
23
from ddtrace.appsec._iast._taint_tracking._taint_objects import taint_pyobject
34
from ddtrace.appsec._iast._taint_tracking._taint_objects_base import get_tainted_ranges
@@ -323,7 +324,7 @@ async def _wrapper_prompt_template_aformat(func, instance, args, kwargs):
323324

324325
def _propagate_prompt_template_format(kwargs, result):
325326
try:
326-
if not asm_config.is_iast_request_enabled:
327+
if not is_iast_request_enabled():
327328
return result
328329

329330
for value in kwargs.values():

ddtrace/appsec/_iast/_metrics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
from ddtrace.appsec._constants import TELEMETRY_INFORMATION_VERBOSITY
88
from ddtrace.appsec._constants import TELEMETRY_MANDATORY_VERBOSITY
99
from ddtrace.appsec._deduplications import deduplication
10+
from ddtrace.appsec._iast._iast_request_context_base import _num_objects_tainted_in_request
1011
from ddtrace.appsec._iast._taint_tracking import OriginType
1112
from ddtrace.appsec._iast._taint_tracking import origin_to_str
1213
from ddtrace.appsec._iast._utils import _is_iast_debug_enabled
13-
from ddtrace.appsec._iast._utils import _num_objects_tainted_in_request
1414
from ddtrace.internal import telemetry
1515
from ddtrace.internal.logger import get_logger
1616
from ddtrace.internal.telemetry.constants import TELEMETRY_LOG_LEVEL

ddtrace/appsec/_iast/_patches/json_tainting.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from ddtrace.settings.asm import config as asm_config
55

66
from ..._constants import IAST
7+
from .._iast_request_context_base import is_iast_request_enabled
78
from .._patch_modules import WrapFunctonsForIAST
89

910

@@ -36,7 +37,7 @@ def patch():
3637

3738
def wrapped_loads(wrapped, instance, args, kwargs):
3839
obj = wrapped(*args, **kwargs)
39-
if asm_config._iast_enabled and asm_config.is_iast_request_enabled:
40+
if is_iast_request_enabled():
4041
try:
4142
from .._taint_tracking._taint_objects import taint_pyobject
4243
from .._taint_tracking._taint_objects_base import get_tainted_ranges

ddtrace/appsec/_iast/_span_metrics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
from ddtrace.appsec._constants import IAST_SPAN_TAGS
44
from ddtrace.appsec._iast._iast_env import _get_iast_env
5+
from ddtrace.appsec._iast._iast_request_context_base import _num_objects_tainted_in_request
56
from ddtrace.appsec._iast._metrics import _metric_key_as_snake_case
6-
from ddtrace.appsec._iast._utils import _num_objects_tainted_in_request
77

88

99
def _set_span_tag_iast_request_tainted(span):

ddtrace/appsec/_iast/_taint_tracking/_taint_objects.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from ddtrace.appsec._constants import IAST
66
from ddtrace.appsec._constants import IAST_SPAN_TAGS
77
from ddtrace.appsec._iast._iast_request_context_base import IAST_CONTEXT
8+
from ddtrace.appsec._iast._iast_request_context_base import is_iast_request_enabled
89
from ddtrace.appsec._iast._logs import iast_propagation_debug_log
910
from ddtrace.appsec._iast._metrics import _set_metric_iast_executed_source
1011
from ddtrace.appsec._iast._span_metrics import increment_iast_span_metric
@@ -13,15 +14,14 @@
1314
from ddtrace.appsec._iast._taint_tracking._taint_objects_base import _taint_pyobject_base
1415
from ddtrace.appsec._iast._taint_tracking._taint_objects_base import _taint_pyobject_base_new
1516
from ddtrace.internal.logger import get_logger
16-
from ddtrace.settings.asm import config as asm_config
1717

1818

1919
log = get_logger(__name__)
2020

2121

2222
def taint_pyobject(pyobject: Any, source_name: Any, source_value: Any, source_origin=None) -> Any:
2323
try:
24-
if asm_config.is_iast_request_enabled:
24+
if is_iast_request_enabled():
2525
if source_origin is None:
2626
source_origin = OriginType.PARAMETER
2727

@@ -51,7 +51,7 @@ def taint_pyobject_new(pyobject: Any, source_name: Any, source_value: Any, sourc
5151

5252
def copy_ranges_to_string(pyobject: str, ranges: Sequence[TaintRange]) -> str:
5353
# NB this function uses comment-based type annotation because TaintRange is conditionally imported
54-
if asm_config.is_iast_request_enabled:
54+
if is_iast_request_enabled():
5555
if not isinstance(pyobject, IAST.TAINTEABLE_TYPES):
5656
return pyobject
5757

0 commit comments

Comments
 (0)