GHSA-3xx2-mqjm-hg9x
## Summary The `GET`, `POST`, and `DELETE` handlers under `/agents/:id/keys` in the Paperclip control-plane API only call `assertBoard(req)`, which verifies that the caller has a board-type session but does not verify that the caller has access to the company owning the target agent. A board user whose membership is limited to Company A can therefore list, create, or revoke agent API keys for any agent in Company B by supplying the victim agent's UUID in the URL path. The `POST` handler returns the newly-minted token in cleartext, which authenticates subsequent requests as `{type:"agent", companyId:<CompanyB>}`, giving the attacker full agent-level access inside the victim tenant — a complete cross-tenant compromise. ## Details The three vulnerable routes are defined in `server/src/routes/agents.ts:2050-2087`: ```ts router.get("/agents/:id/keys", async (req, res) => { assertBoard(req); // <-- only checks actor.type === "board" const id = req.params.id as string; const keys = await svc.listKeys(id); res.json(keys); }); router.post("/agents/:id/keys", validate(createAgentKeySchema), async (req, res) => { assertBoard(req); // <-- same const id = req.params.id as string; const key = await svc.createApiKey(id, req.body.name); // ... activity log ... res.status(201).json(key); // returns cleartext `token` }); router.delete("/agents/:id/keys/:keyId", async (req, res) => { assertBoard(req); // <-- same const keyId = req.params.keyId as string; const revoked = await svc.revokeKey(keyId); if (!revoked) { res.status(404).json({ error: "Key not found" }); return; } res.json({ ok: true }); }); ``` `assertBoard` in `server/src/routes/authz.ts:4-8` is intentionally narrow: ```ts export function assertBoard(req: Request) { if (req.actor.type !== "board") { throw forbidden("Board access required"); } } ``` It does **not** consult `req.actor.companyIds` or `req.
Properties
- ghsa_id
- GHSA-3xx2-mqjm-hg9x
- severity
- critical
- summary
- Paperclip: Cross-tenant agent API key IDOR in `/agents/:id/keys` routes allows full victim-company compromise
- cvss_score
- 10
- cve_id
- GHSA-3xx2-mqjm-hg9x
- cvss_vector
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
- is_ghsa_only
- true
- ghsa_published
- 2026-04-16T22:49:46Z
- source_url
- https://github.com/advisories/GHSA-3xx2-mqjm-hg9x
- ghsa_updated
- 2026-04-16T22:49:48Z
Related Entities (4)
VULNERABLE_TO (1)
AFFECTS (1)
HAS_WEAKNESS (1)
REPORTED_BY (1)
Explore deeper with Ninja Signal's threat intelligence graph