Flagged by the independent audit of the #138 re-cut (2026-07-03). Non-blocking there; filing as the agreed follow-up.
The list route GET /api/harnesses/[id]/surfaces/[platform]/admins passes the auth middleware unauthenticated (GETs are intentionally ungated for the plugin's boolean hot paths). But unlike the boolean routes, it returns the full admin identity list — Signal phone numbers / UUIDs, Telegram ids — and accepts any harness id, so anything that can reach HSM (host.docker.internal:3000 from every agent container) can enumerate admin PII cross-harness, including admins set via the dashboard that appear in no agent's env.
With mutations transport-gated (#139, active), this is information disclosure only — its old role as a confused-deputy actor source is defanged. Severity is bounded (each agent already holds its own surface's admin ids in env) but cross-harness enumeration of phone numbers is a real privacy gap.
Fix options: require the operator session cookie for this one GET (dashboard is the only intended consumer — the plugin never calls the list route, only the boolean admins/[userId] and groups/[groupId] paths, which must stay open); or return ids redacted/hashed unless authenticated. Add a test that a cookie-less list GET is refused while the boolean hot paths stay 200.
🤖 Generated with Claude Code
Flagged by the independent audit of the #138 re-cut (2026-07-03). Non-blocking there; filing as the agreed follow-up.
The list route
GET /api/harnesses/[id]/surfaces/[platform]/adminspasses the auth middleware unauthenticated (GETs are intentionally ungated for the plugin's boolean hot paths). But unlike the boolean routes, it returns the full admin identity list — Signal phone numbers / UUIDs, Telegram ids — and accepts any harness id, so anything that can reach HSM (host.docker.internal:3000from every agent container) can enumerate admin PII cross-harness, including admins set via the dashboard that appear in no agent's env.With mutations transport-gated (#139, active), this is information disclosure only — its old role as a confused-deputy
actorsource is defanged. Severity is bounded (each agent already holds its own surface's admin ids in env) but cross-harness enumeration of phone numbers is a real privacy gap.Fix options: require the operator session cookie for this one GET (dashboard is the only intended consumer — the plugin never calls the list route, only the boolean
admins/[userId]andgroups/[groupId]paths, which must stay open); or return ids redacted/hashed unless authenticated. Add a test that a cookie-less list GET is refused while the boolean hot paths stay 200.🤖 Generated with Claude Code