Skip to content

Commit 36fefd3

Browse files
committed
fix: match Sentry URLs by parsed hostname
1 parent 7ad4e1a commit 36fefd3

3 files changed

Lines changed: 75 additions & 14 deletions

File tree

‎sentry_sdk/utils.py‎

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1782,14 +1782,31 @@ def match_regex_list(
17821782

17831783
def is_sentry_url(client: "sentry_sdk.client.BaseClient", url: str) -> bool:
17841784
"""
1785-
Determines whether the given URL matches the Sentry DSN.
1785+
Determines whether the given URL's hostname matches the Sentry DSN.
1786+
1787+
``url`` may be an absolute URL or a raw host with an optional port.
17861788
"""
1787-
return (
1788-
client is not None
1789-
and client.transport is not None
1790-
and client.transport.parsed_dsn is not None
1791-
and client.transport.parsed_dsn.netloc in url
1792-
)
1789+
if (
1790+
client is None
1791+
or client.transport is None
1792+
or client.transport.parsed_dsn is None
1793+
):
1794+
return False
1795+
1796+
dsn_host = client.transport.parsed_dsn.host
1797+
1798+
# ``HTTPConnection.host`` is a raw hostname without brackets for IPv6.
1799+
if url.lower() == dsn_host.lower():
1800+
return True
1801+
1802+
candidate = url if "://" in url or url.startswith("//") else "//" + url
1803+
1804+
try:
1805+
hostname = urlsplit(candidate).hostname
1806+
except ValueError:
1807+
return False
1808+
1809+
return hostname is not None and hostname.lower() == dsn_host.lower()
17931810

17941811

17951812
def _generate_installed_modules() -> "Iterator[Tuple[str, str]]":

‎tests/test_utils.py‎

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -869,15 +869,15 @@ class TestIntegration(Integration):
869869

870870

871871
@pytest.fixture
872-
def mock_client_with_dsn_netloc():
872+
def mock_client_with_dsn():
873873
"""
874-
Returns a mocked Client with a DSN netloc of "abcd1234.ingest.sentry.io".
874+
Returns a mocked Client with a DSN host of "abcd1234.ingest.sentry.io".
875875
"""
876876
mock_client = mock.Mock(spec=sentry_sdk.Client)
877877
mock_client.transport = mock.Mock(spec=sentry_sdk.Transport)
878878
mock_client.transport.parsed_dsn = mock.Mock(spec=Dsn)
879879

880-
mock_client.transport.parsed_dsn.netloc = "abcd1234.ingest.sentry.io"
880+
mock_client.transport.parsed_dsn.host = "abcd1234.ingest.sentry.io"
881881

882882
return mock_client
883883

@@ -886,13 +886,37 @@ def mock_client_with_dsn_netloc():
886886
["test_url", "is_sentry_url_expected"],
887887
[
888888
["https://asdf@abcd1234.ingest.sentry.io/123456789", True],
889+
["HTTP://ABCD1234.INGEST.SENTRY.IO:8443/envelope", True],
890+
["abcd1234.ingest.sentry.io", True],
891+
["abcd1234.ingest.sentry.io:9000", True],
889892
["https://asdf@abcd1234.ingest.notsentry.io/123456789", False],
893+
["https://abcd1234.ingest.sentry.io.evil.test/api/1", False],
894+
["https://abcd1234.ingest.sentry.io@attacker.test/api/1", False],
895+
["https://attacker.test/abcd1234.ingest.sentry.io", False],
896+
["https://attacker.test/?next=abcd1234.ingest.sentry.io", False],
897+
["abcd1234.ingest.sentry.io.evil.test", False],
898+
["https://[::1", False],
890899
],
891900
)
892-
def test_is_sentry_url_true(
893-
test_url, is_sentry_url_expected, mock_client_with_dsn_netloc
894-
):
895-
ret_val = is_sentry_url(mock_client_with_dsn_netloc, test_url)
901+
def test_is_sentry_url(test_url, is_sentry_url_expected, mock_client_with_dsn):
902+
ret_val = is_sentry_url(mock_client_with_dsn, test_url)
903+
904+
assert ret_val == is_sentry_url_expected
905+
906+
907+
@pytest.mark.parametrize(
908+
["test_url", "is_sentry_url_expected"],
909+
[
910+
["2001:db8::1", True],
911+
["[2001:db8::1]:9000", True],
912+
["HTTP://[2001:DB8::1]:9000/envelope", True],
913+
["http://[2001:db8::2]:9000/envelope", False],
914+
],
915+
)
916+
def test_is_sentry_url_ipv6(test_url, is_sentry_url_expected, mock_client_with_dsn):
917+
mock_client_with_dsn.transport.parsed_dsn.host = "2001:db8::1"
918+
919+
ret_val = is_sentry_url(mock_client_with_dsn, test_url)
896920

897921
assert ret_val == is_sentry_url_expected
898922

‎tests/tracing/test_misc.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,26 @@ def test_should_propagate_trace(
393393
"http://squirrelchasers.ingest.sentry.io/12312012",
394394
False,
395395
),
396+
(
397+
"https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012",
398+
"https://SQUIRRELCHASERS.INGEST.SENTRY.IO/12312012",
399+
False,
400+
),
401+
(
402+
"https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012",
403+
"https://squirrelchasers.ingest.sentry.io.evil.test/12312012",
404+
True,
405+
),
406+
(
407+
"https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012",
408+
"https://squirrelchasers.ingest.sentry.io@attacker.test/12312012",
409+
True,
410+
),
411+
(
412+
"https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012",
413+
"https://attacker.test/?next=squirrelchasers.ingest.sentry.io",
414+
True,
415+
),
396416
(
397417
"https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012",
398418
"http://ingest.sentry.io/12312012",

0 commit comments

Comments
 (0)