fix(security): scope send_to_session to an exact session owner

send_to_session let an authenticated caller reach a null-owner session.
The owner gate was `if owner and sess.owner and sess.owner != owner`, so a
target whose owner is None (legacy rows, or a session created while auth
was off) skipped the check and was read/written by any authenticated user.
list_sessions (get_sessions_for_user) and manage_session already exclude
null-owner sessions from an authenticated caller via an exact owner match,
so this path was the lone inconsistency — the same class of gap the
calendar owner=None fix closed.

Require an exact owner match: `if owner and sess.owner != owner`. Auth-off
(no owner) is unchanged, an exact-owner match still passes, and both
another user's session and a null-owner session are now not-found. Adds a
regression test that an authenticated caller cannot read the transcript of
or write into a null-owner session while single-user access still works.
This commit is contained in:
ashvinctrl
2026-07-04 14:13:04 +05:30
parent 1f6dc80525
commit 43ead1a0eb
2 changed files with 55 additions and 2 deletions
+6 -2
View File
@@ -184,8 +184,12 @@ async def send_to_session(content: str, session_id: Optional[str] = None, owner:
if not sess:
return {"error": f"Session '{target_sid}' not found"}
# Owner-scope: reject access to another user's session
if owner and getattr(sess, "owner", None) and sess.owner != owner:
# Owner-scope: reject access to another user's session. When the caller is
# authenticated, a null-owner (legacy / auth-was-off) session is not theirs
# either — list_sessions (get_sessions_for_user) and manage_session already
# exclude those, so treating it as reachable here let an authenticated agent
# read/write a session the other tools hide. Require an exact owner match.
if owner and getattr(sess, "owner", None) != owner:
return {"error": f"Session '{target_sid}' not found"}
if not message: