Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions sentry_sdk/integrations/_asgi_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import sentry_sdk
from sentry_sdk.data_collection import _apply_data_collection_filtering_to_query_string
from sentry_sdk.integrations._wsgi_common import _filter_headers
from sentry_sdk.integrations._wsgi_common import _filter_headers_legacy
from sentry_sdk.scope import should_send_default_pii
from sentry_sdk.utils import has_data_collection_enabled

Expand Down Expand Up @@ -121,7 +121,7 @@ def _get_request_data(

headers = _get_headers(asgi_scope)

request_data["headers"] = _filter_headers(
request_data["headers"] = _filter_headers_legacy(
headers,
use_annotated_value=False,
)
Expand Down Expand Up @@ -175,7 +175,7 @@ def _get_request_attributes(

headers = _get_headers(asgi_scope)

filtered_headers = _filter_headers(headers, use_annotated_value=False)
filtered_headers = _filter_headers_legacy(headers, use_annotated_value=False)
for header, value in filtered_headers.items():
attributes[f"http.request.header.{header.lower()}"] = value

Expand Down
125 changes: 96 additions & 29 deletions sentry_sdk/integrations/_wsgi_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@
from sentry_sdk._types import Event


DEFAULT_HTTP_METHODS_TO_CAPTURE = (
"CONNECT",
"DELETE",
"GET",
# "HEAD", # do not capture HEAD requests by default
# "OPTIONS", # do not capture OPTIONS requests by default
"PATCH",
"POST",
"PUT",
"TRACE",
)

SENSITIVE_ENV_KEYS = (
"REMOTE_ADDR",
"HTTP_X_FORWARDED_FOR",
Expand All @@ -39,18 +51,6 @@
x[len("HTTP_") :] for x in SENSITIVE_ENV_KEYS if x.startswith("HTTP_")
)

DEFAULT_HTTP_METHODS_TO_CAPTURE = (
"CONNECT",
"DELETE",
"GET",
# "HEAD", # do not capture HEAD requests by default
# "OPTIONS", # do not capture OPTIONS requests by default
"PATCH",
"POST",
"PUT",
"TRACE",
)


def request_body_within_bounds(
client: "Optional[sentry_sdk.client.BaseClient]", content_length: int
Expand Down Expand Up @@ -89,23 +89,15 @@ def extract_into_event(self, event: "Event") -> None:
content_length = self.content_length()
request_info = event.get("request", {})

# Prior to data collection being implemented we unconditionally attached
# the request body, which is why we default to True here.
attach_request_body = True

if has_data_collection_enabled(client.options):
cookies = _apply_key_value_collection_filtering(
items=dict(self.cookies()),
behaviour=client.options["data_collection"]["cookies"],
)
if cookies:
request_info["cookies"] = cookies
data_collection = client.options["data_collection"]
cookies = _apply_key_value_collection_filtering(
items=dict(self.cookies()),
behaviour=data_collection["cookies"],
)
if cookies:
request_info["cookies"] = cookies

attach_request_body = (
"incoming_request" in client.options["data_collection"]["http_bodies"]
)
elif should_send_default_pii():
request_info["cookies"] = dict(self.cookies())
attach_request_body = "incoming_request" in data_collection["http_bodies"]

if attach_request_body:
if not request_body_within_bounds(client, content_length):
Expand Down Expand Up @@ -210,6 +202,63 @@ def env(self) -> "Dict[str, Any]":
raise NotImplementedError()


class LegacyRequestExtractor(RequestExtractor):
def extract_into_event(self, event: "Event") -> None:
client = sentry_sdk.get_client()
if not client.is_active():
return

data: "Optional[Union[AnnotatedValue, Dict[str, Any]]]" = None
content_length = self.content_length()
request_info = event.get("request", {})

# Prior to data collection being implemented we unconditionally attached
# the request body, which is why we default to True here.
attach_request_body = True
if has_data_collection_enabled(client.options):
cookies = _apply_key_value_collection_filtering(
items=dict(self.cookies()),
behaviour=client.options["data_collection"]["cookies"],
)
if cookies:
request_info["cookies"] = cookies

attach_request_body = (
"incoming_request" in client.options["data_collection"]["http_bodies"]
)
elif should_send_default_pii():
request_info["cookies"] = dict(self.cookies())

if attach_request_body:
if not request_body_within_bounds(client, content_length):
data = AnnotatedValue.removed_because_over_size_limit()
else:
# First read the raw body data
# It is important to read this first because if it is Django
# it will cache the body and then we can read the cached version
# again in parsed_body() (or json() or wherever).
raw_data = None
try:
raw_data = self.raw_data()
except _RAW_DATA_EXCEPTIONS:
# If DjangoRestFramework is used it already read the body for us
# so reading it here will fail. We can ignore this.
pass

parsed_body = self.parsed_body()
if parsed_body is not None:
data = parsed_body
elif raw_data:
data = AnnotatedValue.removed_because_raw_data()
else:
data = None

if data is not None:
request_info["data"] = data

event["request"] = deepcopy(request_info)


def _is_json_content_type(ct: "Optional[str]") -> bool:
mt = (ct or "").split(";", 1)[0]
return (
Expand All @@ -222,12 +271,29 @@ def _is_json_content_type(ct: "Optional[str]") -> bool:
def _filter_headers(
headers: "Mapping[str, str]",
use_annotated_value: bool = True,
) -> "Mapping[str, Union[AnnotatedValue, str]]":
client = sentry_sdk.get_client()

filtered = _apply_key_value_collection_filtering(
items=headers,
behaviour=client.options["data_collection"]["http_headers"]["request"],
)

for key in filtered:
if isinstance(key, str) and key.lower() in ("cookie", "set-cookie"):
filtered[key] = SENSITIVE_DATA_SUBSTITUTE

return filtered


def _filter_headers_legacy(
headers: "Mapping[str, str]",
use_annotated_value: bool = True,
) -> "Mapping[str, Union[AnnotatedValue, str]]":
client_options = sentry_sdk.get_client().options

if has_data_collection_enabled(client_options):
data_collection_configuration = client_options["data_collection"]

filtered = _apply_key_value_collection_filtering(
items=headers,
behaviour=data_collection_configuration["http_headers"]["request"],
Expand All @@ -238,6 +304,7 @@ def _filter_headers(
filtered[key] = SENSITIVE_DATA_SUBSTITUTE

return filtered

else:
if should_send_default_pii():
return headers
Expand Down
6 changes: 3 additions & 3 deletions sentry_sdk/integrations/django/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
)
from sentry_sdk.integrations._wsgi_common import (
DEFAULT_HTTP_METHODS_TO_CAPTURE,
RequestExtractor,
LegacyRequestExtractor,
)
from sentry_sdk.integrations.logging import ignore_logger_for_events
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
Expand Down Expand Up @@ -703,7 +703,7 @@ def sentry_patched_response_for_exception(
exception_handler.response_for_exception = sentry_patched_response_for_exception


class DjangoRequestExtractor(RequestExtractor):
class DjangoRequestExtractor(LegacyRequestExtractor):
def __init__(self, request: "Union[WSGIRequest, ASGIRequest]") -> None:
try:
drf_request = request._sentry_drf_request_backref()
Expand Down Expand Up @@ -747,7 +747,7 @@ def parsed_body(self) -> "Optional[Dict[str, Any]]":
try:
return self.request.data
except Exception:
return RequestExtractor.parsed_body(self)
return LegacyRequestExtractor.parsed_body(self)


def _set_user_info(request: "WSGIRequest", event: "Event") -> None:
Expand Down
4 changes: 2 additions & 2 deletions sentry_sdk/integrations/flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from sentry_sdk.integrations import DidNotEnable, Integration, _check_minimum_version
from sentry_sdk.integrations._wsgi_common import (
DEFAULT_HTTP_METHODS_TO_CAPTURE,
RequestExtractor,
LegacyRequestExtractor,
)
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
from sentry_sdk.scope import should_send_default_pii
Expand Down Expand Up @@ -185,7 +185,7 @@ def _request_started(app: "Flask", **kwargs: "Any") -> None:
scope.add_event_processor(evt_processor)


class FlaskRequestExtractor(RequestExtractor):
class FlaskRequestExtractor(LegacyRequestExtractor):
def env(self) -> "Dict[str, str]":
return self.request.environ

Expand Down
6 changes: 3 additions & 3 deletions sentry_sdk/integrations/gcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from sentry_sdk.consts import OP
from sentry_sdk.data_collection import _apply_data_collection_filtering_to_query_string
from sentry_sdk.integrations import Integration
from sentry_sdk.integrations._wsgi_common import _filter_headers
from sentry_sdk.integrations._wsgi_common import _filter_headers_legacy
from sentry_sdk.integrations.cloud_resource_context import CLOUD_PROVIDER
from sentry_sdk.scope import Scope, should_send_default_pii
from sentry_sdk.traces import SegmentNameSource
Expand Down Expand Up @@ -86,7 +86,7 @@ def sentry_func(
header_attributes: "dict[str, Any]" = {}
if hasattr(gcp_event, "headers"):
headers = gcp_event.headers
for header, header_value in _filter_headers(
for header, header_value in _filter_headers_legacy(
headers, use_annotated_value=False
).items():
header_attributes[f"http.request.header.{header.lower()}"] = (
Expand Down Expand Up @@ -244,7 +244,7 @@ def event_processor(event: "Event", hint: "Hint") -> "Optional[Event]":
request["query_string"] = query_string

if hasattr(gcp_event, "headers"):
request["headers"] = _filter_headers(gcp_event.headers)
request["headers"] = _filter_headers_legacy(gcp_event.headers)

if hasattr(gcp_event, "data"):
if has_data_collection_enabled(client_options):
Expand Down
6 changes: 3 additions & 3 deletions sentry_sdk/integrations/quart.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from sentry_sdk.consts import SPANDATA
from sentry_sdk.data_collection import _apply_data_collection_filtering_to_query_string
from sentry_sdk.integrations import DidNotEnable, Integration
from sentry_sdk.integrations._wsgi_common import _filter_headers
from sentry_sdk.integrations._wsgi_common import _filter_headers_legacy
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
from sentry_sdk.scope import should_send_default_pii
from sentry_sdk.traces import SOURCE_FOR_STYLE as SEGMENT_SOURCE_FOR_STYLE
Expand Down Expand Up @@ -187,7 +187,7 @@ async def _request_websocket_started(app: "Quart", **kwargs: "Any") -> None:
segment.set_attribute("http.request.method", request_websocket.method)
header_attributes: "dict[str, Any]" = {}

for header, header_value in _filter_headers(
for header, header_value in _filter_headers_legacy(
dict(request_websocket.headers), use_annotated_value=False
).items():
header_attributes[f"http.request.header.{header.lower()}"] = header_value
Expand Down Expand Up @@ -282,7 +282,7 @@ def inner(event: "Event", hint: "dict[str, Any]") -> "Event":
request_info["url"] = request.url
request_info["query_string"] = request.query_string
request_info["method"] = request.method
request_info["headers"] = _filter_headers(dict(request.headers))
request_info["headers"] = _filter_headers_legacy(dict(request.headers))

client_options = sentry_sdk.get_client().options
if has_data_collection_enabled(client_options):
Expand Down
11 changes: 7 additions & 4 deletions sentry_sdk/integrations/sanic.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
_apply_data_collection_filtering_to_query_string,
)
from sentry_sdk.integrations import DidNotEnable, Integration, _check_minimum_version
from sentry_sdk.integrations._wsgi_common import RequestExtractor, _filter_headers
from sentry_sdk.integrations._wsgi_common import (
LegacyRequestExtractor,
_filter_headers_legacy,
)
from sentry_sdk.scope import should_send_default_pii
from sentry_sdk.traces import SegmentNameSource
from sentry_sdk.utils import (
Expand Down Expand Up @@ -64,7 +67,7 @@ def setup_once() -> None:
_setup_sanic()


class SanicRequestExtractor(RequestExtractor):
class SanicRequestExtractor(LegacyRequestExtractor):
def content_length(self) -> int:
if self.request.body is None:
return 0
Expand Down Expand Up @@ -251,7 +254,7 @@ def _get_request_attributes(request: "Request") -> "Dict[str, Any]":
if request.method:
attributes[SPANDATA.HTTP_REQUEST_METHOD] = request.method.upper()

headers = _filter_headers(dict(request.headers), use_annotated_value=False)
headers = _filter_headers_legacy(dict(request.headers), use_annotated_value=False)
for header, value in headers.items():
attributes[f"{SPANDATA.HTTP_REQUEST_HEADER}.{header.lower()}"] = value

Expand Down Expand Up @@ -340,7 +343,7 @@ def sanic_processor(event: "Event", hint: "Optional[Hint]") -> "Optional[Event]"
or client_options["data_collection"]["user_info"]
):
request_info["env"] = {"REMOTE_ADDR": request.remote_addr}
request_info["headers"] = _filter_headers(dict(request.headers))
request_info["headers"] = _filter_headers_legacy(dict(request.headers))

return event

Expand Down
10 changes: 5 additions & 5 deletions sentry_sdk/integrations/tornado.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from sentry_sdk.data_collection import _apply_data_collection_filtering_to_query_string
from sentry_sdk.integrations import DidNotEnable, Integration, _check_minimum_version
from sentry_sdk.integrations._wsgi_common import (
RequestExtractor,
_filter_headers,
LegacyRequestExtractor,
_filter_headers_legacy,
_is_json_content_type,
request_body_within_bounds,
)
Expand Down Expand Up @@ -147,7 +147,7 @@ def _get_request_attributes(request: "Any") -> "Dict[str, Any]":
if request.method:
attributes[SPANDATA.HTTP_REQUEST_METHOD] = request.method.upper()

headers = _filter_headers(dict(request.headers), use_annotated_value=False)
headers = _filter_headers_legacy(dict(request.headers), use_annotated_value=False)
for header, value in headers.items():
attributes[f"{SPANDATA.HTTP_REQUEST_HEADER}.{header.lower()}"] = value

Expand Down Expand Up @@ -280,7 +280,7 @@ def tornado_processor(event: "Event", hint: "dict[str, Any]") -> "Event":
or client_options["data_collection"]["user_info"]
):
request_info["env"] = {"REMOTE_ADDR": request.remote_ip}
request_info["headers"] = _filter_headers(dict(request.headers))
request_info["headers"] = _filter_headers_legacy(dict(request.headers))

if has_data_collection_enabled(client_options):
if client_options["data_collection"]["user_info"]:
Expand All @@ -305,7 +305,7 @@ def tornado_processor(event: "Event", hint: "dict[str, Any]") -> "Event":
return tornado_processor


class TornadoRequestExtractor(RequestExtractor):
class TornadoRequestExtractor(LegacyRequestExtractor):
def content_length(self) -> int:
if self.request.body is None:
return 0
Expand Down
Loading
Loading