Skip to content

Commit

Permalink
feat(asm): standalone sca billing (#11655)
Browse files Browse the repository at this point in the history
  • Loading branch information
gnufede authored Dec 17, 2024
1 parent 29ccfdc commit 01d9b50
Show file tree
Hide file tree
Showing 5 changed files with 390 additions and 243 deletions.
6 changes: 4 additions & 2 deletions ddtrace/_trace/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,9 @@ def __init__(
self._iast_enabled = asm_config._iast_enabled
self._appsec_standalone_enabled = asm_config._appsec_standalone_enabled
self._dogstatsd_url = agent.get_stats_url() if dogstatsd_url is None else dogstatsd_url
self._apm_opt_out = (self._asm_enabled or self._iast_enabled) and self._appsec_standalone_enabled
self._apm_opt_out = self._appsec_standalone_enabled and (
self._asm_enabled or self._iast_enabled or config._sca_enabled
)
if self._apm_opt_out:
self.enabled = False
# Disable compute stats (neither agent or tracer should compute them)
Expand Down Expand Up @@ -498,7 +500,7 @@ def configure(
if appsec_standalone_enabled is not None:
self._appsec_standalone_enabled = asm_config._appsec_standalone_enabled = appsec_standalone_enabled

if self._appsec_standalone_enabled and (self._asm_enabled or self._iast_enabled):
if self._appsec_standalone_enabled and (self._asm_enabled or self._iast_enabled or config._sca_enabled):
self._apm_opt_out = True
self.enabled = False
# Disable compute stats (neither agent or tracer should compute them)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
features:
- |
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``
134 changes: 119 additions & 15 deletions tests/appsec/appsec/test_asm_standalone.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,145 @@
#!/usr/bin/env python3
import copy

import pytest

import ddtrace
from ddtrace.contrib.trace_utils import set_http_meta
from ddtrace.ext import SpanTypes
from tests.utils import override_env


@pytest.fixture(
params=[
{"iast_enabled": True, "appsec_enabled": True, "appsec_standalone_enabled": True},
{"iast_enabled": True, "appsec_enabled": True, "appsec_standalone_enabled": False},
{"iast_enabled": True, "appsec_enabled": False, "appsec_standalone_enabled": False},
{"iast_enabled": True, "appsec_enabled": False, "appsec_standalone_enabled": True},
{"iast_enabled": False, "appsec_enabled": True, "appsec_standalone_enabled": True},
{"iast_enabled": False, "appsec_enabled": True, "appsec_standalone_enabled": False},
{"iast_enabled": False, "appsec_enabled": False, "appsec_standalone_enabled": False},
{"iast_enabled": False, "appsec_enabled": False, "appsec_standalone_enabled": True},
{"appsec_enabled": True},
{"appsec_enabled": False},
{"iast_enabled": True},
{"iast_enabled": False},
{"DD_APPSEC_SCA_ENABLED": "1", "iast_enabled": True, "appsec_enabled": True, "appsec_standalone_enabled": True},
{
"DD_APPSEC_SCA_ENABLED": "1",
"iast_enabled": True,
"appsec_enabled": True,
"appsec_standalone_enabled": False,
},
{
"DD_APPSEC_SCA_ENABLED": "1",
"iast_enabled": True,
"appsec_enabled": False,
"appsec_standalone_enabled": False,
},
{
"DD_APPSEC_SCA_ENABLED": "1",
"iast_enabled": True,
"appsec_enabled": False,
"appsec_standalone_enabled": True,
},
{
"DD_APPSEC_SCA_ENABLED": "1",
"iast_enabled": False,
"appsec_enabled": True,
"appsec_standalone_enabled": True,
},
{
"DD_APPSEC_SCA_ENABLED": "1",
"iast_enabled": False,
"appsec_enabled": True,
"appsec_standalone_enabled": False,
},
{
"DD_APPSEC_SCA_ENABLED": "1",
"iast_enabled": False,
"appsec_enabled": False,
"appsec_standalone_enabled": False,
},
{
"DD_APPSEC_SCA_ENABLED": "1",
"iast_enabled": False,
"appsec_enabled": False,
"appsec_standalone_enabled": True,
},
{"DD_APPSEC_SCA_ENABLED": "1", "appsec_enabled": True},
{"DD_APPSEC_SCA_ENABLED": "1", "appsec_enabled": False},
{"DD_APPSEC_SCA_ENABLED": "1", "iast_enabled": True},
{"DD_APPSEC_SCA_ENABLED": "1", "iast_enabled": False},
{"DD_APPSEC_SCA_ENABLED": "0", "iast_enabled": True, "appsec_enabled": True, "appsec_standalone_enabled": True},
{
"DD_APPSEC_SCA_ENABLED": "0",
"iast_enabled": True,
"appsec_enabled": True,
"appsec_standalone_enabled": False,
},
{
"DD_APPSEC_SCA_ENABLED": "0",
"iast_enabled": True,
"appsec_enabled": False,
"appsec_standalone_enabled": False,
},
{
"DD_APPSEC_SCA_ENABLED": "0",
"iast_enabled": True,
"appsec_enabled": False,
"appsec_standalone_enabled": True,
},
{
"DD_APPSEC_SCA_ENABLED": "0",
"iast_enabled": False,
"appsec_enabled": True,
"appsec_standalone_enabled": True,
},
{
"DD_APPSEC_SCA_ENABLED": "0",
"iast_enabled": False,
"appsec_enabled": True,
"appsec_standalone_enabled": False,
},
{
"DD_APPSEC_SCA_ENABLED": "0",
"iast_enabled": False,
"appsec_enabled": False,
"appsec_standalone_enabled": False,
},
{
"DD_APPSEC_SCA_ENABLED": "0",
"iast_enabled": False,
"appsec_enabled": False,
"appsec_standalone_enabled": True,
},
{"DD_APPSEC_SCA_ENABLED": "0", "appsec_enabled": True},
{"DD_APPSEC_SCA_ENABLED": "0", "appsec_enabled": False},
{"DD_APPSEC_SCA_ENABLED": "0", "iast_enabled": True},
{"DD_APPSEC_SCA_ENABLED": "0", "iast_enabled": False},
]
)
def tracer_appsec_standalone(request, tracer):
tracer.configure(api_version="v0.4", **request.param)
yield tracer, request.param
new_env = {k: v for k, v in request.param.items() if k.startswith("DD_")}
with override_env(new_env):
# Reset the config so it picks up the env var value
ddtrace.config._reset()

# Copy the params to a new dict, including the env var
request_param_copy = copy.deepcopy(request.param)

# Remove the environment variables as they are unexpected args for the tracer configure
request.param.pop("DD_APPSEC_SCA_ENABLED", None)
tracer.configure(api_version="v0.4", **request.param)

yield tracer, request_param_copy

# Reset tracer configuration
ddtrace.config._reset()
tracer.configure(api_version="v0.4", appsec_enabled=False, appsec_standalone_enabled=False, iast_enabled=False)


def test_appsec_standalone_apm_enabled_metric(tracer_appsec_standalone):
tracer, args = tracer_appsec_standalone

with tracer.trace("test", span_type=SpanTypes.WEB) as span:
set_http_meta(span, {}, raw_uri="http://example.com/.git", status_code="404")

if args.get("appsec_standalone_enabled", None) and (
args.get("appsec_enabled", None) or args.get("iast_enabled", None)
args.get("appsec_enabled", None)
or args.get("iast_enabled", None)
or args.get("DD_APPSEC_SCA_ENABLED", "0") == "1"
):
assert tracer._apm_opt_out is True
assert span.get_metric("_dd.apm.enabled") == 0.0
else:
assert tracer._apm_opt_out is False
assert span.get_metric("_dd.apm.enabled") is None
Loading

0 comments on commit 01d9b50

Please sign in to comment.