Skip to content

Commit 2e12b6b

Browse files
fix(appsec): always cleanup any created asm request context managers [backport #5813 to 1.13] (#5815)
Backport of #5813 to 1.13 There is a possible code path that could cause us to not clean up the asm request context added to the span context. If this occurs, then we will get an error encoding because `span.context._meta` is only meant to hold string values. This is a short term mitigation to ensure we always cleanup the context manager added to `span.context._meta`, but we should consider an alternative approach which doesn't rely on `span.context._meta`. ## Checklist - [x] Change(s) are motivated and described in the PR description. - [x] Testing strategy is described if automated tests are not included in the PR. - [x] Risk is outlined (performance impact, potential for breakage, maintainability, etc). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/contributing.html#Release-Note-Guidelines) are followed. - [x] Documentation is included (in-code, generated user docs, [public corp docs](https://github.com/DataDog/documentation/)). - [x] OPTIONAL: PR description includes explicit acknowledgement of the performance implications of the change as reported in the benchmarks PR comment. ## Reviewer Checklist - [x] Title is accurate. - [x] No unnecessary changes are introduced. - [x] Description motivates each change. - [x] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [x] Testing strategy adequately addresses listed risk(s). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] Release note makes sense to a user of the library. - [x] Reviewer has explicitly acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment. Co-authored-by: Brett Langdon <[email protected]>
1 parent a3deaea commit 2e12b6b

File tree

3 files changed

+36
-19
lines changed

3 files changed

+36
-19
lines changed

ddtrace/appsec/processor.py

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -376,23 +376,22 @@ def _is_needed(self, address):
376376

377377
def on_span_finish(self, span):
378378
# type: (Span) -> None
379-
if span.span_type != SpanTypes.WEB:
380-
return
381-
382-
# Force to set respond headers at the end
383-
headers_req = _context.get_item(SPAN_DATA_NAMES.RESPONSE_HEADERS_NO_COOKIES, span=span)
384-
if headers_req:
385-
_set_headers(span, headers_req, kind="response")
386-
387-
# this call is only necessary for tests or frameworks that are not using blocking
388-
if span.get_tag(APPSEC.JSON) is None and _asm_request_context.in_context():
389-
log.debug("metrics waf call")
390-
_asm_request_context.call_waf_callback()
391-
392-
self._ddwaf._at_request_end()
393-
394-
# release asm context if it was created by the span
395379
asm_context = span.context._meta.get("ASM_CONTEXT_%d" % id(span), None)
396-
if asm_context is not None:
397-
asm_context.__exit__(None, None, None) # type: ignore
398-
del span.context._meta["ASM_CONTEXT_%d" % id(span)]
380+
try:
381+
if span.span_type == SpanTypes.WEB:
382+
# Force to set respond headers at the end
383+
headers_req = _context.get_item(SPAN_DATA_NAMES.RESPONSE_HEADERS_NO_COOKIES, span=span)
384+
if headers_req:
385+
_set_headers(span, headers_req, kind="response")
386+
387+
# this call is only necessary for tests or frameworks that are not using blocking
388+
if span.get_tag(APPSEC.JSON) is None and _asm_request_context.in_context():
389+
log.debug("metrics waf call")
390+
_asm_request_context.call_waf_callback()
391+
392+
self._ddwaf._at_request_end()
393+
finally:
394+
# release asm context if it was created by the span
395+
if asm_context is not None:
396+
asm_context.__exit__(None, None, None) # type: ignore
397+
del span.context._meta["ASM_CONTEXT_%d" % id(span)]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fixes:
2+
- |
3+
appsec: Fixes an encoding error when we are unable to cleanup the AppSec request context associated with a span.

tests/appsec/test_processor.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,3 +652,18 @@ def test_ddwaf_run_contained_oserror(tracer_appsec, caplog):
652652

653653
assert span.get_tag(APPSEC.JSON) is None
654654
assert "OSError: ddwaf run failed" in caplog.text
655+
656+
657+
def test_asm_context_meta(tracer_appsec):
658+
tracer = tracer_appsec
659+
660+
# For a web type span, a context manager is added, but then removed
661+
with tracer.trace("test", span_type=SpanTypes.WEB) as span:
662+
assert span.context._meta["ASM_CONTEXT_%d" % (id(span),)]
663+
assert span.context._meta.get("ASM_CONTEXT_%d" % (id(span),)) is None
664+
665+
# Regression test, if the span type changes after being created, we always removed
666+
with tracer.trace("test", span_type=SpanTypes.WEB) as span:
667+
span.span_type = SpanTypes.HTTP
668+
assert span.context._meta["ASM_CONTEXT_%d" % (id(span),)]
669+
assert span.context._meta.get("ASM_CONTEXT_%d" % (id(span),)) is None

0 commit comments

Comments
 (0)