| Gói / sản phẩm | Phiên bản bị ảnh hưởng | Phiên bản đã vá |
|---|---|---|
| @paperclipai/server | < 2026.416.0 | 2026.416.0 |
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
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.
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.
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.
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.
These endpoints in server/src/routes/access.ts require no authentication:
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.
GET /api/health returns deployment mode, exposure setting, auth status, bootstrap status, version, and feature flags.
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.
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 fileAdd authentication to CLI challenge creation in server/src/routes/access.ts:
POST /api/cli-auth/challenges -- add assertBoard at minimumAdd authentication to skill endpoints in server/src/routes/access.ts:
GET /api/skills/availableGET /api/skills/indexGET /api/skills/:skillNameReduce health endpoint information -- consider removing deploymentMode, deploymentExposure, and version from the unauthenticated response, or gating the full response behind assertBoard
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.
Discord: sagi03581
Happy to help verify fixes or provide additional details.
Bạn đang dùng một trong các sản phẩm này? Quét site để biết có đang lộ phiên bản có lỗ hổng không.
Quét site miễn phí