Skip to content

Commit c13cb24

Browse files
ericapisaniclaude
andcommitted
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 <noreply@anthropic.com>
1 parent 7787e6d commit c13cb24

2 files changed

Lines changed: 38 additions & 7 deletions

File tree

sentry_sdk/integrations/pyreqwest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import Any, Generator
33

44
import sentry_sdk
5+
from build.lib.sentry_sdk.scope import should_send_default_pii
56
from sentry_sdk import start_span
67
from sentry_sdk.consts import OP, SPANDATA
78
from sentry_sdk.integrations import DidNotEnable, Integration
@@ -95,7 +96,7 @@ def _sentry_pyreqwest_span(request: "Request") -> "Generator[Any, None, None]":
9596
SPANDATA.HTTP_REQUEST_METHOD: request.method,
9697
},
9798
) as span:
98-
if parsed_url is not None:
99+
if parsed_url is not None and should_send_default_pii():
99100
span.set_attribute(SPANDATA.URL_FULL, parsed_url.url)
100101
span.set_attribute(SPANDATA.URL_QUERY, parsed_url.query)
101102
span.set_attribute(SPANDATA.URL_FRAGMENT, parsed_url.fragment)

tests/integrations/pyreqwest/test_pyreqwest.py

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,20 @@ def clear_captured_requests():
5959
PyreqwestMockHandler.captured_requests.clear()
6060

6161

62+
@pytest.mark.parametrize("send_default_pii", [True, False])
6263
@pytest.mark.parametrize("span_streaming", [True, False])
6364
def test_sync_client_spans(
6465
sentry_init,
6566
capture_events,
6667
capture_items,
6768
server_port,
6869
span_streaming,
70+
send_default_pii,
6971
):
7072
sentry_init(
7173
integrations=[PyreqwestIntegration()],
7274
traces_sample_rate=1.0,
75+
send_default_pii=send_default_pii,
7376
_experiments={"trace_lifecycle": "stream" if span_streaming else "static"},
7477
)
7578

