From e43247d784fea92955afdc4f7dbb5145e6e1e8e0 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Thu, 25 Jun 2026 15:06:14 -0400 Subject: [PATCH] fix(boto3): Gate url.full, url.query, url.fragment behind send_default_pii URL attributes on streamed spans now require send_default_pii=True, consistent with the aiohttp and wsgi integrations. Co-Authored-By: Claude Opus 4.6 --- sentry_sdk/integrations/boto3.py | 3 +- tests/integrations/boto3/test_s3.py | 50 +++++++++++++++++------------ 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/sentry_sdk/integrations/boto3.py b/sentry_sdk/integrations/boto3.py index b460e91d8e..a7fdd99b21 100644 --- a/sentry_sdk/integrations/boto3.py +++ b/sentry_sdk/integrations/boto3.py @@ -4,6 +4,7 @@ import sentry_sdk from sentry_sdk.consts import OP, SPANDATA from sentry_sdk.integrations import DidNotEnable, Integration, _check_minimum_version +from sentry_sdk.scope import should_send_default_pii from sentry_sdk.traces import StreamedSpan from sentry_sdk.tracing import Span from sentry_sdk.tracing_utils import has_span_streaming_enabled @@ -74,7 +75,7 @@ def _sentry_request_created( SPANDATA.RPC_METHOD: f"{service_id}/{operation_name}", }, ) - if request.url is not None: + if request.url is not None and should_send_default_pii(): with capture_internal_exceptions(): parsed_url = parse_url(request.url, sanitize=False) span.set_attribute(SPANDATA.URL_FULL, parsed_url.url) diff --git a/tests/integrations/boto3/test_s3.py b/tests/integrations/boto3/test_s3.py index 8bdfce4223..16d318b7d1 100644 --- a/tests/integrations/boto3/test_s3.py +++ b/tests/integrations/boto3/test_s3.py @@ -72,16 +72,19 @@ def test_basic( assert span["description"] == "aws.s3.ListObjects" +@pytest.mark.parametrize("send_default_pii", [True, False]) @pytest.mark.parametrize("span_streaming", [True, False]) def test_streaming( sentry_init, capture_events, capture_items, span_streaming, + send_default_pii, ): sentry_init( traces_sample_rate=1.0, integrations=[Boto3Integration()], + send_default_pii=send_default_pii, _experiments={"trace_lifecycle": "stream" if span_streaming else "static"}, ) @@ -108,26 +111,32 @@ def test_streaming( span1 = spans[0] assert span1["attributes"]["sentry.op"] == "http.client" assert span1["name"] == "aws.s3.GetObject" - assert span1["attributes"] == ApproxDict( - { - "http.request.method": "GET", - "rpc.method": "S3/GetObject", - "sentry.environment": "production", - "sentry.op": "http.client", - "sentry.origin": "auto.http.boto3", - "sentry.release": mock.ANY, - "sentry.sdk.name": "sentry.python", - "sentry.sdk.version": mock.ANY, - "sentry.segment.id": mock.ANY, - "sentry.segment.name": "custom parent", - "server.address": mock.ANY, - "thread.id": mock.ANY, - "thread.name": mock.ANY, - "url.full": "https://bucket.s3.amazonaws.com/foo.pdf", - "url.fragment": "", - "url.query": "", - } - ) + + expected_attrs = { + "http.request.method": "GET", + "rpc.method": "S3/GetObject", + "sentry.environment": "production", + "sentry.op": "http.client", + "sentry.origin": "auto.http.boto3", + "sentry.release": mock.ANY, + "sentry.sdk.name": "sentry.python", + "sentry.sdk.version": mock.ANY, + "sentry.segment.id": mock.ANY, + "sentry.segment.name": "custom parent", + "server.address": mock.ANY, + "thread.id": mock.ANY, + "thread.name": mock.ANY, + } + if send_default_pii: + expected_attrs["url.full"] = "https://bucket.s3.amazonaws.com/foo.pdf" + expected_attrs["url.fragment"] = "" + expected_attrs["url.query"] = "" + assert span1["attributes"] == ApproxDict(expected_attrs) + + if not send_default_pii: + assert "url.full" not in span1["attributes"] + assert "url.fragment" not in span1["attributes"] + assert "url.query" not in span1["attributes"] span2 = spans[1] assert span2["attributes"]["sentry.op"] == "http.client.stream" @@ -233,6 +242,7 @@ def test_omit_url_data_if_parsing_fails( sentry_init( traces_sample_rate=1.0, integrations=[Boto3Integration()], + send_default_pii=True, _experiments={"trace_lifecycle": "stream" if span_streaming else "static"}, )