Skip to content

Commit d991be7

Browse files
authored
Wrap parse_url calls in capture_internal_exceptions (#2162)
1 parent f4c19e1 commit d991be7

File tree

8 files changed

+274
-37
lines changed

8 files changed

+274
-37
lines changed

sentry_sdk/integrations/boto3.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from sentry_sdk._functools import partial
99
from sentry_sdk._types import TYPE_CHECKING
10-
from sentry_sdk.utils import parse_url, parse_version
10+
from sentry_sdk.utils import capture_internal_exceptions, parse_url, parse_version
1111

1212
if TYPE_CHECKING:
1313
from typing import Any
@@ -71,13 +71,14 @@ def _sentry_request_created(service_id, request, operation_name, **kwargs):
7171
description=description,
7272
)
7373

74-
parsed_url = parse_url(request.url, sanitize=False)
74+
with capture_internal_exceptions():
75+
parsed_url = parse_url(request.url, sanitize=False)
76+
span.set_data("aws.request.url", parsed_url.url)
77+
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
78+
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
7579

7680
span.set_tag("aws.service_id", service_id)
7781
span.set_tag("aws.operation_name", operation_name)
78-
span.set_data("aws.request.url", parsed_url.url)
79-
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
80-
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
8182
span.set_data(SPANDATA.HTTP_METHOD, request.method)
8283

8384
# We do it in order for subsequent http calls/retries be

sentry_sdk/integrations/httpx.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
from sentry_sdk.consts import OP, SPANDATA
33
from sentry_sdk.integrations import Integration, DidNotEnable
44
from sentry_sdk.tracing_utils import should_propagate_trace
5-
from sentry_sdk.utils import logger, parse_url
5+
from sentry_sdk.utils import (
6+
SENSITIVE_DATA_SUBSTITUTE,
7+
capture_internal_exceptions,
8+
logger,
9+
parse_url,
10+
)
611

712
from sentry_sdk._types import TYPE_CHECKING
813

@@ -42,16 +47,23 @@ def send(self, request, **kwargs):
4247
if hub.get_integration(HttpxIntegration) is None:
4348
return real_send(self, request, **kwargs)
4449

45-
parsed_url = parse_url(str(request.url), sanitize=False)
50+
parsed_url = None
51+
with capture_internal_exceptions():
52+
parsed_url = parse_url(str(request.url), sanitize=False)
4653

4754
with hub.start_span(
4855
op=OP.HTTP_CLIENT,
49-
description="%s %s" % (request.method, parsed_url.url),
56+
description="%s %s"
57+
% (
58+
request.method,
59+
parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE,
60+
),
5061
) as span:
5162
span.set_data(SPANDATA.HTTP_METHOD, request.method)
52-
span.set_data("url", parsed_url.url)
53-
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
54-
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
63+
if parsed_url is not None:
64+
span.set_data("url", parsed_url.url)
65+
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
66+
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
5567

5668
if should_propagate_trace(hub, str(request.url)):
5769
for key, value in hub.iter_trace_propagation_headers():
@@ -82,16 +94,23 @@ async def send(self, request, **kwargs):
8294
if hub.get_integration(HttpxIntegration) is None:
8395
return await real_send(self, request, **kwargs)
8496

85-
parsed_url = parse_url(str(request.url), sanitize=False)
97+
parsed_url = None
98+
with capture_internal_exceptions():
99+
parsed_url = parse_url(str(request.url), sanitize=False)
86100

87101
with hub.start_span(
88102
op=OP.HTTP_CLIENT,
89-
description="%s %s" % (request.method, parsed_url.url),
103+
description="%s %s"
104+
% (
105+
request.method,
106+
parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE,
107+
),
90108
) as span:
91109
span.set_data(SPANDATA.HTTP_METHOD, request.method)
92-
span.set_data("url", parsed_url.url)
93-
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
94-
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
110+
if parsed_url is not None:
111+
span.set_data("url", parsed_url.url)
112+
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
113+
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
95114

96115
if should_propagate_trace(hub, str(request.url)):
97116
for key, value in hub.iter_trace_propagation_headers():

sentry_sdk/integrations/stdlib.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from sentry_sdk.scope import add_global_event_processor
1010
from sentry_sdk.tracing_utils import EnvironHeaders, should_propagate_trace
1111
from sentry_sdk.utils import (
12+
SENSITIVE_DATA_SUBSTITUTE,
1213
capture_internal_exceptions,
1314
logger,
1415
safe_repr,
@@ -84,17 +85,21 @@ def putrequest(self, method, url, *args, **kwargs):
8485
url,
8586
)
8687

87-
parsed_url = parse_url(real_url, sanitize=False)
88+
parsed_url = None
89+
with capture_internal_exceptions():
90+
parsed_url = parse_url(real_url, sanitize=False)
8891

8992
span = hub.start_span(
9093
op=OP.HTTP_CLIENT,
91-
description="%s %s" % (method, parsed_url.url),
94+
description="%s %s"
95+
% (method, parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE),
9296
)
9397

9498
span.set_data(SPANDATA.HTTP_METHOD, method)
95-
span.set_data("url", parsed_url.url)
96-
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
97-
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
99+
if parsed_url is not None:
100+
span.set_data("url", parsed_url.url)
101+
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
102+
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
98103

99104
rv = real_putrequest(self, method, url, *args, **kwargs)
100105

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def _capture_internal_exception(self, exc_info):
6969

7070
@request.addfinalizer
7171
def _():
72-
# rerasise the errors so that this just acts as a pass-through (that
72+
# reraise the errors so that this just acts as a pass-through (that
7373
# happens to keep track of the errors which pass through it)
7474
for e in errors:
7575
reraise(*e)

