diff --git a/ddtrace/appsec/_handlers.py b/ddtrace/appsec/_handlers.py index dcd2db2844d..7fd051cf2ef 100644 --- a/ddtrace/appsec/_handlers.py +++ b/ddtrace/appsec/_handlers.py @@ -301,13 +301,26 @@ def _on_django_func_wrapped(fn_args, fn_kwargs, first_arg_expected_type, *_): http_req.COOKIES = taint_structure(http_req.COOKIES, OriginType.COOKIE_NAME, OriginType.COOKIE) http_req.GET = taint_structure(http_req.GET, OriginType.PARAMETER_NAME, OriginType.PARAMETER) http_req.POST = taint_structure(http_req.POST, OriginType.BODY, OriginType.BODY) - if not is_pyobject_tainted(getattr(http_req, "_body", None)): - http_req._body = taint_pyobject( - http_req.body, - source_name=origin_to_str(OriginType.BODY), - source_value=http_req.body, - source_origin=OriginType.BODY, - ) + if getattr(http_req, "_body", None) is not None and not is_pyobject_tainted(getattr(http_req, "_body", None)): + try: + http_req._body = taint_pyobject( + http_req._body, + source_name=origin_to_str(OriginType.BODY), + source_value=http_req._body, + source_origin=OriginType.BODY, + ) + except AttributeError: + log.debug("IAST can't set attribute http_req._body", exc_info=True) + elif getattr(http_req, "body", None) is not None and not is_pyobject_tainted(getattr(http_req, "body", None)): + try: + http_req.body = taint_pyobject( + http_req.body, + source_name=origin_to_str(OriginType.BODY), + source_value=http_req.body, + source_origin=OriginType.BODY, + ) + except AttributeError: + log.debug("IAST can't set attribute http_req.body", exc_info=True) http_req.headers = taint_structure(http_req.headers, OriginType.HEADER_NAME, OriginType.HEADER) http_req.path = taint_pyobject( diff --git a/ddtrace/contrib/internal/django/utils.py b/ddtrace/contrib/internal/django/utils.py index 50c032a77ee..489088aa78c 100644 --- a/ddtrace/contrib/internal/django/utils.py +++ b/ddtrace/contrib/internal/django/utils.py @@ -289,7 +289,7 @@ def _extract_body(request): def _remake_body(request): # some libs that utilize django (Spyne) require the body stream to be unread or else will throw errors # see: https://github.com/arskom/spyne/blob/f105ec2f41495485fef1211fe73394231b3f76e5/spyne/server/wsgi.py#L538 - if request.method in _BODY_METHODS: + if request.method in _BODY_METHODS and getattr(request, "_body", None): try: unread_body = io.BytesIO(request._body) if unread_body.seekable(): diff --git a/tests/contrib/django/test_django_appsec_iast.py b/tests/contrib/django/test_django_appsec_iast.py index 7f07dab17bb..2bc808736d0 100644 --- a/tests/contrib/django/test_django_appsec_iast.py +++ b/tests/contrib/django/test_django_appsec_iast.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import json +import logging import pytest @@ -32,6 +33,19 @@ def reset_context(): _ = create_context() +@pytest.fixture(autouse=True) +def check_native_code_exception_in_each_python_aspect_test(request, caplog): + if "skip_iast_check_logs" in request.keywords: + yield + else: + caplog.set_level(logging.DEBUG) + with override_env({IAST.ENV_DEBUG: "true"}), caplog.at_level(logging.DEBUG): + yield + + log_messages = [record.message for record in caplog.get_records("call")] + assert not any("[IAST] " in message for message in log_messages), log_messages + + def _aux_appsec_get_root_span( client, test_spans,