Skip to content

Commit

Permalink
chore(wsgi): remove some appsec code from wsgi contrib (#6326)
Browse files Browse the repository at this point in the history
This change adjusts the `flask_block` callback-setting logic to use the
Core API rather than the AppSec-specific `set_value` call it had used
previously.

In the case of request blocking, the separation of concerns ideally
breaks down as follows. The AppSec Product code in the `ddtrace/appsec`
directory knows how to make a block/don't block decision based on
communication with libddwaf. The Wsgi code in `ddtrace/contrib` knows
how to take that blocking decision into account when processing
requests.

## 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/releasenotes.html)
are followed. If no release note is required, add label
`changelog/no-changelog`.
- [x] Documentation is included (in-code, generated user docs, [public
corp docs](https://github.com/DataDog/documentation/)).
- [x] Backport labels are set (if
[applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting))

## Reviewer Checklist

- [ ] Title is accurate.
- [ ] No unnecessary changes are introduced.
- [ ] Description motivates each change.
- [ ] Avoids breaking
[API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces)
changes unless absolutely necessary.
- [ ] Testing strategy adequately addresses listed risk(s).
- [ ] Change is maintainable (easy to change, telemetry, documentation).
- [ ] Release note makes sense to a user of the library.
- [ ] Reviewer has explicitly 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)

---------

Co-authored-by: Federico Mon <federico.mon@datadoghq.com>
  • Loading branch information
emmettbutler and gnufede authored Jul 17, 2023
1 parent 4e17ec3 commit 9a3693f
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 12 deletions.
24 changes: 19 additions & 5 deletions ddtrace/appsec/_asm_request_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def __init__(self):

def finalise(self):
if self.active:
env = core.get_item("asm_env")
env = self.execution_context.get_item("asm_env")
# assert _CONTEXT_ID.get() == self._id
callbacks = GLOBAL_CALLBACKS.get(_CONTEXT_CALL, []) + env.callbacks.get(_CONTEXT_CALL)
if callbacks is not None:
Expand Down Expand Up @@ -319,12 +319,26 @@ def asm_request_context_manager(
The ASM context manager
"""
if config._appsec_enabled:
resources = _DataHandler()
asm_request_context_set(remote_ip, headers, headers_case_sensitive, block_request_callable)
resources = _on_context_started(remote_ip, headers, headers_case_sensitive, block_request_callable)
try:
yield resources
finally:
resources.finalise()
core.set_item("asm_env", None)
_on_context_ended(resources)
else:
yield None


def _on_context_started(remote_ip, headers, headers_case_sensitive, block_request_callable):
resources = _DataHandler()
asm_request_context_set(remote_ip, headers, headers_case_sensitive, block_request_callable)
core.on("wsgi.block_decided", _on_block_decided)
return resources


def _on_context_ended(resources):
resources.finalise()
core.set_item("asm_env", None)


def _on_block_decided(callback):
set_value(_CALLBACKS, "flask_block", callback)
9 changes: 2 additions & 7 deletions ddtrace/contrib/wsgi/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,29 +165,25 @@ def __call__(self, environ, start_response):
headers = get_request_headers(environ)
closing_iterator = ()
not_blocked = True
with _asm_request_context.asm_request_context_manager(
environ.get("REMOTE_ADDR"), headers, headers_case_sensitive=True
):
with _asm_request_context.asm_request_context_manager(environ.get("REMOTE_ADDR"), headers, True):
req_span = self.tracer.trace(
self._request_span_name,
service=trace_utils.int_service(self._pin, self._config),
span_type=SpanTypes.WEB,
)

if self.tracer._appsec_enabled:
# [IP Blocking]
if core.get_item(HTTP_REQUEST_BLOCKED, span=req_span):
ctype, content = self._make_block_content(environ, headers, req_span)
start_response("403 FORBIDDEN", [("content-type", ctype)])
closing_iterator = [content]
not_blocked = False

# [Suspicious Request Blocking on request]
def blocked_view():
ctype, content = self._make_block_content(environ, headers, req_span)
return content, 403, [("content-type", ctype)]

_asm_request_context.set_value(_asm_request_context._CALLBACKS, "flask_block", blocked_view)
core.dispatch("wsgi.block_decided", [blocked_view])

if not_blocked:
req_span.set_tag_str(COMPONENT, self._config.integration_name)
Expand All @@ -212,7 +208,6 @@ def blocked_view():
req_span.finish()
raise
if self.tracer._appsec_enabled and core.get_item(HTTP_REQUEST_BLOCKED, span=req_span):
# [Suspicious Request Blocking on request or response]
_, content = self._make_block_content(environ, headers, req_span)
closing_iterator = [content]

Expand Down

0 comments on commit 9a3693f

Please sign in to comment.