Skip to content

Commit d1e6d1a

Browse files
grichaclaude
andcommitted
fix(hybridcloud): Always set ViewerContext in RPC endpoint
The RPC endpoint ran inside ViewerContextMiddleware, which sets a ViewerContext for the transport request itself. When no viewer_context was provided in meta, nullcontext() left the middleware's context active, leaking incorrect identity to the RPC handler. Always enter viewer_context_scope with either the caller's deserialized context or an empty ViewerContext(actor_type=UNKNOWN) to override the middleware's context. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1c4f18a commit d1e6d1a

File tree

2 files changed

+10
-8
lines changed

2 files changed

+10
-8
lines changed

src/sentry/api/endpoints/internal/rpc.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import contextlib
2-
31
import pydantic
42
import sentry_sdk
53
from rest_framework import status
@@ -66,16 +64,16 @@ def post(self, request: Request, service_name: str, method_name: str) -> Respons
6664

6765
meta = request.data.get("meta") or {}
6866
vc_data = meta.get("viewer_context")
69-
vc_scope: contextlib.AbstractContextManager[None] = contextlib.nullcontext()
67+
vc = ViewerContext()
7068
if vc_data:
7169
try:
72-
vc_scope = viewer_context_scope(ViewerContext.deserialize(vc_data))
70+
vc = ViewerContext.deserialize(vc_data)
7371
except Exception as e:
7472
sentry_sdk.capture_exception()
7573
raise ParseError from e
7674

7775
try:
78-
with vc_scope, auth_context.applied_to_request(request):
76+
with viewer_context_scope(vc), auth_context.applied_to_request(request):
7977
result = dispatch_to_local_service(service_name, method_name, arguments)
8078
except RpcValidationException as e:
8179
return Response(

tests/sentry/api/endpoints/test_rpc.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ def capturing_dispatch(*args, **kwargs):
184184
assert ctx.user_id == 42
185185
assert ctx.actor_type == ActorType.USER
186186

187-
def test_viewer_context_none_when_meta_empty(self) -> None:
188-
"""No ViewerContext set when meta is empty."""
187+
def test_viewer_context_unknown_when_meta_empty(self) -> None:
188+
"""Empty ViewerContext with UNKNOWN actor type when meta has no viewer_context."""
189189
organization = self.create_organization()
190190
captured_contexts: list[ViewerContext | None] = []
191191

@@ -208,7 +208,11 @@ def capturing_dispatch(*args, **kwargs):
208208

209209
assert response.status_code == 200
210210
assert len(captured_contexts) == 1
211-
assert captured_contexts[0] is None
211+
ctx = captured_contexts[0]
212+
assert ctx is not None
213+
assert ctx.user_id is None
214+
assert ctx.organization_id is None
215+
assert ctx.actor_type == ActorType.UNKNOWN
212216

213217
def test_viewer_context_roundtrip_through_meta(self) -> None:
214218
"""ViewerContext set on the sending side arrives on the receiving side."""

0 commit comments

Comments
 (0)