Skip to content

Commit 5a46e1f

Browse files
ericapisaniclaude
andcommitted
fix(httpx): Gate url.full, url.query, and url.fragment behind send_default_pii
These URL attributes can contain PII (query params, fragments), so they should only be set when send_default_pii is enabled, consistent with the aiohttp and wsgi integrations. Refs #2558 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 7787e6d commit 5a46e1f

2 files changed

Lines changed: 24 additions & 9 deletions

File tree

sentry_sdk/integrations/httpx.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import sentry_sdk
44
from sentry_sdk.consts import OP, SPANDATA
55
from sentry_sdk.integrations import DidNotEnable, Integration
6+
from sentry_sdk.scope import should_send_default_pii
67
from sentry_sdk.tracing import BAGGAGE_HEADER_NAME
78
from sentry_sdk.tracing_utils import (
89
add_http_request_source,
@@ -73,7 +74,7 @@ def send(self: "Client", request: "Request", **kwargs: "Any") -> "Response":
7374
) as streamed_span:
7475
attributes: "Attributes" = {}
7576

76-
if parsed_url is not None:
77+
if parsed_url is not None and should_send_default_pii():
7778
attributes["url.full"] = parsed_url.url
7879
if parsed_url.query:
7980
attributes["url.query"] = parsed_url.query
@@ -183,7 +184,7 @@ async def send(
183184
) as streamed_span:
184185
attributes: "Attributes" = {}
185186

186-
if parsed_url is not None:
187+
if parsed_url is not None and should_send_default_pii():
187188
attributes["url.full"] = parsed_url.url
188189
if parsed_url.query:
189190
attributes["url.query"] = parsed_url.query

tests/integrations/httpx/test_httpx.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,18 +1119,20 @@ def test_span_origin_span_streaming(
11191119
assert http_span["attributes"]["sentry.origin"] == "auto.http.httpx"
11201120

11211121

1122+
@pytest.mark.parametrize("send_default_pii", [True, False])
11221123
@pytest.mark.parametrize(
11231124
"httpx_client",
11241125
(httpx.Client(), httpx.AsyncClient()),
11251126
)
11261127
def test_http_url_attributes_span_streaming(
1127-
sentry_init, capture_items, httpx_client, httpx_mock
1128+
sentry_init, capture_items, httpx_client, httpx_mock, send_default_pii
11281129
):
11291130
httpx_mock.add_response()
11301131

11311132
sentry_init(
11321133
integrations=[HttpxIntegration()],
11331134
traces_sample_rate=1.0,
1135+
send_default_pii=send_default_pii,
11341136
_experiments={"trace_lifecycle": "stream"},
11351137
)
11361138

@@ -1148,24 +1150,32 @@ def test_http_url_attributes_span_streaming(
11481150
http_span = _get_http_client_span(items)
11491151

11501152
assert http_span["attributes"]["http.request.method"] == "GET"
1151-
assert http_span["attributes"]["url.full"] == "http://example.com/"
1152-
assert http_span["attributes"]["url.query"] == "foo=bar"
1153-
assert http_span["attributes"]["url.fragment"] == "frag"
11541153
assert http_span["attributes"]["http.response.status_code"] == 200
11551154

1155+
if send_default_pii:
1156+
assert http_span["attributes"]["url.full"] == "http://example.com/"
1157+
assert http_span["attributes"]["url.query"] == "foo=bar"
1158+
assert http_span["attributes"]["url.fragment"] == "frag"
1159+
else:
1160+
assert "url.full" not in http_span["attributes"]
1161+
assert "url.query" not in http_span["attributes"]
1162+
assert "url.fragment" not in http_span["attributes"]
1163+
11561164

1165+
@pytest.mark.parametrize("send_default_pii", [True, False])
11571166
@pytest.mark.parametrize(
11581167
"httpx_client",
11591168
(httpx.Client(), httpx.AsyncClient()),
11601169
)
11611170
def test_http_url_attributes_no_query_or_fragment_span_streaming(
1162-
sentry_init, capture_items, httpx_client, httpx_mock
1171+
sentry_init, capture_items, httpx_client, httpx_mock, send_default_pii
11631172
):
11641173
httpx_mock.add_response()
11651174

11661175
sentry_init(
11671176
integrations=[HttpxIntegration()],
11681177
traces_sample_rate=1.0,
1178+
send_default_pii=send_default_pii,
11691179
_experiments={"trace_lifecycle": "stream"},
11701180
)
11711181

@@ -1183,7 +1193,11 @@ def test_http_url_attributes_no_query_or_fragment_span_streaming(
11831193
http_span = _get_http_client_span(items)
11841194

11851195
assert http_span["attributes"]["http.request.method"] == "GET"
1186-
assert http_span["attributes"]["url.full"] == "http://example.com/"
1196+
assert http_span["attributes"]["http.response.status_code"] == 200
11871197
assert "url.query" not in http_span["attributes"]
11881198
assert "url.fragment" not in http_span["attributes"]
1189-
assert http_span["attributes"]["http.response.status_code"] == 200
1199+
1200+
if send_default_pii:
1201+
assert http_span["attributes"]["url.full"] == "http://example.com/"
1202+
else:
1203+
assert "url.full" not in http_span["attributes"]

0 commit comments

Comments
 (0)