From bb9df176f8249dd705aa0b002ad36b22887730d9 Mon Sep 17 00:00:00 2001 From: Pablo Deputter Date: Wed, 23 Sep 2026 15:01:05 +0200 Subject: [PATCH 1/4] ref(boto3): Drop `send_default_pii` support --- tests/integrations/boto3/test_s3.py | 69 +++++++---------------------- 1 file changed, 15 insertions(+), 54 deletions(-) diff --git a/tests/integrations/boto3/test_s3.py b/tests/integrations/boto3/test_s3.py index 79ae609d52..e7c57126d9 100644 --- a/tests/integrations/boto3/test_s3.py +++ b/tests/integrations/boto3/test_s3.py @@ -50,17 +50,11 @@ def test_basic( assert span["name"] == "aws.s3.ListObjects" -@pytest.mark.parametrize("send_default_pii", [True, False]) -def test_streaming( - sentry_init, - capture_events, - capture_items, - send_default_pii, -): +def test_streaming(sentry_init, capture_events, capture_items): sentry_init( traces_sample_rate=1.0, integrations=[Boto3Integration()], - send_default_pii=send_default_pii, + data_collection={}, ) s3 = session.resource("s3") @@ -101,15 +95,11 @@ def test_streaming( "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.full"] = "https://bucket.s3.amazonaws.com/foo.pdf" assert span1["attributes"] == ApproxDict(expected_attrs) assert "url.fragment" not in span1["attributes"] assert "url.query" not in span1["attributes"] - if not send_default_pii: - assert "url.full" not in span1["attributes"] - span2 = spans[1] assert span2["attributes"]["sentry.op"] == "http.client.stream" assert span2["name"] == "aws.s3.GetObject" @@ -157,7 +147,7 @@ def test_omit_url_data_if_parsing_fails( sentry_init( traces_sample_rate=1.0, integrations=[Boto3Integration()], - send_default_pii=True, + data_collection={}, ) s3 = session.resource("s3") @@ -228,12 +218,11 @@ def test_span_origin( assert spans[0]["attributes"]["sentry.origin"] == "auto.http.boto3" -@pytest.mark.parametrize("send_default_pii", [True, False]) -def test_breadcrumb(sentry_init, capture_events, send_default_pii): +def test_breadcrumb(sentry_init, capture_events): sentry_init( integrations=[Boto3Integration()], default_integrations=False, - send_default_pii=send_default_pii, + data_collection={}, ) s3 = session.resource("s3") @@ -253,24 +242,14 @@ def test_breadcrumb(sentry_init, capture_events, send_default_pii): assert crumb["type"] == "http" assert crumb["category"] == "httplib" - if send_default_pii: - assert crumb["data"] == ApproxDict( - { - SPANDATA.URL_FULL: mock.ANY, - SPANDATA.HTTP_REQUEST_METHOD: "GET", - SPANDATA.URL_QUERY: mock.ANY, - } - ) - assert SPANDATA.URL_FRAGMENT not in crumb["data"] - else: - assert crumb["data"] == ApproxDict( - { - SPANDATA.HTTP_REQUEST_METHOD: "GET", - } - ) - assert SPANDATA.URL_FULL not in crumb["data"] - assert SPANDATA.URL_QUERY not in crumb["data"] - assert SPANDATA.URL_FRAGMENT not in crumb["data"] + assert crumb["data"] == ApproxDict( + { + SPANDATA.URL_FULL: mock.ANY, + SPANDATA.HTTP_REQUEST_METHOD: "GET", + SPANDATA.URL_QUERY: mock.ANY, + } + ) + assert SPANDATA.URL_FRAGMENT not in crumb["data"] BUCKET_URL = "https://bucket.s3.amazonaws.com/" @@ -279,19 +258,9 @@ def test_breadcrumb(sentry_init, capture_events, send_default_pii): # means the URL is recorded without a query string. # Structure of the parameters is "init_kwargs, expected_query" URL_QUERY_PARAMS = [ - pytest.param( - {"send_default_pii": True}, - "list-type=2&prefix=foo&continuation-token=abc&encoding-type=url", - id="send_default_pii_true", - ), - pytest.param( - {"send_default_pii": False}, - None, - id="send_default_pii_false", - ), pytest.param( {}, - None, + "list-type=2&prefix=foo&continuation-token=%5BFiltered%5D&encoding-type=url", id="defaults", ), pytest.param( @@ -334,14 +303,6 @@ def test_breadcrumb(sentry_init, capture_events, send_default_pii): "", id="data_collection_off", ), - pytest.param( - { - "send_default_pii": True, - "data_collection": {"url_query_params": {"mode": "off"}}, - }, - "", - id="data_collection_wins_over_send_default_pii", - ), ] From 2871745b78af455cff052160f36665e48dd66eb7 Mon Sep 17 00:00:00 2001 From: Pablo Deputter Date: Wed, 23 Sep 2026 15:09:56 +0200 Subject: [PATCH 2/4] ref(boto3): Remove `send_default_pii` fallback from `tracing_utils.get_url_attributes()` --- sentry_sdk/tracing_utils.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 7a81240dcc..32bba988e6 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -192,30 +192,24 @@ def add_http_breadcrumb(status_code: "Optional[int]", data: "dict[str, Any]") -> def get_url_attributes( - client: "sentry_sdk.client.BaseClient", parsed_url: "Optional[ParsedUrl]" + client: "sentry_sdk.client.BaseClient", + parsed_url: "Optional[ParsedUrl]", ) -> "Attributes": """Build the `url.*` span attributes for an outgoing HTTP request. - The query string is only included when the user has opted into collecting - it, either through `data_collection` (in which case the configured - filtering is applied) or through the legacy `send_default_pii`. + The query string is filtered through the resolved `data_collection` + settings. """ attributes: "Attributes" = {} if parsed_url is None: return attributes - query: "Optional[str]" - if has_data_collection_enabled(client.options): - query = None - if parsed_url.query: - query = _apply_data_collection_filtering_to_query_string( - query_string=parsed_url.query, - behaviour=client.options["data_collection"]["url_query_params"], - ) - elif client.should_send_default_pii(): - query = parsed_url.query - else: - return attributes + query: "Optional[str]" = None + if parsed_url.query: + query = _apply_data_collection_filtering_to_query_string( + query_string=parsed_url.query, + behaviour=client.options["data_collection"]["url_query_params"], + ) url_full = parsed_url.url if query: From 72d9464a556e8f8fb7f264baf0243627ca2da186 Mon Sep 17 00:00:00 2001 From: Pablo Deputter Date: Wed, 23 Sep 2026 15:29:42 +0200 Subject: [PATCH 3/4] ref(boto3): Add back `get_url_attributes_legacy()` --- sentry_sdk/integrations/httpx.py | 6 ++--- sentry_sdk/integrations/httpx2.py | 6 ++--- sentry_sdk/integrations/pyreqwest.py | 6 ++--- sentry_sdk/integrations/stdlib.py | 4 ++-- sentry_sdk/tracing_utils.py | 36 ++++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/sentry_sdk/integrations/httpx.py b/sentry_sdk/integrations/httpx.py index a3a79a67d5..4286b84aa9 100644 --- a/sentry_sdk/integrations/httpx.py +++ b/sentry_sdk/integrations/httpx.py @@ -7,7 +7,7 @@ from sentry_sdk.tracing_utils import ( add_http_breadcrumb, add_http_request_source, - get_url_attributes, + get_url_attributes_legacy, propagate_trace_headers, ) from sentry_sdk.utils import ( @@ -59,7 +59,7 @@ def send(self: "Client", request: "Request", **kwargs: "Any") -> "Response": with capture_internal_exceptions(): parsed_url = parse_url(str(request.url), sanitize=False) - url_attributes = get_url_attributes(client, parsed_url) + url_attributes = get_url_attributes_legacy(client, parsed_url) if sentry_sdk.get_current_span() is None: span_ctx = nullcontext() @@ -148,7 +148,7 @@ async def send( }, ) - url_attributes = get_url_attributes(client, parsed_url) + url_attributes = get_url_attributes_legacy(client, parsed_url) with span_ctx as span: propagate_trace_headers(client, request) diff --git a/sentry_sdk/integrations/httpx2.py b/sentry_sdk/integrations/httpx2.py index 2f382601a3..34804f7b03 100644 --- a/sentry_sdk/integrations/httpx2.py +++ b/sentry_sdk/integrations/httpx2.py @@ -7,7 +7,7 @@ from sentry_sdk.tracing_utils import ( add_http_breadcrumb, add_http_request_source, - get_url_attributes, + get_url_attributes_legacy, propagate_trace_headers, ) from sentry_sdk.utils import ( @@ -59,7 +59,7 @@ def send(self: "Client", request: "Request", **kwargs: "Any") -> "Response": with capture_internal_exceptions(): parsed_url = parse_url(str(request.url), sanitize=False) - url_attributes = get_url_attributes(client, parsed_url) + url_attributes = get_url_attributes_legacy(client, parsed_url) if sentry_sdk.get_current_span() is None: span_ctx = nullcontext() @@ -148,7 +148,7 @@ async def send( }, ) - url_attributes = get_url_attributes(client, parsed_url) + url_attributes = get_url_attributes_legacy(client, parsed_url) with span_ctx as span: propagate_trace_headers(client, request) diff --git a/sentry_sdk/integrations/pyreqwest.py b/sentry_sdk/integrations/pyreqwest.py index 4c90414095..3d9d3c0d8a 100644 --- a/sentry_sdk/integrations/pyreqwest.py +++ b/sentry_sdk/integrations/pyreqwest.py @@ -7,7 +7,7 @@ from sentry_sdk.tracing_utils import ( add_http_breadcrumb, add_http_request_source, - get_url_attributes, + get_url_attributes_legacy, propagate_trace_headers, ) from sentry_sdk.utils import ( @@ -155,7 +155,7 @@ async def sentry_async_middleware( # after the request has been sent parsed_url = parse_url(str(request.url), sanitize=False) - url_attributes = get_url_attributes(sentry_sdk.get_client(), parsed_url) + url_attributes = get_url_attributes_legacy(sentry_sdk.get_client(), parsed_url) response = None with _sentry_pyreqwest_span(request, url_attributes) as span: @@ -193,7 +193,7 @@ def sentry_sync_middleware( # after the request has been sent parsed_url = parse_url(str(request.url), sanitize=False) - url_attributes = get_url_attributes(sentry_sdk.get_client(), parsed_url) + url_attributes = get_url_attributes_legacy(sentry_sdk.get_client(), parsed_url) response = None with _sentry_pyreqwest_span(request, url_attributes) as span: diff --git a/sentry_sdk/integrations/stdlib.py b/sentry_sdk/integrations/stdlib.py index fdd544a94d..b6ebd83ad4 100644 --- a/sentry_sdk/integrations/stdlib.py +++ b/sentry_sdk/integrations/stdlib.py @@ -14,7 +14,7 @@ EnvironHeaders, add_http_breadcrumb, add_http_request_source, - get_url_attributes, + get_url_attributes_legacy, should_propagate_trace, ) from sentry_sdk.utils import ( @@ -274,7 +274,7 @@ def putrequest( span: "Optional[Span]" = None breadcrumb: "dict[str, Any]" = {} - url_attributes = get_url_attributes(client, parsed_url) + url_attributes = get_url_attributes_legacy(client, parsed_url) breadcrumb[SPANDATA.HTTP_REQUEST_METHOD] = method breadcrumb.update(url_attributes) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 32bba988e6..e6fd0f2861 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -191,6 +191,42 @@ def add_http_breadcrumb(status_code: "Optional[int]", data: "dict[str, Any]") -> sentry_sdk.add_breadcrumb(**kwargs) +def get_url_attributes_legacy( + client: "sentry_sdk.client.BaseClient", + parsed_url: "Optional[ParsedUrl]", +) -> "Attributes": + """Build outgoing HTTP URL attributes using legacy PII behavior.""" + attributes: "Attributes" = {} + if parsed_url is None: + return attributes + + query: "Optional[str]" + if has_data_collection_enabled(client.options): + query = None + if parsed_url.query: + query = _apply_data_collection_filtering_to_query_string( + query_string=parsed_url.query, + behaviour=client.options["data_collection"]["url_query_params"], + ) + elif client.should_send_default_pii(): + query = parsed_url.query + else: + return attributes + + url_full = parsed_url.url + if query: + attributes[SPANDATA.URL_QUERY] = query + url_full += "?" + query + + if parsed_url.fragment: + attributes[SPANDATA.URL_FRAGMENT] = parsed_url.fragment + url_full += "#" + parsed_url.fragment + + attributes[SPANDATA.URL_FULL] = url_full + + return attributes + + def get_url_attributes( client: "sentry_sdk.client.BaseClient", parsed_url: "Optional[ParsedUrl]", From 36f8250af27c6f4c19907a1599b9b3b10645297e Mon Sep 17 00:00:00 2001 From: Pablo Deputter Date: Wed, 23 Sep 2026 15:41:04 +0200 Subject: [PATCH 4/4] add back comment --- sentry_sdk/tracing_utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index e6fd0f2861..a7d4bb84b0 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -195,7 +195,12 @@ def get_url_attributes_legacy( client: "sentry_sdk.client.BaseClient", parsed_url: "Optional[ParsedUrl]", ) -> "Attributes": - """Build outgoing HTTP URL attributes using legacy PII behavior.""" + """Build the `url.*` span attributes for an outgoing HTTP request. + + The query string is only included when the user has opted into collecting + it, either through `data_collection` (in which case the configured + filtering is applied) or through the legacy `send_default_pii`. + """ attributes: "Attributes" = {} if parsed_url is None: return attributes