Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion sentry_sdk/integrations/_asgi_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,5 @@ def _get_request_attributes(asgi_scope: "Any") -> "dict[str, Any]":
if client and should_send_default_pii():
ip = _get_ip(asgi_scope)
attributes["client.address"] = ip
attributes["user.ip_address"] = ip
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was moved to asgi.py


return attributes
14 changes: 6 additions & 8 deletions sentry_sdk/integrations/aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,12 @@ async def sentry_app_handle(
else {}
)

client_address_attributes = (
{
"client.address": request.remote,
"user.ip_address": request.remote,
}
if should_send_default_pii() and request.remote
else {}
)
client_address_attributes = {}
if should_send_default_pii() and request.remote:
client_address_attributes["client.address"] = request.remote
scope.set_attribute(
SPANDATA.USER_IP_ADDRESS, request.remote
)

span_ctx = sentry_sdk.traces.start_span(
# If this name makes it to the UI, AIOHTTP's URL
Expand Down
10 changes: 8 additions & 2 deletions sentry_sdk/integrations/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@

import sentry_sdk
from sentry_sdk.api import continue_trace
from sentry_sdk.consts import OP
from sentry_sdk.consts import OP, SPANDATA
from sentry_sdk.integrations._asgi_common import (
_get_headers,
_get_ip,
_get_request_attributes,
_get_request_data,
_get_url,
Expand All @@ -23,7 +24,7 @@
DEFAULT_HTTP_METHODS_TO_CAPTURE,
nullcontext,
)
from sentry_sdk.scope import Scope
from sentry_sdk.scope import Scope, should_send_default_pii
from sentry_sdk.sessions import track_session
from sentry_sdk.traces import (
SOURCE_FOR_STYLE as SEGMENT_SOURCE_FOR_STYLE,
Expand Down Expand Up @@ -247,6 +248,11 @@ async def _run_app(
"network.protocol.name": ty,
}

if scope.get("client") and should_send_default_pii():
sentry_scope.set_attribute(
SPANDATA.USER_IP_ADDRESS, _get_ip(scope)
)

if ty in ("http", "websocket"):
if (
ty == "websocket"
Expand Down
4 changes: 3 additions & 1 deletion sentry_sdk/integrations/sanic.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ async def _context_enter(request: "Request") -> None:
sentry_sdk.traces.continue_trace(dict(request.headers))
scope.set_custom_sampling_context({"sanic_request": request})

if should_send_default_pii() and request.remote_addr:
scope.set_attribute(SPANDATA.USER_IP_ADDRESS, request.remote_addr)

span = sentry_sdk.traces.start_span(
# Unless the request results in a 404 error, the name and source
# will get overwritten in _set_transaction
Expand Down Expand Up @@ -375,7 +378,6 @@ def _get_request_attributes(request: "Request") -> "Dict[str, Any]":

if should_send_default_pii() and request.remote_addr:
attributes[SPANDATA.CLIENT_ADDRESS] = request.remote_addr
attributes[SPANDATA.USER_IP_ADDRESS] = request.remote_addr

return attributes

Expand Down
4 changes: 3 additions & 1 deletion sentry_sdk/integrations/tornado.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ def _handle_request_impl(self: "RequestHandler") -> "Generator[None, None, None]
sentry_sdk.traces.continue_trace(dict(headers))
scope.set_custom_sampling_context({"tornado_request": self.request})

if should_send_default_pii() and self.request.remote_ip:
scope.set_attribute(SPANDATA.USER_IP_ADDRESS, self.request.remote_ip)

span_ctx = sentry_sdk.traces.start_span(
name=_DEFAULT_ROOT_SPAN_NAME,
attributes={
Expand Down Expand Up @@ -204,7 +207,6 @@ def _get_request_attributes(request: "Any") -> "Dict[str, Any]":

if should_send_default_pii() and request.remote_ip:
attributes[SPANDATA.CLIENT_ADDRESS] = request.remote_ip
attributes[SPANDATA.USER_IP_ADDRESS] = request.remote_ip

with capture_internal_exceptions():
raw_data = _get_tornado_request_data(request)
Expand Down
10 changes: 8 additions & 2 deletions sentry_sdk/integrations/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import sentry_sdk
from sentry_sdk._werkzeug import _get_headers, get_host
from sentry_sdk.api import continue_trace
from sentry_sdk.consts import OP
from sentry_sdk.consts import OP, SPANDATA
from sentry_sdk.integrations._wsgi_common import (
DEFAULT_HTTP_METHODS_TO_CAPTURE,
_filter_headers,
Expand Down Expand Up @@ -134,6 +134,13 @@ def __call__(
)
Scope.set_custom_sampling_context({"wsgi_environ": environ})

if should_send_default_pii():
client_ip = get_client_ip(environ)
if client_ip:
scope.set_attribute(
SPANDATA.USER_IP_ADDRESS, client_ip
)

span_ctx = sentry_sdk.traces.start_span(
name=_DEFAULT_TRANSACTION_NAME,
attributes={
Expand Down Expand Up @@ -412,6 +419,5 @@ def _get_request_attributes(
client_ip = get_client_ip(environ)
if client_ip:
attributes["client.address"] = client_ip
attributes["user.ip_address"] = client_ip

return attributes
37 changes: 37 additions & 0 deletions tests/integrations/aiohttp/test_aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1583,3 +1583,40 @@ async def handler(request):
span_id=client_span["span_id"],
sampled=1,
)


@pytest.mark.asyncio
@pytest.mark.parametrize("send_default_pii", [True, False])
async def test_user_ip_address_on_all_spans(
sentry_init, aiohttp_client, capture_items, send_default_pii
):
sentry_init(
integrations=[AioHttpIntegration()],
traces_sample_rate=1.0,
send_default_pii=send_default_pii,
_experiments={"trace_lifecycle": "stream"},
)

async def hello(request):
with sentry_sdk.traces.start_span(name="child-span"):
pass
return web.Response(text="hello")

app = web.Application()
app.router.add_get("/", hello)

items = capture_items("span")

client = await aiohttp_client(app)
await client.get("/")

sentry_sdk.flush()

child_span, server_span, client_span = [item.payload for item in items]

if send_default_pii:
assert server_span["attributes"]["user.ip_address"] == "127.0.0.1"
assert child_span["attributes"]["user.ip_address"] == "127.0.0.1"
else:
assert "user.ip_address" not in server_span["attributes"]
assert "user.ip_address" not in child_span["attributes"]
56 changes: 56 additions & 0 deletions tests/integrations/asgi/test_asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -1002,3 +1002,59 @@ async def test_custom_transaction_name(
assert transaction_event["type"] == "transaction"
assert transaction_event["transaction"] == "foobar"
assert transaction_event["transaction_info"] == {"source": "custom"}


@pytest.mark.asyncio
@pytest.mark.parametrize("send_default_pii", [True, False])
async def test_user_ip_address_on_all_spans(
sentry_init,
capture_items,
send_default_pii,
):
async def app(scope, receive, send):
if scope["type"] == "lifespan":
while True:
message = await receive()
if message["type"] == "lifespan.startup":
await send({"type": "lifespan.startup.complete"})
elif message["type"] == "lifespan.shutdown":
await send({"type": "lifespan.shutdown.complete"})
return

with sentry_sdk.traces.start_span(name="child-span"):
pass

await send(
{
"type": "http.response.start",
"status": 200,
"headers": [[b"content-type", b"text/plain"]],
}
)
await send({"type": "http.response.body", "body": b"Hello, world!"})

sentry_init(
send_default_pii=send_default_pii,
traces_sample_rate=1.0,
_experiments={"trace_lifecycle": "stream"},
)
sentry_app = SentryAsgiMiddleware(app)

async def wrapped_app(scope, receive, send):
scope["client"] = ("127.0.0.1", 0)
await sentry_app(scope, receive, send)

async with TestClient(wrapped_app) as client:
items = capture_items("span")
await client.get("/some_url")

sentry_sdk.flush()

child_span, server_span = [item.payload for item in items]

if send_default_pii:
assert server_span["attributes"]["user.ip_address"] == "127.0.0.1"
assert child_span["attributes"]["user.ip_address"] == "127.0.0.1"
else:
assert "user.ip_address" not in server_span["attributes"]
assert "user.ip_address" not in child_span["attributes"]
44 changes: 44 additions & 0 deletions tests/integrations/sanic/test_sanic.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,3 +541,47 @@ def test_span_origin(sentry_init, app, capture_events, capture_items, span_strea
else:
(_, event) = events
assert event["contexts"]["trace"]["origin"] == "auto.http.sanic"


@pytest.mark.skipif(
not PERFORMANCE_SUPPORTED, reason="Performance not supported on this Sanic version"
)
@pytest.mark.parametrize("send_default_pii", [True, False])
def test_user_ip_address_on_all_spans(
sentry_init, app, capture_items, send_default_pii
):
app.config.FORWARDED_SECRET = "test"

@app.route("/child-span")
def child_span_handler(request):
with sentry_sdk.traces.start_span(name="child-span"):
pass
return response.text("ok")

sentry_init(
integrations=[SanicIntegration()],
default_integrations=False,
traces_sample_rate=1.0,
send_default_pii=send_default_pii,
_experiments={"trace_lifecycle": "stream"},
)

items = capture_items("span")

c = get_client(app)
with c as client:
client.get(
"/child-span",
headers={"Forwarded": "for=127.0.0.1;secret=test"},
)

sentry_sdk.flush()

child_span, server_span = [item.payload for item in items]

if send_default_pii:
assert server_span["attributes"]["user.ip_address"] == "127.0.0.1"
assert child_span["attributes"]["user.ip_address"] == "127.0.0.1"
else:
assert "user.ip_address" not in server_span["attributes"]
assert "user.ip_address" not in child_span["attributes"]
35 changes: 35 additions & 0 deletions tests/integrations/tornado/test_tornado.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ async def post(self):
return b"hello"


class ChildSpanHandler(RequestHandler):
def get(self):
with sentry_sdk.traces.start_span(name="child-span"):
pass
self.write("ok")


def test_basic(tornado_testcase, sentry_init, capture_events):
sentry_init(integrations=[TornadoIntegration()], send_default_pii=True)
events = capture_events()
Expand Down Expand Up @@ -527,3 +534,31 @@ def test_span_origin(
else:
(_, event) = events
assert event["contexts"]["trace"]["origin"] == "auto.http.tornado"


@pytest.mark.parametrize("send_default_pii", [True, False])
def test_user_ip_address_on_all_spans(
tornado_testcase, sentry_init, capture_items, send_default_pii
):
sentry_init(
integrations=[TornadoIntegration()],
traces_sample_rate=1.0,
send_default_pii=send_default_pii,
_experiments={"trace_lifecycle": "stream"},
)

items = capture_items("span")

client = tornado_testcase(Application([(r"/hi", ChildSpanHandler)]))
client.fetch("/hi")

sentry_sdk.flush()

child_span, server_span = [item.payload for item in items]

if send_default_pii:
assert server_span["attributes"]["user.ip_address"] == "127.0.0.1"
assert child_span["attributes"]["user.ip_address"] == "127.0.0.1"
else:
assert "user.ip_address" not in server_span["attributes"]
assert "user.ip_address" not in child_span["attributes"]
32 changes: 32 additions & 0 deletions tests/integrations/wsgi/test_wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -841,3 +841,35 @@ def app(environ, start_response):
)
def test_get_request_url_x_forwarded_proto(environ, use_x_forwarded_for, expected_url):
assert get_request_url(environ, use_x_forwarded_for) == expected_url


@pytest.mark.parametrize("send_default_pii", [True, False])
def test_user_ip_address_on_all_spans(sentry_init, capture_items, send_default_pii):
def dogpark(environ, start_response):
with sentry_sdk.traces.start_span(name="child-span"):
pass
start_response("200 OK", [])
return ["Go get the ball! Good dog!"]

sentry_init(
send_default_pii=send_default_pii,
traces_sample_rate=1.0,
_experiments={"trace_lifecycle": "stream"},
)
app = SentryWsgiMiddleware(dogpark)
client = Client(app)

items = capture_items("span")

client.get("/dogs/are/great/", environ_base={"REMOTE_ADDR": "127.0.0.1"})

sentry_sdk.flush()

child_span, server_span = [item.payload for item in items]

if send_default_pii:
assert server_span["attributes"]["user.ip_address"] == "127.0.0.1"
assert child_span["attributes"]["user.ip_address"] == "127.0.0.1"
else:
assert "user.ip_address" not in server_span["attributes"]
assert "user.ip_address" not in child_span["attributes"]
Loading