Paperclip: Cross-tenant agent API token minting via missing assertCompanyAccess on /api/agents/:id/keys
Date: 2026-04
Affected: npm/@paperclipai/server
Attack vector: auth-bypass
Category: vulnerability-disclosure
Severity: Critical
CWE: CWE-1220
CWE-285
CWE-639
CWE-862
CVSS: 9.9 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H)

<img width="7007" height="950" alt="01-setup" src="https://github.com/user-attachments/assets/1596b8d1-8de5-4c21-b1d2-2db41b568d7e" /> > Isolated paperclip instance running in authenticated mode (default config) > on a clean Docker image matching commit b649bd4 (2026.411.0-canary.8, post > the 2026.410.0 patch). This advisory was verified on an unmodified build. ### Summary `POST /api/agents/:id/keys`, `GET /api/agents/:id/keys`, and `DELETE /api/agents/:id/keys/:keyId` (`server/src/routes/agents.ts` lines 2050-2087) only call `assertBoard` to authorize the caller. They never call `assertCompanyAccess` and never verify that the caller is a member of the company that owns the target agent. Any authenticated board user (including a freshly signed-up account with zero company memberships and no `instance_admin` role) can mint a plaintext `pcp_*` agent API token for any agent in any company on the instance. The minted token is bound to the **victim** agent's `companyId` server-side, so every downstream `assertCompanyAccess` check on that token authorizes operations inside the victim tenant. This is a pure authorization bypass on the core tenancy boundary. It is distinct from GHSA-68qg-g8mg-6pr7 (the unauth import → RCE chain disclosed in 2026.410.0): that advisory fixed one handler, this report is a different handler with the same class of mistake that the 2026.410.0 patch did not cover. ### Root Cause `server/src/routes/agents.ts`, lines 2050-2087: ```ts router.get("/age


Mitigations:

Architectural layers (MAESTRO):