Skip to content

fix(pods): stop leaking other users' private DMs into admin sidebars#375

Merged
samxu01 merged 1 commit into
mainfrom
fix/pod-visibility-admin-leak
May 15, 2026
Merged

fix(pods): stop leaking other users' private DMs into admin sidebars#375
samxu01 merged 1 commit into
mainfrom
fix/pod-visibility-admin-leak

Conversation

@samxu01
Copy link
Copy Markdown
Contributor

@samxu01 samxu01 commented May 15, 2026

Summary

  • GET /api/pods and GET /api/pods/:type no longer bypass membership filter for admins on the default sidebar listing
  • GET /api/pods/:id now returns 404 for personal pod types (agent-room / agent-dm / agent-admin) when caller is not a member
  • ?scope=all becomes an explicit admin-only opt-in for moderation tooling; non-admins silently downgrade to scope=mine

Why

Reproduced as xcjsam (global admin) on app-dev: sidebar showed 82 pods, including sam-demo's 1:1 agent-rooms with Nova/Pixel/Cody. Clicking any of them looked like "Talk to" was navigating into a stranger's DM. Post fails with Not authorized to post in this pod (correct), so the user experience was "I clicked Talk-to and now I can't chat." The real bug was upstream: the pod listing should never have surfaced other users' personal DMs.

POST /api/agents/runtime/room itself was correct — it creates a fresh (agentUser, caller) room and the message route correctly gates posting on membership. The leak was purely in the read side.

Test plan

  • backend/__tests__/unit/controllers/podController.test.js — 17/17 passing (4 new cases: admin filter on default scope, scope=all admin pass-through, scope=all non-admin downgrade, getPodById 404)
  • After deploy: verify with xcjsam token that GET /api/pods returns only xcjsam's pods (not 82), and that GET /api/pods/<sam-demo's agent-room id> returns 404
  • Verify "Talk to" still creates a fresh room and navigates correctly (it already did; just confirm the sidebar no longer surfaces the wrong destination)

`getAllPods` and `getPodsByType` were bypassing the membership filter
for global admins. The intent was a moderation view; the effect was
that admins (e.g. the xcjsam dev account) saw every agent-room and
agent-dm in the instance in their default sidebar — including
1:1 DMs other users had with their installed agents. Clicking one
of those rows looked like "Talk to" was navigating to a stranger's
DM, and posting failed with "Not authorized to post in this pod"
because the pod-message route correctly checks membership.

Three changes:

1. `getAllPods`: default `scope=mine` now filters to caller membership
   for everyone, admins included. `?scope=all` is a separate admin-
   only opt-in for moderation tooling; non-admins are silently
   downgraded to `scope=mine`.

2. `getPodsByType`: personal pod types (agent-room, agent-dm,
   agent-admin) are always filtered to caller membership — the
   instance-wide audit is a separate admin tool, not this generic
   listing.

3. `getPodById`: add a membership gate for personal pod types and
   404 non-members. The route previously returned the full pod
   payload to anyone authenticated, which leaked existence and
   membership of every private DM by direct ID lookup.

Unit tests rewritten to encode the corrected invariant + cover the
new `scope=all` admin path and the `getPodById` 404 case.
@samxu01 samxu01 merged commit 7d0ac2d into main May 15, 2026
8 of 9 checks passed
lilyshen0722 added a commit that referenced this pull request May 15, 2026
Adds a new bullet under Agent Runtime — Quick Rules covering the
membership-by-default gate on the sidebar / listing / direct-ID
surfaces (admins do NOT bypass; ?scope=all is the admin opt-in)
and the canViewPod gate that pod-scoped read endpoints must call
before returning content.

Mirrors the pod-manager skill update in commonly-skills@e2ae064.
samxu01 added a commit that referenced this pull request May 16, 2026
PR #375 made getPodById 404 non-members of all personal pod types to
prevent admins accidentally landing in other users' DMs via the sidebar.
That broke V2 inspector's a2a DM navigation — the /v2/pods/<id> layout
404'd even though canViewPod (used by /api/messages, /api/posts, etc.)
allows §3.7 fan-out viewers.

Carve-out:
- agent-dm: defers to canViewPod (admins + §3.7 fan-out)
- agent-room: strict membership (1:1 user↔agent)
- agent-admin: strict membership (ops surface)

Frontend already renders an isReadOnly banner in place of the composer
for non-member viewers. 20/20 podController tests pass (3 new).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant