Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
45f7309
chore(iast): context refactor
avara1986 Sep 5, 2025
db03389
chore(iast): context refactor
avara1986 Sep 8, 2025
cbf312c
chore(iast): join, index, format, extend migrated. modulo and re fail
avara1986 Sep 8, 2025
3aa695d
chore(iast): remove deprecated initializer functions
avara1986 Sep 8, 2025
f70e356
chore(iast): update tests
avara1986 Sep 8, 2025
e32e3a9
chore(iast): update tests
avara1986 Sep 8, 2025
16ad30d
chore(iast): modulo first approach
avara1986 Sep 8, 2025
0b5b00e
chore(iast): fix style
avara1986 Sep 8, 2025
eedfc0a
chore(iast): fix tests
avara1986 Sep 9, 2025
4a2abfe
chore(iast): fix tests
avara1986 Sep 10, 2025
6c57386
chore(iast): update get_map_objects to check subtypes
avara1986 Sep 11, 2025
628c858
Merge branch 'main' into avara1986/APPSEC-58124_replace_context_map
avara1986 Sep 11, 2025
351f8df
chore(iast): fix tests
avara1986 Sep 11, 2025
ec8c643
chore(iast): fix tests
avara1986 Sep 11, 2025
f7e00ee
chore(iast): fix tests
avara1986 Sep 11, 2025
493abac
chore(iast): fix tests
avara1986 Sep 11, 2025
0632751
chore: fix constant
avara1986 Sep 11, 2025
dc6a952
chore: fix constant
avara1986 Sep 11, 2025
6cc72e5
Merge branch 'main' into avara1986/APPSEC-58124_replace_context_map
avara1986 Sep 11, 2025
cf52972
chore(iast): fix tests
avara1986 Sep 11, 2025
6f0a00e
chore(iast): clean code
avara1986 Sep 12, 2025
32c4553
chore(iast): fix tests
avara1986 Sep 12, 2025
040abea
chore(iast): clean code
avara1986 Sep 12, 2025
3ffe3f2
chore(iast): fix tests
avara1986 Sep 12, 2025
4c81368
chore(iast): fix tests
avara1986 Sep 12, 2025
9d40e7f
chore(iast): clean code
avara1986 Sep 12, 2025
f7d93bc
chore(iast): clean code
avara1986 Sep 12, 2025
2da5714
chore(iast): clean code
avara1986 Sep 12, 2025
09da208
chore(iast): fix tests
avara1986 Sep 12, 2025
58b4728
chore(iast): fix tests
avara1986 Sep 12, 2025
58d7a71
chore(iast): fix tests
avara1986 Sep 12, 2025
dac7774
chore(iast): fix tests
avara1986 Sep 12, 2025
0da4df7
chore(iast): fix benchmarks
avara1986 Sep 12, 2025
cff5f6f
chore(iast): fix benchmarks
avara1986 Sep 12, 2025
ab90e13
chore(iast): display more info in logs
avara1986 Sep 12, 2025
20883d1
chore(iast): fix benchmarks
avara1986 Sep 12, 2025
564371e
chore(iast): fix benchmarks
avara1986 Sep 12, 2025
86384f8
Merge branch 'main' into avara1986/APPSEC-58124_replace_context_map
avara1986 Sep 15, 2025
2319c78
chore(iast): fix tests
avara1986 Sep 15, 2025
6b03f1b
chore(iast): fix tests
avara1986 Sep 15, 2025
795de37
chore(iast): fix tests
avara1986 Sep 15, 2025
ddb57f2
Merge branch 'main' into avara1986/APPSEC-58124_replace_context_map
avara1986 Sep 15, 2025
e9457ea
chore(iast): fix tests
avara1986 Sep 15, 2025
1c5a7b3
chore(iast): fix tests
avara1986 Sep 15, 2025
392f695
chore(iast): experiment ci visibility
avara1986 Sep 15, 2025
49d741b
keep unpatched httplib
gnufede Sep 16, 2025
6181044
Revert "chore(iast): experiment ci visibility"
gnufede Sep 16, 2025
cdd8da7
use unpatched urllib_parse
gnufede Sep 16, 2025
795db1e
use unpatched urllib parse
gnufede Sep 16, 2025
2fbbb12
undo unpatched httpclient
gnufede Sep 16, 2025
bed3645
change approach to store function
gnufede Sep 16, 2025
adb80ee
cleanup
gnufede Sep 16, 2025
47fd4f8
revert to cleanup
gnufede Sep 16, 2025
902bd51
add log warning
gnufede Sep 17, 2025
cf1f634
remove assert
gnufede Sep 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions benchmarks/appsec_iast_aspects/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,29 @@


try:
# 3.15+
# >= 3.15
from ddtrace.appsec._iast._iast_request_context_base import _iast_finish_request as end_iast_context
from ddtrace.appsec._iast._iast_request_context_base import _iast_start_request as iast_start_request
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
except ImportError:
try:
# 3.6+
# >= 3.6; < 3.15
from ddtrace.appsec._iast._iast_request_context_base import end_iast_context
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
from ddtrace.appsec._iast._iast_request_context_base import start_iast_context as iast_start_request
except ImportError:
# Pre 3.6
# < 3.6
from ddtrace.appsec._iast._iast_request_context import end_iast_context
from ddtrace.appsec._iast._iast_request_context import iast_start_request

try:
# >= 3.6; < 3.15
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
except ImportError:
try:
# < 3.6
from ddtrace.appsec._iast._iast_request_context import set_iast_request_enabled
except ImportError:
# >= 3.15
set_iast_request_enabled = lambda x: None # noqa: E731


def _start_iast_context_and_oce():
Expand Down
18 changes: 13 additions & 5 deletions benchmarks/appsec_iast_aspects_ospath/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,29 @@


try:
# 3.15+
# >= 3.15
from ddtrace.appsec._iast._iast_request_context_base import _iast_finish_request as end_iast_context
from ddtrace.appsec._iast._iast_request_context_base import _iast_start_request as iast_start_request
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
except ImportError:
try:
# 3.6+
# >= 3.6; < 3.15
from ddtrace.appsec._iast._iast_request_context_base import end_iast_context
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
from ddtrace.appsec._iast._iast_request_context_base import start_iast_context as iast_start_request
except ImportError:
# Pre 3.6
# < 3.6
from ddtrace.appsec._iast._iast_request_context import end_iast_context
from ddtrace.appsec._iast._iast_request_context import iast_start_request

try:
# >= 3.6; < 3.15
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
except ImportError:
try:
# < 3.6
from ddtrace.appsec._iast._iast_request_context import set_iast_request_enabled
except ImportError:
# >= 3.15
set_iast_request_enabled = lambda x: None # noqa: E731


def _start_iast_context_and_oce():
Expand Down
18 changes: 13 additions & 5 deletions benchmarks/appsec_iast_aspects_re_module/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,29 @@


try:
# 3.15+
# >= 3.15
from ddtrace.appsec._iast._iast_request_context_base import _iast_finish_request as end_iast_context
from ddtrace.appsec._iast._iast_request_context_base import _iast_start_request as iast_start_request
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
except ImportError:
try:
# 3.6+
# >= 3.6; < 3.15
from ddtrace.appsec._iast._iast_request_context_base import end_iast_context
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
from ddtrace.appsec._iast._iast_request_context_base import start_iast_context as iast_start_request
except ImportError:
# Pre 3.6
# < 3.6
from ddtrace.appsec._iast._iast_request_context import end_iast_context
from ddtrace.appsec._iast._iast_request_context import iast_start_request

try:
# >= 3.6; < 3.15
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
except ImportError:
try:
# < 3.6
from ddtrace.appsec._iast._iast_request_context import set_iast_request_enabled
except ImportError:
# >= 3.15
set_iast_request_enabled = lambda x: None # noqa: E731


def _start_iast_context_and_oce():
Expand Down
18 changes: 13 additions & 5 deletions benchmarks/appsec_iast_aspects_split/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,29 @@


try:
# 3.15+
# >= 3.15
from ddtrace.appsec._iast._iast_request_context_base import _iast_finish_request as end_iast_context
from ddtrace.appsec._iast._iast_request_context_base import _iast_start_request as iast_start_request
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
except ImportError:
try:
# 3.6+
# >= 3.6; < 3.15
from ddtrace.appsec._iast._iast_request_context_base import end_iast_context
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
from ddtrace.appsec._iast._iast_request_context_base import start_iast_context as iast_start_request
except ImportError:
# Pre 3.6
# < 3.6
from ddtrace.appsec._iast._iast_request_context import end_iast_context
from ddtrace.appsec._iast._iast_request_context import iast_start_request

try:
# >= 3.6; < 3.15
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
except ImportError:
try:
# < 3.6
from ddtrace.appsec._iast._iast_request_context import set_iast_request_enabled
except ImportError:
# >= 3.15
set_iast_request_enabled = lambda x: None # noqa: E731


def _start_iast_context_and_oce():
Expand Down
18 changes: 13 additions & 5 deletions benchmarks/appsec_iast_propagation/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,29 @@


