Skip to content

Commit 01d9b50

Browse files
authored
feat(asm): standalone sca billing (#11655)
1 parent 29ccfdc commit 01d9b50

File tree

5 files changed

+390
-243
lines changed

5 files changed

+390
-243
lines changed

ddtrace/_trace/tracer.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,9 @@ def __init__(
236236
self._iast_enabled = asm_config._iast_enabled
237237
self._appsec_standalone_enabled = asm_config._appsec_standalone_enabled
238238
self._dogstatsd_url = agent.get_stats_url() if dogstatsd_url is None else dogstatsd_url
239-
self._apm_opt_out = (self._asm_enabled or self._iast_enabled) and self._appsec_standalone_enabled
239+
self._apm_opt_out = self._appsec_standalone_enabled and (
240+
self._asm_enabled or self._iast_enabled or config._sca_enabled
241+
)
240242
if self._apm_opt_out:
241243
self.enabled = False
242244
# Disable compute stats (neither agent or tracer should compute them)
@@ -498,7 +500,7 @@ def configure(
498500
if appsec_standalone_enabled is not None:
499501
self._appsec_standalone_enabled = asm_config._appsec_standalone_enabled = appsec_standalone_enabled
500502

501-
if self._appsec_standalone_enabled and (self._asm_enabled or self._iast_enabled):
503+
if self._appsec_standalone_enabled and (self._asm_enabled or self._iast_enabled or config._sca_enabled):
502504
self._apm_opt_out = True
503505
self.enabled = False
504506
# Disable compute stats (neither agent or tracer should compute them)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
features:
3+
- |
4+
ASM: This introduces "Standalone SCA billing", opting out for APM billing and applying to only SCA. Enable this by setting these two environment variables: ``DD_APPSEC_SCA_ENABLED`` and ``DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED``
+119-15
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,145 @@
11
#!/usr/bin/env python3
2+
import copy
3+
24
import pytest
35

6+
import ddtrace
47
from ddtrace.contrib.trace_utils import set_http_meta
58
from ddtrace.ext import SpanTypes
9+
from tests.utils import override_env
610

711

812
@pytest.fixture(
913
params=[
10-
{"iast_enabled": True, "appsec_enabled": True, "appsec_standalone_enabled": True},
11-
{"iast_enabled": True, "appsec_enabled": True, "appsec_standalone_enabled": False},
12-
{"iast_enabled": True, "appsec_enabled": False, "appsec_standalone_enabled": False},
13-
{"iast_enabled": True, "appsec_enabled": False, "appsec_standalone_enabled": True},
14-
{"iast_enabled": False, "appsec_enabled": True, "appsec_standalone_enabled": True},
15-
{"iast_enabled": False, "appsec_enabled": True, "appsec_standalone_enabled": False},
16-
{"iast_enabled": False, "appsec_enabled": False, "appsec_standalone_enabled": False},
17-
{"iast_enabled": False, "appsec_enabled": False, "appsec_standalone_enabled": True},
18-
{"appsec_enabled": True},
19-
{"appsec_enabled": False},
20-
{"iast_enabled": True},
21-
{"iast_enabled": False},
14+
{"DD_APPSEC_SCA_ENABLED": "1", "iast_enabled": True, "appsec_enabled": True, "appsec_standalone_enabled": True},
15+
{
16+
"DD_APPSEC_SCA_ENABLED": "1",
17+
"iast_enabled": True,
18+
"appsec_enabled": True,
19+
"appsec_standalone_enabled": False,
20+
},
21+
{
22+
"DD_APPSEC_SCA_ENABLED": "1",
23+
"iast_enabled": True,
24+
"appsec_enabled": False,
25+
"appsec_standalone_enabled": False,
26+
},
27+
{
28+
"DD_APPSEC_SCA_ENABLED": "1",
29+
"iast_enabled": True,
30+
"appsec_enabled": False,
31+
"appsec_standalone_enabled": True,
32+
},
33+
{
34+
"DD_APPSEC_SCA_ENABLED": "1",
35+
"iast_enabled": False,
36+
"appsec_enabled": True,
37+
"appsec_standalone_enabled": True,
38+
},
39+
{
40+
"DD_APPSEC_SCA_ENABLED": "1",
41+
"iast_enabled": False,
42+
"appsec_enabled": True,
43+
"appsec_standalone_enabled": False,
44+
},
45+
{
46+
"DD_APPSEC_SCA_ENABLED": "1",
47+
"iast_enabled": False,
48+
"appsec_enabled": False,
49+
"appsec_standalone_enabled": False,
50+
},
51+
{
52+
"DD_APPSEC_SCA_ENABLED": "1",
53+
"iast_enabled": False,
54+
"appsec_enabled": False,
55+
"appsec_standalone_enabled": True,
56+
},
57+
{"DD_APPSEC_SCA_ENABLED": "1", "appsec_enabled": True},
58+
{"DD_APPSEC_SCA_ENABLED": "1", "appsec_enabled": False},
59+
{"DD_APPSEC_SCA_ENABLED": "1", "iast_enabled": True},
60+
{"DD_APPSEC_SCA_ENABLED": "1", "iast_enabled": False},
61+
{"DD_APPSEC_SCA_ENABLED": "0", "iast_enabled": True, "appsec_enabled": True, "appsec_standalone_enabled": True},
62+
{
63+
"DD_APPSEC_SCA_ENABLED": "0",
64+
"iast_enabled": True,
65+
"appsec_enabled": True,
66+
"appsec_standalone_enabled": False,
67+
},
68+
{
69+
"DD_APPSEC_SCA_ENABLED": "0",
70+
"iast_enabled": True,
71+
"appsec_enabled": False,
72+
"appsec_standalone_enabled": False,
73+
},
74+
{
75+
"DD_APPSEC_SCA_ENABLED": "0",
76+
"iast_enabled": True,
77+
"appsec_enabled": False,
78+
"appsec_standalone_enabled": True,
79+
},
80+
{
81+
"DD_APPSEC_SCA_ENABLED": "0",
82+
"iast_enabled": False,
83+
"appsec_enabled": True,
84+
"appsec_standalone_enabled": True,
85+
},
86+
{
87+
"DD_APPSEC_SCA_ENABLED": "0",
88+
"iast_enabled": False,
89+
"appsec_enabled": True,
90+
"appsec_standalone_enabled": False,
91+
},
92+
{
93+
"DD_APPSEC_SCA_ENABLED": "0",
94+
"iast_enabled": False,
95+
"appsec_enabled": False,
96+
"appsec_standalone_enabled": False,
97+
},
98+
{
99+
"DD_APPSEC_SCA_ENABLED": "0",
100+
"iast_enabled": False,
101+
"appsec_enabled": False,
102+
"appsec_standalone_enabled": True,
103+
},
104+
{"DD_APPSEC_SCA_ENABLED": "0", "appsec_enabled": True},
105+
{"DD_APPSEC_SCA_ENABLED": "0", "appsec_enabled": False},
106+
{"DD_APPSEC_SCA_ENABLED": "0", "iast_enabled": True},
107+
{"DD_APPSEC_SCA_ENABLED": "0", "iast_enabled": False},
22108
]
23109
)
24110
def tracer_appsec_standalone(request, tracer):
25-
tracer.configure(api_version="v0.4", **request.param)
26-
yield tracer, request.param
111+
new_env = {k: v for k, v in request.param.items() if k.startswith("DD_")}
112+
with override_env(new_env):
113+
# Reset the config so it picks up the env var value
114+
ddtrace.config._reset()
115+
116+
# Copy the params to a new dict, including the env var
117+
request_param_copy = copy.deepcopy(request.param)
118+
119+
# Remove the environment variables as they are unexpected args for the tracer configure
120+
request.param.pop("DD_APPSEC_SCA_ENABLED", None)
121+
tracer.configure(api_version="v0.4", **request.param)
122+
123+
yield tracer, request_param_copy
124+
27125
# Reset tracer configuration
126+
ddtrace.config._reset()
28127
tracer.configure(api_version="v0.4", appsec_enabled=False, appsec_standalone_enabled=False, iast_enabled=False)
29128

30129

31130
def test_appsec_standalone_apm_enabled_metric(tracer_appsec_standalone):
32131
tracer, args = tracer_appsec_standalone
132+
33133
with tracer.trace("test", span_type=SpanTypes.WEB) as span:
34134
set_http_meta(span, {}, raw_uri="http://example.com/.git", status_code="404")
35135

36136
if args.get("appsec_standalone_enabled", None) and (
37-
args.get("appsec_enabled", None) or args.get("iast_enabled", None)
137+
args.get("appsec_enabled", None)
138+
or args.get("iast_enabled", None)
139+
or args.get("DD_APPSEC_SCA_ENABLED", "0") == "1"
38140
):
141+
assert tracer._apm_opt_out is True
39142
assert span.get_metric("_dd.apm.enabled") == 0.0
40143
else:
144+
assert tracer._apm_opt_out is False
41145
assert span.get_metric("_dd.apm.enabled") is None

0 commit comments

Comments
 (0)