{"id":"GHSA-xfqj-r5qw-8g4j","aliases":[],"url":"https://o3.security/vulnerability/GHSA-xfqj-r5qw-8g4j","summary":"Paperclip: Unauthenticated Access to Multiple API Endpoints in Authenticated Mode","details":"## Summary\n\nSeveral API endpoints in `authenticated` mode have no authentication at all. They respond to completely unauthenticated requests with sensitive data or allow state-changing operations. No account, no session, no API key needed.\n\nVerified against the latest version.\n\nDiscord: sagi03581\n\n## Steps to Reproduce\n\n### 1. Unauthenticated issue data access\n\n`GET /api/heartbeat-runs/:runId/issues` returns issue data for a heartbeat run with zero authentication. Every other endpoint in `server/src/routes/activity.ts` calls `assertCompanyAccess`, but this one was missed.\n\n```bash\ncurl -s http://<target>:3100/api/heartbeat-runs/00000000-0000-0000-0000-000000000001/issues\n# -> []  (HTTP 200, not 401 or 403)\n```\n\nIf an attacker obtains a valid run UUID (from logs, error messages, shared URLs, or by probing), they can read issue data without any credentials.\n\n### 2. Unauthenticated CLI auth challenge creation\n\n`POST /api/cli-auth/challenges` creates a CLI authentication challenge with no actor check at all. The handler at `server/src/routes/access.ts:1638-1659` skips any auth verification.\n\n```bash\ncurl -s -X POST -H \"Content-Type: application/json\" \\\n  -d '{\"command\":\"test\"}' \\\n  http://<target>:3100/api/cli-auth/challenges\n# returns challenge ID, token, and a pre-generated board API key\n```\n\nThe response includes a `boardApiToken` that becomes active once the challenge is approved. Combined with open registration (separate report), this enables persistent API key generation.\n\n### 3. Unauthenticated agent instruction / system prompt leakage\n\nThese endpoints in `server/src/routes/access.ts` require no authentication:\n\n```bash\ncurl -s http://<target>:3100/api/skills/index\n# returns all available skill endpoints\n\ncurl -s http://<target>:3100/api/skills/paperclip\n# returns the FULL agent heartbeat procedure including:\n#   - every API endpoint and its parameters\n#   - authentication mechanism (env var names, header formats)\n#   - the complete agent coordination protocol\n#   - the agent creation/hiring workflow\n\ncurl -s http://<target>:3100/api/skills/paperclip-create-agent\n# returns the full agent creation workflow with adapter configs\n```\n\nThis hands an attacker a complete map of the internal API without authenticating. It also leaks how agents authenticate, how heartbeats work, and what adapter configurations are available.\n\n### 4. Unauthenticated deployment configuration disclosure\n\n`GET /api/health` returns deployment mode, exposure setting, auth status, bootstrap status, version, and feature flags.\n\n```bash\ncurl -s http://<target>:3100/api/health\n# {\n#   \"deploymentMode\": \"authenticated\",\n#   \"deploymentExposure\": \"public\",\n#   \"authReady\": true,\n#   \"bootstrapStatus\": \"ready\",\n#   \"version\": \"2026.403.0\",\n#   ...\n# }\n```\n\nTells an attacker exactly how the instance is configured, whether registration is available, and what version is running.\n\n## Impact\n\n- **Data exposure**: heartbeat run issues accessible without credentials. Agent instructions and full API structure exposed to anyone.\n- **Reconnaissance**: an attacker can fingerprint the deployment (mode, version, features) and map the entire internal API before attempting anything else.\n- **Auth bypass stepping stone**: unauthenticated CLI challenge creation is a building block for the full RCE chain (reported separately).\n\n## Suggested Fixes\n\n1. **Add authentication to heartbeat run issues** in `server/src/routes/activity.ts`:\n   - `GET /api/heartbeat-runs/:runId/issues` -- add `assertCompanyAccess` like every other endpoint in the same file\n\n2. **Add authentication to CLI challenge creation** in `server/src/routes/access.ts`:\n   - `POST /api/cli-auth/challenges` -- add `assertBoard` at minimum\n\n3. **Add authentication to skill endpoints** in `server/src/routes/access.ts`:\n   - `GET /api/skills/available`\n   - `GET /api/skills/index`\n   - `GET /api/skills/:skillName`\n\n4. **Reduce health endpoint information** -- consider removing `deploymentMode`, `deploymentExposure`, and `version` from the unauthenticated response, or gating the full response behind `assertBoard`\n\n5. Consider a **global auth rejection middleware** for all `/api/*` routes in `authenticated` mode. Currently unauthenticated requests get `actor: { type: \"none\" }` and pass through to `next()`, relying on each route handler to check individually. A missing check means an open endpoint. Rejecting `type: \"none\"` at the middleware level for all routes except an explicit public allowlist (health, sign-in, sign-up, webhooks) would prevent this class of bug entirely.\n\n## Contact\n\nDiscord: sagi03581\n\nHappy to help verify fixes or provide additional details.","published":"2026-04-16T22:47:05Z","modified":"2026-04-16T23:06:03.284301Z","cvss":{"score":8.3,"severity":"HIGH","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L"},"epss":null,"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"@paperclipai/server","fixedVersion":"2026.416.0"}],"fix":null,"references":[{"type":"WEB","url":"https://github.com/paperclipai/paperclip/security/advisories/GHSA-xfqj-r5qw-8g4j"},{"type":"PACKAGE","url":"https://github.com/paperclipai/paperclip"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-04-16T23:06:03.284301Z"}}