@@ -88,12 +91,18 @@ def test_sync_client_spans(
8891
span = spans[0]
8992
assert span["attributes"]["sentry.op"] == "http.client"
9093
assert span["name"] == f"GET http://localhost:{server_port}/hello"
91-
assert span["attributes"]["url.full"] == f"http://localhost:{server_port}/hello"
9294
assert span["attributes"][SPANDATA.HTTP_REQUEST_METHOD] == "GET"
9395
assert span["attributes"][SPANDATA.HTTP_STATUS_CODE] == 200
94-
assert span["attributes"][SPANDATA.URL_QUERY] == "q=test"
95-
assert span["attributes"][SPANDATA.URL_FRAGMENT] == "frag"
9696
assert span["attributes"]["sentry.origin"] == "auto.http.pyreqwest"
97+
98+
if send_default_pii:
99+
assert span["attributes"]["url.full"] == f"http://localhost:{server_port}/hello"
100+
assert span["attributes"][SPANDATA.URL_QUERY] == "q=test"
101+
assert span["attributes"][SPANDATA.URL_FRAGMENT] == "frag"
102+
else:
103+
assert "url.full" not in span["attributes"]
104+
assert SPANDATA.URL_QUERY not in span["attributes"]
105+
assert SPANDATA.URL_FRAGMENT not in span["attributes"]
97106
else:
98107
events = capture_events()
99108

@@ -116,17 +125,20 @@ def test_sync_client_spans(
116125

117126

118127
@pytest.mark.asyncio
128+
@pytest.mark.parametrize("send_default_pii", [True, False])
119129
@pytest.mark.parametrize("span_streaming", [True, False])
120130
async def test_async_client_spans(
121131
sentry_init,
122132
capture_events,
123133
capture_items,
124134
server_port,
125135
span_streaming,
136+
send_default_pii,
126137
):
127138
sentry_init(
128139
integrations=[PyreqwestIntegration()],
129140
traces_sample_rate=1.0,
141+
send_default_pii=send_default_pii,
130142
_experiments={"trace_lifecycle": "stream" if span_streaming else "static"},
131143
)
132144

@@ -145,10 +157,14 @@ async def test_async_client_spans(
145157
span = spans[0]
146158
assert span["attributes"]["sentry.op"] == "http.client"
147159
assert span["name"] == f"GET {url}"
148-
assert span["attributes"]["url.full"] == url
149160
assert span["attributes"][SPANDATA.HTTP_REQUEST_METHOD] == "GET"
150161
assert span["attributes"][SPANDATA.HTTP_STATUS_CODE] == 200
151162
assert span["attributes"]["sentry.origin"] == "auto.http.pyreqwest"
163+
164+
if send_default_pii:
165+
assert span["attributes"]["url.full"] == url
166+
else:
167+
assert "url.full" not in span["attributes"]
152168
else:
153169
events = capture_events()
154170

@@ -168,17 +184,20 @@ async def test_async_client_spans(
168184
assert span["origin"] == "auto.http.pyreqwest"
169185

170186

187+
@pytest.mark.parametrize("send_default_pii", [True, False])
171188
@pytest.mark.parametrize("span_streaming", [True, False])
172189
def test_sync_simple_request_spans(
173190
sentry_init,
174191
capture_events,
175192
capture_items,
176193
server_port,
177194
span_streaming,
195+
send_default_pii,
178196
):
179197
sentry_init(
180198
integrations=[PyreqwestIntegration()],
181199
traces_sample_rate=1.0,
200+
send_default_pii=send_default_pii,
182201
_experiments={"trace_lifecycle": "stream" if span_streaming else "static"},
183202
)
184203

@@ -196,10 +215,14 @@ def test_sync_simple_request_spans(
196215
span = spans[0]
197216
assert span["attributes"]["sentry.op"] == "http.client"
198217
assert span["name"] == f"GET {url}"
199-
assert span["attributes"]["url.full"] == url
200218
assert span["attributes"][SPANDATA.HTTP_REQUEST_METHOD] == "GET"
201219
assert span["attributes"][SPANDATA.HTTP_STATUS_CODE] == 200
202220
assert span["attributes"]["sentry.origin"] == "auto.http.pyreqwest"
221+
222+
if send_default_pii:
223+
assert span["attributes"]["url.full"] == url
224+
else:
225+
assert "url.full" not in span["attributes"]
203226
else:
204227
events = capture_events()
205228

@@ -219,17 +242,20 @@ def test_sync_simple_request_spans(
219242

220243

221244
@pytest.mark.asyncio
245+
@pytest.mark.parametrize("send_default_pii", [True, False])
222246
@pytest.mark.parametrize("span_streaming", [True, False])
223247
async def test_async_simple_request_spans(
224248
sentry_init,
225249
capture_events,
226250
capture_items,
227251
server_port,
228252
span_streaming,
253+
send_default_pii,
229254
):
230255
sentry_init(
231256
integrations=[PyreqwestIntegration()],
232257
traces_sample_rate=1.0,
258+
send_default_pii=send_default_pii,
233259
_experiments={"trace_lifecycle": "stream" if span_streaming else "static"},
234260
)
235261

@@ -247,10 +273,14 @@ async def test_async_simple_request_spans(
247273
span = spans[0]
248274
assert span["attributes"]["sentry.op"] == "http.client"
249275
assert span["name"] == f"GET {url}"
250-
assert span["attributes"]["url.full"] == url
251276
assert span["attributes"][SPANDATA.HTTP_REQUEST_METHOD] == "GET"
252277
assert span["attributes"][SPANDATA.HTTP_STATUS_CODE] == 200
253278
assert span["attributes"]["sentry.origin"] == "auto.http.pyreqwest"
279+
280+
if send_default_pii:
281+
assert span["attributes"]["url.full"] == url
282+
else:
283+
assert "url.full" not in span["attributes"]
254284
else:
255285
events = capture_events()
256286

0 commit comments

Comments
 (0)