Skip to content

Commit 0b6d943

Browse files
committed
feat(asgi): Add sentry.is_localhost for inbound filters
1 parent 842f573 commit 0b6d943

3 files changed

Lines changed: 86 additions & 1 deletion

File tree

‎sentry_sdk/integrations/asgi.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
capture_internal_exceptions,
4949
event_from_exception,
5050
has_data_collection_enabled,
51+
is_localhost,
5152
logger,
5253
nullcontext,
5354
qualname_from_function,
@@ -232,6 +233,18 @@ async def _run_app(
232233
processor = partial(self.event_processor, asgi_scope=scope)
233234
sentry_scope.add_event_processor(processor)
234235

236+
headers = _get_headers(scope)
237+
server = scope.get("server")
238+
sentry_scope.set_attribute(
239+
SPANDATA.SENTRY_IS_LOCALHOST,
240+
is_localhost(
241+
client_ip=_get_ip(scope) if scope.get("client") else None,
242+
url_host=server[0] if server else None,
243+
host_header=headers.get("host"),
244+
forwarded_host_header=headers.get("x-forwarded-host"),
245+
),
246+
)
247+
235248
ty = scope["type"]
236249
(
237250
transaction_name,

‎sentry_sdk/integrations/wsgi.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ def __call__(
131131
SPANDATA.SENTRY_IS_LOCALHOST,
132132
is_localhost(
133133
client_ip=get_client_ip(environ),
134-
url_host=environ.get("SERVER_NAME"),
134+
url_host=environ.get("SERVER_NAME")
135+
if not environ.get("HTTP_HOST")
136+
else None,
135137
host_header=environ.get("HTTP_HOST"),
136138
forwarded_host_header=environ.get("HTTP_X_FORWARDED_HOST"),
137139
),

‎tests/integrations/asgi/test_asgi.py‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,3 +1490,73 @@ async def wrapped_app(scope, receive, send):
14901490
else:
14911491
assert "user.ip_address" not in server_span["attributes"]
14921492
assert "user.ip_address" not in child_span["attributes"]
1493+
1494+
1495+
@pytest.mark.parametrize(
1496+
"client_ip, server, host_header, is_localhost",
1497+
[
1498+
# Loopback IP
1499+
("127.0.0.1", ("example.com", 80), b"example.com", True),
1500+
# IPv6 loopback
1501+
("::1", ("example.com", 80), b"example.com", True),
1502+
# Localhost host header with non-local IP
1503+
("203.0.113.50", ("example.com", 80), b"localhost:8000", True),
1504+
# Server bound to localhost
1505+
("203.0.113.50", ("localhost", 8000), b"example.com", True),
1506+
# Non-local everything
1507+
("203.0.113.50", ("example.com", 80), b"example.com", False),
1508+
],
1509+
)
1510+
@pytest.mark.asyncio
1511+
async def test_is_localhost_attribute(
1512+
sentry_init,
1513+
capture_items,
1514+
client_ip,
1515+
server,
1516+
host_header,
1517+
is_localhost,
1518+
):
1519+
async def app(scope, receive, send):
1520+
if scope["type"] == "lifespan":
1521+
while True:
1522+
message = await receive()
1523+
if message["type"] == "lifespan.startup":
1524+
await send({"type": "lifespan.startup.complete"})
1525+
elif message["type"] == "lifespan.shutdown":
1526+
await send({"type": "lifespan.shutdown.complete"})
1527+
return
1528+
1529+
with sentry_sdk.traces.start_span(name="child-span"):
1530+
pass
1531+
1532+
await send(
1533+
{
1534+
"type": "http.response.start",
1535+
"status": 200,
1536+
"headers": [[b"content-type", b"text/plain"]],
1537+
}
1538+
)
1539+
await send({"type": "http.response.body", "body": b"Hello, world!"})
1540+
1541+
sentry_init(traces_sample_rate=1.0, trace_lifecycle="stream")
1542+
sentry_app = SentryAsgiMiddleware(app)
1543+
1544+
async def wrapped_app(scope, receive, send):
1545+
if scope["type"] != "lifespan":
1546+
scope["client"] = (client_ip, 0)
1547+
scope["server"] = server
1548+
scope["headers"] = [(k, v) for k, v in scope["headers"] if k != b"host"]
1549+
scope["headers"].append((b"host", host_header))
1550+
1551+
await sentry_app(scope, receive, send)
1552+
1553+
async with TestClient(wrapped_app) as client:
1554+
items = capture_items("span")
1555+
await client.get("/some_url")
1556+
1557+
sentry_sdk.flush()
1558+
1559+
child_span, server_span = [item.payload for item in items]
1560+
1561+
assert server_span["attributes"]["sentry.is_localhost"] is is_localhost
1562+
assert child_span["attributes"]["sentry.is_localhost"] is is_localhost

0 commit comments

Comments
 (0)