Skip to content

Commit 2ceea73

Browse files
florentinlhappynancee
authored andcommitted
feat(appsec): add metric for unsupported lambda event types (#13855)
## Motivation Avoid billing when Appsec is enabled for unsupported lambda events. To keep track of executions with unsupported events, we add a span metric. ## Changes - Selectively skip processing the span based on the event To make the information available, I used the same pattern as the asm context initialization by storing temporary information inside the `ExecutionContext`. The only difference is that in the case of lambda we only have a single global `ExecutionContext` so we have to clean it up. ## Notes This PR relies on: DataDog/datadog-lambda-python#627 ## 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 d27975d commit 2ceea73

File tree

5 files changed

+42
-1
lines changed

5 files changed

+42
-1
lines changed

ddtrace/appsec/_constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ class APPSEC(metaclass=Constant_Class):
121121
RASP_ERROR: Literal["_dd.appsec.rasp.error"] = "_dd.appsec.rasp.error"
122122
ERROR_TYPE: Literal["_dd.appsec.error.type"] = "_dd.appsec.error.type"
123123
ERROR_MESSAGE: Literal["_dd.appsec.error.message"] = "_dd.appsec.error.message"
124+
UNSUPPORTED_EVENT_TYPE: Literal["_dd.appsec.unsupported_event_type"] = "_dd.appsec.unsupported_event_type"
124125

125126

126127
TELEMETRY_OFF_NAME = "OFF"

ddtrace/appsec/_processor.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
from typing import Tuple
1515
from typing import Union
1616

17+
from ddtrace.ext import SpanTypes
18+
from ddtrace.internal import core
19+
1720

1821
if TYPE_CHECKING:
1922
import ddtrace.appsec._ddwaf as ddwaf
@@ -181,6 +184,16 @@ def on_span_start(self, span: Span) -> None:
181184
if span.span_type not in asm_config._asm_processed_span_types:
182185
return
183186

187+
if span.span_type == SpanTypes.SERVERLESS:
188+
skip_event = core.get_item("appsec_skip_next_lambda_event")
189+
if skip_event:
190+
core.discard_item("appsec_skip_next_lambda_event")
191+
log.debug(
192+
"appsec: ignoring unsupported lamdba event",
193+
)
194+
span.set_metric(APPSEC.UNSUPPORTED_EVENT_TYPE, 1.0)
195+
return
196+
184197
ctx = self._ddwaf._at_request_start()
185198
_asm_request_context.start_context(span, ctx.rc_products if ctx is not None else "")
186199
peer_ip = _asm_request_context.get_ip()

ddtrace/settings/asm.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ class ASMConfig(DDConfig):
177177
"_asm_static_rule_file",
178178
"_asm_obfuscation_parameter_key_regexp",
179179
"_asm_obfuscation_parameter_value_regexp",
180+
"_asm_processed_span_types",
180181
"_apm_tracing_enabled",
181182
"_bypass_instrumentation_for_waf",
182183
"_iast_enabled",

tests/appsec/appsec/test_processor.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,3 +767,28 @@ def test_ephemeral_addresses(mock_run, persistent, ephemeral):
767767
WAF_DATA_NAMES[ephemeral]: {"key_2": "value_3"},
768768
}
769769
assert (span._local_root or span).get_tag(APPSEC.RC_PRODUCTS) == "[ASM:1] u:1 r:1"
770+
771+
772+
@pytest.mark.parametrize("skip_event", [True, False])
773+
def test_lambda_unsupported_event(tracer, skip_event):
774+
"""
775+
Test that the processor correctly handles the appsec_skip_next_lambda_event flag.
776+
"""
777+
if skip_event:
778+
core.set_item("appsec_skip_next_lambda_event", True)
779+
780+
config = {
781+
"_asm_enabled": True,
782+
"_asm_processed_span_types": {SpanTypes.SERVERLESS},
783+
}
784+
785+
with asm_context(tracer=tracer, config=config, span_type=SpanTypes.SERVERLESS) as span:
786+
pass
787+
788+
if skip_event:
789+
# When skip_event is True, the metric should be set and context item should be discarded
790+
assert span.get_metric(APPSEC.UNSUPPORTED_EVENT_TYPE) == 1.0
791+
assert core.get_item("appsec_skip_next_lambda_event") is None
792+
else:
793+
# When skip_event is False, the metric should not be set
794+
assert span.get_metric(APPSEC.UNSUPPORTED_EVENT_TYPE) is None

tests/appsec/utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def get_waf_addresses(address: str) -> typing.Optional[typing.Any]:
5353
def asm_context(
5454
tracer=None,
5555
span_name: str = "",
56+
span_type: str = SpanTypes.WEB,
5657
ip_addr: typing.Optional[str] = None,
5758
headers_case_sensitive: bool = False,
5859
headers: typing.Optional[typing.Dict[str, str]] = None,
@@ -80,7 +81,7 @@ def asm_context(
8081
headers=headers,
8182
block_request_callable=block_request_callable,
8283
service=service,
83-
), tracer.trace(span_name or "test", span_type=SpanTypes.WEB, service=service) as span:
84+
), tracer.trace(span_name or "test", span_type=span_type, service=service) as span:
8485
yield span
8586
unpatch_for_waf_addresses()
8687

0 commit comments

Comments
 (0)