From c13cb24f69853909c04adabfabecc17e4c003f3a Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Thu, 25 Jun 2026 14:59:14 -0400 Subject: [PATCH 1/3] fix(pyreqwest): Gate url.full, url.query, url.fragment on send_default_pii Gate URL attributes behind should_send_default_pii() in the streamed span path to avoid leaking sensitive URL data. Update tests to parametrize on send_default_pii and assert attributes are present only when PII sending is enabled. Co-Authored-By: Claude Opus 4.6 --- sentry_sdk/integrations/pyreqwest.py | 3 +- .../integrations/pyreqwest/test_pyreqwest.py | 42 ++++++++++++++++--- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/sentry_sdk/integrations/pyreqwest.py b/sentry_sdk/integrations/pyreqwest.py index f6b9172596..e99cae6cf9 100644 --- a/sentry_sdk/integrations/pyreqwest.py +++ b/sentry_sdk/integrations/pyreqwest.py @@ -2,6 +2,7 @@ from typing import Any, Generator import sentry_sdk +from build.lib.sentry_sdk.scope import should_send_default_pii from sentry_sdk import start_span from sentry_sdk.consts import OP, SPANDATA from sentry_sdk.integrations import DidNotEnable, Integration @@ -95,7 +96,7 @@ def _sentry_pyreqwest_span(request: "Request") -> "Generator[Any, None, None]": SPANDATA.HTTP_REQUEST_METHOD: request.method, }, ) as span: - if parsed_url is not None: + if parsed_url is not None and should_send_default_pii(): span.set_attribute(SPANDATA.URL_FULL, parsed_url.url) span.set_attribute(SPANDATA.URL_QUERY, parsed_url.query) span.set_attribute(SPANDATA.URL_FRAGMENT, parsed_url.fragment) diff --git a/tests/integrations/pyreqwest/test_pyreqwest.py b/tests/integrations/pyreqwest/test_pyreqwest.py index 2b53fa2e48..03f9207734 100644 --- a/tests/integrations/pyreqwest/test_pyreqwest.py +++ b/tests/integrations/pyreqwest/test_pyreqwest.py @@ -59,6 +59,7 @@ def clear_captured_requests(): PyreqwestMockHandler.captured_requests.clear() +@pytest.mark.parametrize("send_default_pii", [True, False]) @pytest.mark.parametrize("span_streaming", [True, False]) def test_sync_client_spans( sentry_init, @@ -66,10 +67,12 @@ def test_sync_client_spans( capture_items, server_port, span_streaming, + send_default_pii, ): sentry_init( integrations=[PyreqwestIntegration()], traces_sample_rate=1.0, + send_default_pii=send_default_pii, _experiments={"trace_lifecycle": "stream" if span_streaming else "static"}, ) @@ -88,12 +91,18 @@ def test_sync_client_spans( span = spans[0] assert span["attributes"]["sentry.op"] == "http.client" assert span["name"] == f"GET http://localhost:{server_port}/hello" - assert span["attributes"]["url.full"] == f"http://localhost:{server_port}/hello" assert span["attributes"][SPANDATA.HTTP_REQUEST_METHOD] == "GET" assert span["attributes"][SPANDATA.HTTP_STATUS_CODE] == 200 - assert span["attributes"][SPANDATA.URL_QUERY] == "q=test" - assert span["attributes"][SPANDATA.URL_FRAGMENT] == "frag" assert span["attributes"]["sentry.origin"] == "auto.http.pyreqwest" + + if send_default_pii: + assert span["attributes"]["url.full"] == f"http://localhost:{server_port}/hello" + assert span["attributes"][SPANDATA.URL_QUERY] == "q=test" + assert span["attributes"][SPANDATA.URL_FRAGMENT] == "frag" + else: + assert "url.full" not in span["attributes"] + assert SPANDATA.URL_QUERY not in span["attributes"] + assert SPANDATA.URL_FRAGMENT not in span["attributes"] else: events = capture_events() @@ -116,6 +125,7 @@ def test_sync_client_spans( @pytest.mark.asyncio +@pytest.mark.parametrize("send_default_pii", [True, False]) @pytest.mark.parametrize("span_streaming", [True, False]) async def test_async_client_spans( sentry_init, @@ -123,10 +133,12 @@ async def test_async_client_spans( capture_items, server_port, span_streaming, + send_default_pii, ): sentry_init( integrations=[PyreqwestIntegration()], traces_sample_rate=1.0, + send_default_pii=send_default_pii, _experiments={"trace_lifecycle": "stream" if span_streaming else "static"}, ) @@ -145,10 +157,14 @@ async def test_async_client_spans( span = spans[0] assert span["attributes"]["sentry.op"] == "http.client" assert span["name"] == f"GET {url}" - assert span["attributes"]["url.full"] == url assert span["attributes"][SPANDATA.HTTP_REQUEST_METHOD] == "GET" assert span["attributes"][SPANDATA.HTTP_STATUS_CODE] == 200 assert span["attributes"]["sentry.origin"] == "auto.http.pyreqwest" + + if send_default_pii: + assert span["attributes"]["url.full"] == url + else: + assert "url.full" not in span["attributes"] else: events = capture_events() @@ -168,6 +184,7 @@ async def test_async_client_spans( assert span["origin"] == "auto.http.pyreqwest" +@pytest.mark.parametrize("send_default_pii", [True, False]) @pytest.mark.parametrize("span_streaming", [True, False]) def test_sync_simple_request_spans( sentry_init, @@ -175,10 +192,12 @@ def test_sync_simple_request_spans( capture_items, server_port, span_streaming, + send_default_pii, ): sentry_init( integrations=[PyreqwestIntegration()], traces_sample_rate=1.0, + send_default_pii=send_default_pii, _experiments={"trace_lifecycle": "stream" if span_streaming else "static"}, ) @@ -196,10 +215,14 @@ def test_sync_simple_request_spans( span = spans[0] assert span["attributes"]["sentry.op"] == "http.client" assert span["name"] == f"GET {url}" - assert span["attributes"]["url.full"] == url assert span["attributes"][SPANDATA.HTTP_REQUEST_METHOD] == "GET" assert span["attributes"][SPANDATA.HTTP_STATUS_CODE] == 200 assert span["attributes"]["sentry.origin"] == "auto.http.pyreqwest" + + if send_default_pii: + assert span["attributes"]["url.full"] == url + else: + assert "url.full" not in span["attributes"] else: events = capture_events() @@ -219,6 +242,7 @@ def test_sync_simple_request_spans( @pytest.mark.asyncio +@pytest.mark.parametrize("send_default_pii", [True, False]) @pytest.mark.parametrize("span_streaming", [True, False]) async def test_async_simple_request_spans( sentry_init, @@ -226,10 +250,12 @@ async def test_async_simple_request_spans( capture_items, server_port, span_streaming, + send_default_pii, ): sentry_init( integrations=[PyreqwestIntegration()], traces_sample_rate=1.0, + send_default_pii=send_default_pii, _experiments={"trace_lifecycle": "stream" if span_streaming else "static"}, ) @@ -247,10 +273,14 @@ async def test_async_simple_request_spans( span = spans[0] assert span["attributes"]["sentry.op"] == "http.client" assert span["name"] == f"GET {url}" - assert span["attributes"]["url.full"] == url assert span["attributes"][SPANDATA.HTTP_REQUEST_METHOD] == "GET" assert span["attributes"][SPANDATA.HTTP_STATUS_CODE] == 200 assert span["attributes"]["sentry.origin"] == "auto.http.pyreqwest" + + if send_default_pii: + assert span["attributes"]["url.full"] == url + else: + assert "url.full" not in span["attributes"] else: events = capture_events() From a1d04732fb052624251a3fe2725396d698c982ae Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Thu, 25 Jun 2026 15:00:43 -0400 Subject: [PATCH 2/3] fix(pyreqwest): Fix should_send_default_pii import path Import from sentry_sdk.scope instead of build.lib.sentry_sdk.scope. Co-Authored-By: Claude Opus 4.6 --- sentry_sdk/integrations/pyreqwest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/pyreqwest.py b/sentry_sdk/integrations/pyreqwest.py index e99cae6cf9..826867ad5e 100644 --- a/sentry_sdk/integrations/pyreqwest.py +++ b/sentry_sdk/integrations/pyreqwest.py @@ -2,7 +2,7 @@ from typing import Any, Generator import sentry_sdk -from build.lib.sentry_sdk.scope import should_send_default_pii +from sentry_sdk.scope import should_send_default_pii from sentry_sdk import start_span from sentry_sdk.consts import OP, SPANDATA from sentry_sdk.integrations import DidNotEnable, Integration From b89059fd2c7ef7a7ff399f5658b534438d9bf0a4 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Thu, 25 Jun 2026 15:07:23 -0400 Subject: [PATCH 3/3] lint --- sentry_sdk/integrations/pyreqwest.py | 2 +- tests/integrations/pyreqwest/test_pyreqwest.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/integrations/pyreqwest.py b/sentry_sdk/integrations/pyreqwest.py index 826867ad5e..aae68c4c10 100644 --- a/sentry_sdk/integrations/pyreqwest.py +++ b/sentry_sdk/integrations/pyreqwest.py @@ -2,10 +2,10 @@ from typing import Any, Generator import sentry_sdk -from sentry_sdk.scope import should_send_default_pii from sentry_sdk import start_span from sentry_sdk.consts import OP, SPANDATA from sentry_sdk.integrations import DidNotEnable, Integration +from sentry_sdk.scope import should_send_default_pii from sentry_sdk.traces import StreamedSpan from sentry_sdk.tracing import BAGGAGE_HEADER_NAME from sentry_sdk.tracing_utils import ( diff --git a/tests/integrations/pyreqwest/test_pyreqwest.py b/tests/integrations/pyreqwest/test_pyreqwest.py index 03f9207734..d3c93c423d 100644 --- a/tests/integrations/pyreqwest/test_pyreqwest.py +++ b/tests/integrations/pyreqwest/test_pyreqwest.py @@ -96,7 +96,10 @@ def test_sync_client_spans( assert span["attributes"]["sentry.origin"] == "auto.http.pyreqwest" if send_default_pii: - assert span["attributes"]["url.full"] == f"http://localhost:{server_port}/hello" + assert ( + span["attributes"]["url.full"] + == f"http://localhost:{server_port}/hello" + ) assert span["attributes"][SPANDATA.URL_QUERY] == "q=test" assert span["attributes"][SPANDATA.URL_FRAGMENT] == "frag" else: