Security Digest · Critical advisory (no CVE assigned)

GHSA-47wq-cj9q-wpmp: Paperclip: Cross-tenant agent API token minting via missing assertCompanyAccess on /api/agents/:id/keys

> 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).

CVSS 9.9 Critical GitHub-reviewed · critical

Should you act?

  • Only if you depend on one of the packages below, directly or transitively, in a vulnerable version. Check your lockfile with npm audit (or: npm ls <package>).
  • No CVE has been assigned, so it will not appear in CVE-based scanners that ignore GitHub advisories. npm audit, pip-audit and Dependabot do read GitHub advisories.

Affected packages

EcosystemPackageVulnerableFirst fixed
npm@paperclipai/server< 2026.416.02026.416.0

Details

> 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 instanceadmin 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:

Compare the handler 12 lines below, router.post("/agents/:id/wakeup"), which shows the correct pattern: it fetches the agent, then calls assertCompanyAccess(req, agent.companyId). The three /keys handlers above do not even fetch the agent.

CVSS: 9.9 (v3.1) CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

Sources and references

  1. GitHub Advisory GHSA-47wq-cj9q-wpmp github.com
  2. github.com github.com

Critical npm advisories · Today’s Security Digest