try:
# 3.15+
# >= 3.15
from ddtrace.appsec._iast._iast_request_context_base import _iast_finish_request as end_iast_context
from ddtrace.appsec._iast._iast_request_context_base import _iast_start_request as iast_start_request
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
except ImportError:
try:
# 3.6+
# >= 3.6; < 3.15
from ddtrace.appsec._iast._iast_request_context_base import end_iast_context
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
from ddtrace.appsec._iast._iast_request_context_base import start_iast_context as iast_start_request
except ImportError:
# Pre 3.6
# < 3.6
from ddtrace.appsec._iast._iast_request_context import end_iast_context
from ddtrace.appsec._iast._iast_request_context import iast_start_request

try:
# >= 3.6; < 3.15
from ddtrace.appsec._iast._iast_request_context_base import set_iast_request_enabled
except ImportError:
try:
# < 3.6
from ddtrace.appsec._iast._iast_request_context import set_iast_request_enabled
except ImportError:
# >= 3.15
set_iast_request_enabled = lambda x: None # noqa: E731

from ddtrace.appsec._iast._taint_tracking import OriginType
from ddtrace.appsec._iast._taint_tracking._taint_objects import taint_pyobject
Expand Down
1 change: 0 additions & 1 deletion ddtrace/appsec/_iast/_iast_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ class IASTEnvironment:
def __init__(self, span: Optional[Span] = None):
self.span = span or core.get_span()