tests/integrations/boto3/test_s3.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1+
import pytest
2+
3+
import boto3
4+
15
from sentry_sdk import Hub
26
from sentry_sdk.integrations.boto3 import Boto3Integration
37
from tests.integrations.boto3.aws_mock import MockResponse
48
from tests.integrations.boto3 import read_fixture
59

6-
import boto3
10+
try:
11+
from unittest import mock # python 3.3 and above
12+
except ImportError:
13+
import mock # python < 3.3
14+
715

816
session = boto3.Session(
917
aws_access_key_id="-",
@@ -53,9 +61,17 @@ def test_streaming(sentry_init, capture_events):
5361
(event,) = events
5462
assert event["type"] == "transaction"
5563
assert len(event["spans"]) == 2
64+
5665
span1 = event["spans"][0]
5766
assert span1["op"] == "http.client"
5867
assert span1["description"] == "aws.s3.GetObject"
68+
assert span1["data"] == {
69+
"http.method": "GET",
70+
"aws.request.url": "https://bucket.s3.amazonaws.com/foo.pdf",
71+
"http.fragment": "",
72+
"http.query": "",
73+
}
74+
5975
span2 = event["spans"][1]
6076
assert span2["op"] == "http.client.stream"
6177
assert span2["description"] == "aws.s3.GetObject"
@@ -83,3 +99,31 @@ def test_streaming_close(sentry_init, capture_events):
8399
assert span1["op"] == "http.client"
84100
span2 = event["spans"][1]
85101
assert span2["op"] == "http.client.stream"
102+
103+
104+
@pytest.mark.tests_internal_exceptions
105+
def test_omit_url_data_if_parsing_fails(sentry_init, capture_events):
106+
sentry_init(traces_sample_rate=1.0, integrations=[Boto3Integration()])
107+
events = capture_events()
108+
109+
s3 = session.resource("s3")
110+
111+
with mock.patch(
112+
"sentry_sdk.integrations.boto3.parse_url",
113+
side_effect=ValueError,
114+
):
115+
with Hub.current.start_transaction() as transaction, MockResponse(
116+
s3.meta.client, 200, {}, read_fixture("s3_list.xml")
117+
):
118+
bucket = s3.Bucket("bucket")
119+
items = [obj for obj in bucket.objects.all()]
120+
assert len(items) == 2
121+
assert items[0].key == "foo.txt"
122+
assert items[1].key == "bar.txt"
123+
transaction.finish()
124+
125+
(event,) = events
126+
assert event["spans"][0]["data"] == {
127+
"http.method": "GET",
128+
# no url data
129+
}

tests/integrations/httpx/test_httpx.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
from sentry_sdk.consts import MATCH_ALL, SPANDATA
99
from sentry_sdk.integrations.httpx import HttpxIntegration
1010

11+
try:
12+
from unittest import mock # python 3.3 and above
13+
except ImportError:
14+
import mock # python < 3.3
15+
1116

1217
@pytest.mark.parametrize(
1318
"httpx_client",
@@ -225,3 +230,30 @@ def test_option_trace_propagation_targets(
225230
assert "sentry-trace" in request_headers
226231
else:
227232
assert "sentry-trace" not in request_headers
233+
234+
235+
@pytest.mark.tests_internal_exceptions
236+
def test_omit_url_data_if_parsing_fails(sentry_init, capture_events):
237+
sentry_init(integrations=[HttpxIntegration()])
238+
239+
httpx_client = httpx.Client()
240+
url = "http://example.com"
241+
responses.add(responses.GET, url, status=200)
242+
243+
events = capture_events()
244+
with mock.patch(
245+
"sentry_sdk.integrations.httpx.parse_url",
246+
side_effect=ValueError,
247+
):
248+
response = httpx_client.get(url)
249+
250+
assert response.status_code == 200
251+
capture_message("Testing!")
252+
253+
(event,) = events
254+
assert event["breadcrumbs"]["values"][0]["data"] == {
255+
SPANDATA.HTTP_METHOD: "GET",
256+
SPANDATA.HTTP_STATUS_CODE: 200,
257+
"reason": "OK",
258+
# no url related data
259+
}

tests/integrations/requests/test_requests.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
from sentry_sdk.consts import SPANDATA
88
from sentry_sdk.integrations.stdlib import StdlibIntegration
99

10+
try:
11+
from unittest import mock # python 3.3 and above
12+
except ImportError:
13+
import mock # python < 3.3
14+
1015

1116
def test_crumb_capture(sentry_init, capture_events):
1217
sentry_init(integrations=[StdlibIntegration()])
@@ -31,3 +36,29 @@ def test_crumb_capture(sentry_init, capture_events):
3136
SPANDATA.HTTP_STATUS_CODE: response.status_code,
3237
"reason": response.reason,
3338
}
39+
40+
41+
@pytest.mark.tests_internal_exceptions
42+
def test_omit_url_data_if_parsing_fails(sentry_init, capture_events):
43+
sentry_init(integrations=[StdlibIntegration()])
44+
45+
url = "https://example.com"
46+
responses.add(responses.GET, url, status=200)
47+
48+
events = capture_events()
49+
50+
with mock.patch(
51+
"sentry_sdk.integrations.stdlib.parse_url",
52+
side_effect=ValueError,
53+
):
54+
response = requests.get(url)
55+
56+
capture_message("Testing!")
57+
58+
(event,) = events
59+
assert event["breadcrumbs"]["values"][0]["data"] == {
60+
SPANDATA.HTTP_METHOD: "GET",
61+
SPANDATA.HTTP_STATUS_CODE: response.status_code,
62+
"reason": response.reason,
63+
# no url related data
64+
}

0 commit comments

Comments
 (0)