From 71be800c555e5ebf3a2813363e1950e8dc72fa4b Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Wed, 26 Aug 2026 09:39:28 -0400 Subject: [PATCH 1/2] fix(starlite): Gate request body collection on data_collection experiment Attach `request.data` only when "incoming_request" is present in `data_collection.http_bodies`. When the experiment is unset, behaviour is unchanged; when it is set, it takes precedence over `send_default_pii`. Refs PY-2419 Refs #6283 --- sentry_sdk/integrations/starlite.py | 10 ++- tests/integrations/starlite/test_starlite.py | 74 +++++++++++++++++++- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/integrations/starlite.py b/sentry_sdk/integrations/starlite.py index 8963fc9e53..398412c762 100644 --- a/sentry_sdk/integrations/starlite.py +++ b/sentry_sdk/integrations/starlite.py @@ -268,6 +268,8 @@ async def handle_wrapper( def event_processor(event: "Event", _: "Hint") -> "Event": request_info = event.get("request", {}) request_info["content_length"] = len(scope.get("_body", b"")) + should_attach_request_body = True + if has_data_collection_enabled(client.options): cookies = _apply_key_value_collection_filtering( items=extracted_request_data["cookies"], @@ -275,9 +277,15 @@ def event_processor(event: "Event", _: "Hint") -> "Event": ) if cookies: request_info["cookies"] = cookies + + should_attach_request_body = ( + "incoming_request" + in client.options["data_collection"]["http_bodies"] + ) elif should_send_default_pii(): request_info["cookies"] = extracted_request_data["cookies"] - if request_data is not None: + + if request_data is not None and should_attach_request_body: request_info["data"] = request_data event["request"] = deepcopy(request_info) diff --git a/tests/integrations/starlite/test_starlite.py b/tests/integrations/starlite/test_starlite.py index e6a416d1f0..5bacb07053 100644 --- a/tests/integrations/starlite/test_starlite.py +++ b/tests/integrations/starlite/test_starlite.py @@ -4,7 +4,7 @@ from typing import Any, Dict import pytest -from starlite import AbstractMiddleware, Controller, LoggingConfig, Starlite, get +from starlite import AbstractMiddleware, Controller, LoggingConfig, Starlite, get, post from starlite.middleware import LoggingMiddlewareConfig, RateLimitConfig from starlite.middleware.session.memory_backend import MemoryBackendConfig from starlite.testing import TestClient @@ -43,6 +43,11 @@ async def message_with_id() -> "Dict[str, Any]": capture_message("hi") return {"status": "ok"} + @post("/body/json") + async def body_json(data: "Dict[str, Any]") -> "Dict[str, Any]": + capture_message("hi") + return {"status": "ok"} + logging_config = LoggingConfig() app = Starlite( @@ -51,6 +56,7 @@ async def message_with_id() -> "Dict[str, Any]": custom_error, message, message_with_id, + body_json, MyController, ], debug=debug, @@ -559,6 +565,72 @@ async def __call__(self, scope, receive, send): COOKIE_HEADER = "jwt=tokenval; theme=dark; lang=en; identity=alice" +@pytest.mark.parametrize( + "data_collection, expect_body", + [ + pytest.param(None, True, id="no_data_collection_experiment"), + pytest.param({}, True, id="data_collection_http_bodies_default"), + pytest.param( + {"http_bodies": ["incoming_request"]}, + True, + id="data_collection_http_bodies_incoming_request", + ), + pytest.param( + {"http_bodies": []}, False, id="data_collection_http_bodies_empty" + ), + ], +) +def test_request_body_data_collection( + sentry_init, capture_events, data_collection, expect_body +): + sentry_init( + traces_sample_rate=1.0, + integrations=[StarliteIntegration()], + _experiments=( + {} if data_collection is None else {"data_collection": data_collection} + ), + ) + + starlite_app = starlite_app_factory() + events = capture_events() + + body = {"foo": {"bar": "baz", "qux": ["1", "2", "3"]}} + + client = TestClient(starlite_app) + client.post("/body/json", json=body) + + (event, transaction_event) = events + + if expect_body: + assert event["request"]["data"] == body + assert transaction_event["request"]["data"] == body + else: + assert "data" not in event["request"] + assert "data" not in transaction_event["request"] + + +def test_request_body_data_collection_wins_over_send_default_pii( + sentry_init, capture_events +): + sentry_init( + traces_sample_rate=1.0, + integrations=[StarliteIntegration()], + send_default_pii=True, + _experiments={"data_collection": {"http_bodies": []}}, + ) + + starlite_app = starlite_app_factory() + events = capture_events() + + client = TestClient(starlite_app) + client.post("/body/json", json={"foo": {"bar": "baz", "qux": ["1", "2", "3"]}}) + + (event, transaction_event) = events + + assert "data" not in event["request"] + assert "data" not in transaction_event["request"] + + @pytest.mark.parametrize( "init_kwargs, expected_cookies", [ From c8a62cc8b1f130c4b1a2e71874700f48749c0903 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Wed, 26 Aug 2026 13:58:25 -0400 Subject: [PATCH 2/2] fix(starlite): Unquote body_json annotations to fix py3.8 OpenAPI schema The test module uses `from __future__ import annotations`, so the quoted annotation `data: "Dict[str, Any]"` is stored as the literal string `'"Dict[str, Any]"'`. `get_type_hints` evaluates that to the plain string `'Dict[str, Any]'`, which becomes a `ForwardRef` that Python <= 3.10 never resolves further. Starlite builds its OpenAPI schema eagerly in `Starlite.__init__`, so the unresolvable `ForwardRef` made `starlite_app_factory()` raise `ImproperlyConfiguredException` and took down the entire starlite suite on py3.8. Dropping the quotes resolves correctly on every Python version. --- tests/integrations/starlite/test_starlite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integrations/starlite/test_starlite.py b/tests/integrations/starlite/test_starlite.py index 5bacb07053..5c39c897d1 100644 --- a/tests/integrations/starlite/test_starlite.py +++ b/tests/integrations/starlite/test_starlite.py @@ -44,7 +44,7 @@ async def message_with_id() -> "Dict[str, Any]": return {"status": "ok"} @post("/body/json") - async def body_json(data: "Dict[str, Any]") -> "Dict[str, Any]": + async def body_json(data: Dict[str, Any]) -> Dict[str, Any]: capture_message("hi") return {"status": "ok"}