Skip to content

Commit d2a6689

Browse files
chore(aap): move iast and api sec logic out of the tracer (#14292)
Following #14244, continue to remove appsec specific code out of the tracer. - IAST Processor is now enabled in product - API Security Manager is now enabled in product or RC layer APPSEC-57505 ## 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 ad0e6ec commit d2a6689

File tree

12 files changed

+83
-34
lines changed

12 files changed

+83
-34
lines changed

ddtrace/_trace/tracer.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -74,27 +74,6 @@ def _default_span_processors_factory(
7474
span_processors: List[SpanProcessor] = []
7575
span_processors += [TopLevelSpanProcessor()]
7676

77-
if asm_config._asm_libddwaf_available:
78-
if asm_config._asm_enabled:
79-
if asm_config._api_security_enabled:
80-
from ddtrace.appsec._api_security.api_manager import APIManager
81-
82-
APIManager.enable()
83-
84-
else:
85-
# api_security_active will keep track of the service status of APIManager
86-
# we don't want to import the module if it was not started before due to
87-
# one click activation of ASM via Remote Config
88-
if asm_config._api_security_active:
89-
from ddtrace.appsec._api_security.api_manager import APIManager
90-
91-
APIManager.disable()
92-
93-
if asm_config._iast_enabled:
94-
from ddtrace.appsec._iast.processor import AppSecIastSpanProcessor
95-
96-
span_processors.append(AppSecIastSpanProcessor())
97-
9877
# When using the NativeWriter stats are computed by the native code.
9978
if config._trace_compute_stats and not config._trace_writer_native:
10079
# Inline the import to avoid pulling in ddsketch or protobuf

ddtrace/appsec/_iast/processor.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from dataclasses import dataclass
2+
from typing import ClassVar
3+
from typing import Optional
24

35
from ddtrace._trace.processor import SpanProcessor
46
from ddtrace._trace.span import Span
@@ -14,6 +16,22 @@
1416

1517
@dataclass(eq=False)
1618
class AppSecIastSpanProcessor(SpanProcessor):
19+
_instance: ClassVar[Optional["AppSecIastSpanProcessor"]] = None
20+
21+
@classmethod
22+
def enable(cls) -> None:
23+
"""Enable the IAST span processor."""
24+
if cls._instance is None:
25+
instance = cls._instance = cls()
26+
instance.register()
27+
28+
@classmethod
29+
def disable(cls) -> None:
30+
"""Disable the IAST span processor."""
31+
if cls._instance is not None:
32+
cls._instance.unregister()
33+
cls._instance = None
34+
1735
def __post_init__(self) -> None:
1836
from . import load_iast
1937

ddtrace/appsec/_listeners.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ def load_appsec() -> None:
3535
from ddtrace.appsec._processor import AppSecSpanProcessor
3636

3737
AppSecSpanProcessor.enable()
38+
if asm_config._api_security_enabled and not asm_config._api_security_active:
39+
from ddtrace.appsec._api_security.api_manager import APIManager
40+
41+
APIManager.enable()
3842

3943

4044
def load_common_appsec_modules():

ddtrace/appsec/_remoteconfiguration.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,23 @@ def disable_asm(local_tracer: Tracer):
167167
AppSecSpanProcessor.disable()
168168

169169
asm_config._asm_enabled = False
170+
if asm_config._api_security_active:
171+
from ddtrace.appsec._api_security.api_manager import APIManager
172+
173+
APIManager.disable()
174+
170175
local_tracer.configure(appsec_enabled=False)
171176

172177

173178
def enable_asm(local_tracer: Tracer):
174-
if not asm_config._asm_enabled:
179+
if asm_config._asm_can_be_enabled and not asm_config._asm_enabled:
175180
from ddtrace.appsec._listeners import load_appsec
176181

177182
asm_config._asm_enabled = True
183+
if asm_config._api_security_enabled:
184+
from ddtrace.appsec._api_security.api_manager import APIManager
185+
186+
APIManager.enable()
178187
load_appsec()
179188
local_tracer.configure(appsec_enabled=True, appsec_enabled_origin=APPSEC.ENABLED_ORIGIN_RC)
180189

ddtrace/appsec/_utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ def set_container_depth(self, depth: int) -> None:
5252
else:
5353
self.container_depth = max(self.container_depth, depth)
5454

55+
def __repr__(self) -> str:
56+
return f"_observator(length={self.string_length}, size={self.container_size}, depth={self.container_depth})"
57+
5558

5659
class DDWaf_result:
5760
__slots__ = [

ddtrace/internal/iast/product.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,11 @@ def post_preload():
8787
def start():
8888
"""
8989
Start the IAST product.
90-
91-
Currently a no-op as all initialization happens in post_preload().
9290
"""
93-
pass
91+
if asm_config._iast_enabled:
92+
from ddtrace.appsec._iast.processor import AppSecIastSpanProcessor
93+
94+
AppSecIastSpanProcessor.enable()
9495

9596

9697
def restart(join=False):

riotfile.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3520,6 +3520,7 @@ def select_pys(min_version: str = MIN_PYTHON_VERSION, max_version: str = MAX_PYT
35203520
"DD_TRACE_AGENT_URL": "http://testagent:9126",
35213521
"AGENT_VERSION": "testagent",
35223522
"DD_REMOTE_CONFIGURATION_ENABLED": "true",
3523+
"DD_API_SECURITY_SAMPLE_DELAY": "0",
35233524
},
35243525
venvs=[
35253526
Venv(
@@ -3572,6 +3573,7 @@ def select_pys(min_version: str = MIN_PYTHON_VERSION, max_version: str = MAX_PYT
35723573
"DD_TRACE_AGENT_URL": "http://testagent:9126",
35733574
"AGENT_VERSION": "testagent",
35743575
"DD_REMOTE_CONFIGURATION_ENABLED": "true",
3576+
"DD_API_SECURITY_SAMPLE_DELAY": "0",
35753577
},
35763578
venvs=[
35773579
Venv(
@@ -3621,6 +3623,7 @@ def select_pys(min_version: str = MIN_PYTHON_VERSION, max_version: str = MAX_PYT
36213623
"AGENT_VERSION": "testagent",
36223624
"DD_REMOTE_CONFIGURATION_ENABLED": "true",
36233625
"DD_IAST_DEDUPLICATION_ENABLED": "false",
3626+
"DD_API_SECURITY_SAMPLE_DELAY": "0",
36243627
},
36253628
venvs=[
36263629
Venv(

tests/appsec/appsec/test_telemetry.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -279,21 +279,30 @@ def test_log_metric_error_ddwaf_update_deduplication_timelapse(telemetry_writer)
279279
({}, True, False, 1, APPSEC.ENABLED_ORIGIN_UNKNOWN),
280280
({}, True, True, 1, APPSEC.ENABLED_ORIGIN_UNKNOWN),
281281
({}, False, True, 1, APPSEC.ENABLED_ORIGIN_RC),
282-
({APPSEC_ENV: "true"}, False, True, 1, APPSEC.ENABLED_ORIGIN_ENV),
282+
({APPSEC_ENV: "true"}, True, True, 1, APPSEC.ENABLED_ORIGIN_ENV),
283+
(
284+
{APPSEC_ENV: "true"},
285+
False,
286+
True,
287+
0,
288+
APPSEC.ENABLED_ORIGIN_ENV,
289+
), # 0 because RC should not change the value if env var is set
283290
),
284291
)
285292
def test_appsec_enabled_metric(
286293
environment, appsec_enabled, rc_enabled, expected_result, expected_origin, telemetry_writer, tracer
287294
):
288295
"""Test that an internal error is logged when the WAF returns an internal error."""
289-
# Restore defaults.
290-
tracer.configure(appsec_enabled=appsec_enabled, appsec_enabled_origin=APPSEC.ENABLED_ORIGIN_UNKNOWN)
296+
# Restore defaults and enabling telemetry appsec service
297+
with override_global_config({"_asm_enabled": True}):
298+
tracer.configure(appsec_enabled=appsec_enabled, appsec_enabled_origin=APPSEC.ENABLED_ORIGIN_UNKNOWN)
291299
telemetry_writer._flush_configuration_queue()
292300

293301
# Start the test
294-
with override_env(environment), override_global_config(dict(_asm_enabled=appsec_enabled)):
302+
with override_env(environment), override_global_config(
303+
dict(_asm_enabled=appsec_enabled, _remote_config_enabled=rc_enabled)
304+
):
295305
tracer.configure(appsec_enabled=appsec_enabled)
296-
AppSecSpanProcessor()
297306
if rc_enabled:
298307
enable_asm(tracer)
299308

12 KB
Binary file not shown.

tests/appsec/contrib_appsec/utils.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ def test_truncation_telemetry(self, interface: Interface, get_entry_span_metric)
322322
("rate_limited", "false"),
323323
),
324324
),
325-
]
325+
], args_list
326326

327327
@pytest.mark.parametrize("asm_enabled", [True, False])
328328
@pytest.mark.parametrize(
@@ -1358,8 +1358,14 @@ def test_api_security_scanners(
13581358
@pytest.mark.parametrize("priority", ["keep", "drop"])
13591359
@pytest.mark.parametrize("delay", [0.0, 120.0])
13601360
def test_api_security_sampling(self, interface: Interface, get_entry_span_tag, apisec_enabled, priority, delay):
1361+
from ddtrace.appsec._api_security.api_manager import APIManager
13611362
from ddtrace.ext import http
13621363

1364+
# clear the hashtable to avoid collisions with previous tests
1365+
if apisec_enabled:
1366+
assert APIManager._instance
1367+
APIManager._instance._hashtable.clear()
1368+
13631369
payload = {"mastercard": "5123456789123456"}
13641370
with override_global_config(
13651371
dict(_asm_enabled=True, _api_security_enabled=apisec_enabled, _api_security_sample_delay=delay)

0 commit comments

Comments
 (0)