self.request_enabled: bool = False
self.iast_reporter: Optional["IastSpanReporter"] = None
self.iast_span_metrics: Dict[str, int] = {}
self.iast_stack_trace_reported: bool = False
Expand Down
6 changes: 1 addition & 5 deletions ddtrace/appsec/_iast/_iast_request_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from ddtrace.appsec._iast._iast_env import _get_iast_env
import ddtrace.appsec._iast._iast_request_context_base as base
from ddtrace.appsec._iast._metrics import _set_metric_iast_request_tainted
from ddtrace.appsec._iast._overhead_control_engine import oce
from ddtrace.appsec._iast._span_metrics import _set_span_tag_iast_executed_sink
from ddtrace.appsec._iast._taint_tracking import OriginType
from ddtrace.appsec._iast._taint_tracking import origin_to_str
Expand Down Expand Up @@ -62,14 +61,11 @@ def _create_and_attach_iast_report_to_span(
base._set_span_tag_iast_request_tainted(req_span)
_set_span_tag_iast_executed_sink(req_span)

base.set_iast_request_enabled(False)
base._iast_finish_request(req_span)

if req_span.get_tag(_ORIGIN_KEY) is None:
req_span.set_tag_str(_ORIGIN_KEY, APPSEC.ORIGIN_VALUE)

oce.release_request()


def _iast_end_request(ctx=None, span=None, *args, **kwargs):
try:
Expand All @@ -84,14 +80,14 @@ def _iast_end_request(ctx=None, span=None, *args, **kwargs):
if req_span is None:
log.debug("iast::propagation::context::Error finishing IAST context. There isn't a SPAN")
return

if asm_config._iast_enabled:
existing_data = req_span.get_tag(IAST.JSON) or req_span.get_struct_tag(IAST.STRUCT)
if existing_data is None:
if req_span.get_metric(IAST.ENABLED) is None:
if not base.is_iast_request_enabled():
req_span.set_metric(IAST.ENABLED, 0.0)
base._iast_finish_request(req_span)
oce.release_request()
return

req_span.set_metric(IAST.ENABLED, 1.0)
Expand Down
91 changes: 56 additions & 35 deletions ddtrace/appsec/_iast/_iast_request_context_base.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import contextvars
from typing import Optional

from ddtrace._trace.span import Span
from ddtrace.appsec._constants import IAST
from ddtrace.appsec._constants import IAST_SPAN_TAGS
from ddtrace.appsec._iast._iast_env import IASTEnvironment
from ddtrace.appsec._iast._iast_env import _get_iast_env
from ddtrace.appsec._iast._overhead_control_engine import oce
from ddtrace.appsec._iast._taint_tracking import num_objects_tainted
from ddtrace.appsec._iast._taint_tracking._context import create_context as create_propagation_context
from ddtrace.appsec._iast._taint_tracking._context import reset_context as reset_propagation_context
from ddtrace.appsec._iast._taint_tracking._context import debug_debug_num_tainted_objects
from ddtrace.appsec._iast._taint_tracking._context import finish_request_context
from ddtrace.appsec._iast._taint_tracking._context import start_request_context
from ddtrace.appsec._iast.sampling.vulnerability_detection import update_global_vulnerability_limit
from ddtrace.internal import core
from ddtrace.internal.logger import get_logger
Expand All @@ -20,7 +19,7 @@

# Stopgap module for providing ASM context for the blocking features wrapping some contextvars.

IAST_CONTEXT = contextvars.ContextVar("iast_var", default=None)
IAST_CONTEXT: "contextvars.ContextVar[Optional[int]]" = contextvars.ContextVar("iast_var", default=None)


def _set_span_tag_iast_request_tainted(span):
Expand All @@ -30,10 +29,6 @@ def _set_span_tag_iast_request_tainted(span):
span.set_tag(IAST_SPAN_TAGS.TELEMETRY_REQUEST_TAINTED, total_objects_tainted)


def finalize_iast_env(env: IASTEnvironment) -> None:
core.discard_item(IAST.REQUEST_CONTEXT_KEY)


def get_iast_stacktrace_reported() -> bool:
env = _get_iast_env()
if env:
Expand All @@ -47,14 +42,6 @@ def set_iast_stacktrace_reported(reported: bool) -> None:
env.iast_stack_trace_reported = reported


def set_iast_request_enabled(request_enabled) -> None:
env = _get_iast_env()
if env:
env.request_enabled = request_enabled
else:
log.debug("iast::propagation::context::Trying to set IAST reporter but no context is present")


def set_iast_request_endpoint(method, route) -> None:
if asm_config._iast_enabled:
env = _get_iast_env()
Expand All @@ -67,36 +54,70 @@ def set_iast_request_endpoint(method, route) -> None:
log.debug("iast::propagation::context::Trying to set IAST request endpoint but no context is present")


def _iast_start_request(span=None, *args, **kwargs):
try:
if asm_config._iast_enabled:
create_propagation_context()
core.set_item(IAST.REQUEST_CONTEXT_KEY, IASTEnvironment(span))
request_iast_enabled = False
if oce.acquire_request(span):
request_iast_enabled = True
set_iast_request_enabled(request_iast_enabled)
except Exception:
log.debug("iast::propagation::context::Error starting IAST context", exc_info=True)
def _iast_start_request(span=None) -> Optional[int]:
"""Initialize the IAST request context for the current execution.

This function acquires the IAST request budget via the Overhead Control Engine,
creates a new native taint context, and stores its identifier in a ContextVar so
subsequent IAST operations can locate the request-local taint map. If a context
is already active, the existing identifier is reused.

The provided span, when present, is attached to the IAST environment for later
enrichment and end-of-request processing.
"""
context_id = None

if asm_config._iast_enabled:
if oce.acquire_request(span):
if not is_iast_request_enabled():
context_id = start_request_context()
IAST_CONTEXT.set(context_id)
if context_id is not None:
core.set_item(IAST.REQUEST_CONTEXT_KEY, IASTEnvironment(span))
elif (context_id := _get_iast_context_id()) is not None:
finish_request_context(context_id)
IAST_CONTEXT.set(None)
return context_id


def _get_iast_context_id() -> Optional[int]:
"""Retrieve the current IAST context identifier from the ContextVar."""
return IAST_CONTEXT.get()


def _iast_finish_request(span: Optional["Span"] = None):
def _iast_finish_request(span=None, shoud_update_global_vulnerability_limit: bool = True) -> bool:
"""Finalize the IAST request context and optionally update global limits.

This function discards the per-request IAST environment, optionally updates the
global vulnerability optimization data, and releases the native taint context
associated with the active request.
"""
env = _get_iast_env()
if env is not None and env.span is span:
update_global_vulnerability_limit(env)
finalize_iast_env(env)
reset_propagation_context()
if shoud_update_global_vulnerability_limit:
update_global_vulnerability_limit(env)
core.discard_item(IAST.REQUEST_CONTEXT_KEY)

context_id = _get_iast_context_id()
if context_id is not None:
finish_request_context(context_id)
IAST_CONTEXT.set(None)
return True

return False


def is_iast_request_enabled() -> bool:
"""Check whether IAST is currently operating within an active request context."""
return asm_config.is_iast_request_enabled
return _get_iast_context_id() is not None


def _num_objects_tainted_in_request() -> int:
"""Get the count of tainted objects tracked in the active IAST request context.

def _num_objects_tainted_in_request():
return num_objects_tainted()
Useful for span metrics and internal telemetry.
"""
context_id = _get_iast_context_id()
if context_id is not None:
return debug_debug_num_tainted_objects(context_id)
return 0
Loading