mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-17 19:20:39 +09:00
Expand sandbox callback bridge allowlist to cover the documented heartbeat surface (#5324)
## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies > - When an agent runs in an e2b sandbox or other non-managed environment, it talks back to the Paperclip server through a per-lease callback bridge that proxies HTTP requests > - The bridge has an allowlist of method/path patterns it will forward; anything outside the list is rejected to keep the bridge tight > - The allowlist had drifted behind what the heartbeat documentation describes as the supported callback surface — several documented endpoints (issue updates, agent-side log emit, work-status writes) were being rejected at the bridge > - This pull request expands the allowlist to cover the documented heartbeat surface and adds tests that pin every newly-allowed pattern, so the doc and the bridge stay in sync > - The benefit is sandboxed runs no longer hit "method not allowed" / "path not allowed" rejections on the documented set of callbacks ## What Changed - `packages/adapter-utils/src/sandbox-callback-bridge.ts`: expand the method/path allowlist to match the documented heartbeat callback surface - `packages/adapter-utils/src/sandbox-callback-bridge.test.ts`: add coverage for every newly-allowed pattern, plus negative cases for patterns that should still be rejected ## Verification - `pnpm vitest run --no-coverage --project @paperclipai/adapter-utils` - `pnpm typecheck` clean - Manual: previously-rejected callbacks from sandboxed runs now succeed end-to-end ## Risks Low. The allowlist only grows; nothing previously allowed is now blocked. Tests pin both the new allowed patterns and that out-of-doc patterns stay rejected. ## Model Used Claude Opus 4.7 (1M context) ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable — new tests cover added patterns + still-rejected negatives - [x] If this change affects the UI, I have included before/after screenshots — N/A (no UI) - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge
This commit is contained in:
parent
83e7ecc58e
commit
36eaf9778f
2 changed files with 156 additions and 3 deletions
|
|
@ -23,15 +23,76 @@ export interface SandboxCallbackBridgeRouteRule {
|
|||
path: RegExp;
|
||||
}
|
||||
|
||||
// Routes the in-sandbox heartbeat skill is documented to call. The server
|
||||
// still enforces actor-level permissions on top of this allowlist; the list
|
||||
// exists to bound the surface area a compromised CLI could reach via the
|
||||
// reverse bridge. Keep this in sync with the Paperclip skill in
|
||||
// `skills/paperclip/SKILL.md` and `references/api-reference.md`.
|
||||
export const DEFAULT_SANDBOX_CALLBACK_BRIDGE_ROUTE_ALLOWLIST: readonly SandboxCallbackBridgeRouteRule[] = [
|
||||
// Identity, inbox, agent self-management
|
||||
{ method: "GET", path: /^\/api\/agents\/me$/ },
|
||||
{ method: "GET", path: /^\/api\/agents\/me\/inbox-lite$/ },
|
||||
{ method: "GET", path: /^\/api\/agents\/me\/inbox\/mine$/ },
|
||||
{ method: "GET", path: /^\/api\/agents\/[^/]+$/ },
|
||||
{ method: "GET", path: /^\/api\/agents\/[^/]+\/skills$/ },
|
||||
{ method: "POST", path: /^\/api\/agents\/[^/]+\/skills\/sync$/ },
|
||||
{ method: "PATCH", path: /^\/api\/agents\/[^/]+\/instructions-path$/ },
|
||||
|
||||
// Company-level reads used to discover work and context
|
||||
{ method: "GET", path: /^\/api\/companies\/[^/]+$/ },
|
||||
{ method: "GET", path: /^\/api\/companies\/[^/]+\/dashboard$/ },
|
||||
{ method: "GET", path: /^\/api\/companies\/[^/]+\/agents$/ },
|
||||
{ method: "GET", path: /^\/api\/companies\/[^/]+\/issues$/ },
|
||||
{ method: "GET", path: /^\/api\/companies\/[^/]+\/projects$/ },
|
||||
{ method: "GET", path: /^\/api\/companies\/[^/]+\/goals$/ },
|
||||
{ method: "GET", path: /^\/api\/companies\/[^/]+\/org$/ },
|
||||
{ method: "GET", path: /^\/api\/companies\/[^/]+\/approvals$/ },
|
||||
{ method: "GET", path: /^\/api\/companies\/[^/]+\/routines$/ },
|
||||
{ method: "GET", path: /^\/api\/companies\/[^/]+\/skills$/ },
|
||||
{ method: "GET", path: /^\/api\/projects\/[^/]+$/ },
|
||||
{ method: "GET", path: /^\/api\/goals\/[^/]+$/ },
|
||||
|
||||
// Issue lifecycle: read context, checkout, update, comment, document, release
|
||||
{ method: "GET", path: /^\/api\/issues\/[^/]+$/ },
|
||||
{ method: "GET", path: /^\/api\/issues\/[^/]+\/heartbeat-context$/ },
|
||||
{ method: "GET", path: /^\/api\/issues\/[^/]+\/comments(?:\/[^/]+)?$/ },
|
||||
{ method: "GET", path: /^\/api\/issues\/[^/]+\/documents(?:\/[^/]+)?$/ },
|
||||
{ method: "POST", path: /^\/api\/issues\/[^/]+\/checkout$/ },
|
||||
{ method: "POST", path: /^\/api\/issues\/[^/]+\/comments$/ },
|
||||
{ method: "POST", path: /^\/api\/issues\/[^/]+\/interactions(?:\/[^/]+)?$/ },
|
||||
{ method: "GET", path: /^\/api\/issues\/[^/]+\/documents(?:\/[^/]+)?$/ },
|
||||
{ method: "GET", path: /^\/api\/issues\/[^/]+\/documents\/[^/]+\/revisions$/ },
|
||||
{ method: "PUT", path: /^\/api\/issues\/[^/]+\/documents\/[^/]+$/ },
|
||||
{ method: "POST", path: /^\/api\/issues\/[^/]+\/checkout$/ },
|
||||
{ method: "POST", path: /^\/api\/issues\/[^/]+\/release$/ },
|
||||
{ method: "PATCH", path: /^\/api\/issues\/[^/]+$/ },
|
||||
{ method: "GET", path: /^\/api\/issues\/[^/]+\/approvals$/ },
|
||||
|
||||
// Issue-thread interactions (suggest tasks, ask questions, request confirmation)
|
||||
{ method: "GET", path: /^\/api\/issues\/[^/]+\/interactions(?:\/[^/]+)?$/ },
|
||||
{ method: "POST", path: /^\/api\/issues\/[^/]+\/interactions$/ },
|
||||
{ method: "POST", path: /^\/api\/issues\/[^/]+\/interactions\/[^/]+\/(?:accept|reject|respond)$/ },
|
||||
|
||||
// Subtasks / delegation
|
||||
{ method: "POST", path: /^\/api\/companies\/[^/]+\/issues$/ },
|
||||
|
||||
// Approvals (request, read, comment)
|
||||
{ method: "GET", path: /^\/api\/approvals\/[^/]+$/ },
|
||||
{ method: "GET", path: /^\/api\/approvals\/[^/]+\/issues$/ },
|
||||
{ method: "GET", path: /^\/api\/approvals\/[^/]+\/comments$/ },
|
||||
{ method: "POST", path: /^\/api\/approvals\/[^/]+\/comments$/ },
|
||||
{ method: "POST", path: /^\/api\/companies\/[^/]+\/approvals$/ },
|
||||
|
||||
// Execution workspaces and runtime services (start/stop/restart dev servers)
|
||||
{ method: "GET", path: /^\/api\/execution-workspaces\/[^/]+$/ },
|
||||
{ method: "POST", path: /^\/api\/execution-workspaces\/[^/]+\/runtime-services\/(?:start|stop|restart)$/ },
|
||||
|
||||
// Routines (agents manage their own routines and triggers)
|
||||
{ method: "GET", path: /^\/api\/routines\/[^/]+$/ },
|
||||
{ method: "GET", path: /^\/api\/routines\/[^/]+\/runs$/ },
|
||||
{ method: "POST", path: /^\/api\/companies\/[^/]+\/routines$/ },
|
||||
{ method: "PATCH", path: /^\/api\/routines\/[^/]+$/ },
|
||||
{ method: "POST", path: /^\/api\/routines\/[^/]+\/run$/ },
|
||||
{ method: "POST", path: /^\/api\/routines\/[^/]+\/triggers$/ },
|
||||
{ method: "PATCH", path: /^\/api\/routine-triggers\/[^/]+$/ },
|
||||
{ method: "DELETE", path: /^\/api\/routine-triggers\/[^/]+$/ },
|
||||
] as const;
|
||||
|
||||
export const DEFAULT_SANDBOX_CALLBACK_BRIDGE_HEADER_ALLOWLIST = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue