mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-15 21:11:08 +02:00
fix(x): fail-closed tool permissions with per-tool catalog declarations
The permission checker returned {required: false} for every non-builtin
toolId and for any builtin outside the executeCommand/file-tools switch,
so composio-execute-tool (email sends, GitHub/Jira writes), executeMcpTool,
and mcp:* attachments on user agents executed with no permission check —
even in manual mode — and never reached the auto-permission classifier.
The extension contract was inverted: a new side-effecting tool shipped
ungated unless someone remembered to extend a switch in another module.
Every builtin now declares its permission policy in the catalog itself
(required field, compile-enforced): "none", "prompt", "command-allowlist",
"file-boundary", "composio-execute", or "mcp-execute". The checker reads
the declaration and FAILS CLOSED: undeclared builtins, mcp:* attachments,
and unknown toolId families require permission. Composio and MCP requests
carry family-specific payloads (new ToolPermissionMetadata kinds, rendered
by the permission card; generic fallback for everything else). All
previously-ungated builtins keep today's behavior via explicit "none"
declarations, except addMcpServer which now prompts; a catalog test pins
the audited gated set so policy changes stay intentional.
Auto-permission flows (background tasks, live notes, channels) route the
newly gated calls through the existing classifier per the §9.3 matrix;
defer still denies without a human. No grant persistence yet — every
gated call prompts (or classifies) each time.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
e22d3b4796
commit
ac7679ec16
23 changed files with 388 additions and 25 deletions
|
|
@ -100,6 +100,24 @@ export const ToolPermissionMetadata = z.discriminatedUnion("kind", [
|
|||
paths: z.array(z.string()),
|
||||
pathPrefix: z.string(),
|
||||
}),
|
||||
// A Composio action execution (composio-execute-tool).
|
||||
z.object({
|
||||
kind: z.literal("composio"),
|
||||
toolkitSlug: z.string(),
|
||||
toolSlug: z.string(),
|
||||
}),
|
||||
// An MCP tool execution (executeMcpTool or an mcp:* attachment).
|
||||
z.object({
|
||||
kind: z.literal("mcp"),
|
||||
serverName: z.string().optional(),
|
||||
toolName: z.string(),
|
||||
}),
|
||||
// Generic fail-closed request: a tool with no more specific policy.
|
||||
z.object({
|
||||
kind: z.literal("tool"),
|
||||
toolId: z.string(),
|
||||
toolName: z.string(),
|
||||
}),
|
||||
]);
|
||||
|
||||
export const ToolPermissionRequestEvent = BaseRunEvent.extend({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue