Skip to content

fix(daemon): gate dynamic stdio MCP spawn (H6 / DR-038)#81

Open
sudhirverma wants to merge 1 commit into
mainfrom
AAP-82828
Open

fix(daemon): gate dynamic stdio MCP spawn (H6 / DR-038)#81
sudhirverma wants to merge 1 commit into
mainfrom
AAP-82828

Conversation

@sudhirverma

@sudhirverma sudhirverma commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes finding H6: dynamic RegisterMcpServer with transport: stdio previously spawned command / args directly, so any caller with mcp_register (or anyone when consumers were unset) could run arbitrary host commands.

This change fails closed before any process is spawned:

  • Allowlistcommand must match security.stdio_command_allowlist (basename or absolute path). Empty / omitted allowlist denies all dynamic stdio.
  • Operator approval — allowlisted spawns wait for dashboard / POST /api/mcp/stdio-spawns/:requestId unless stdio_require_approval: false.
  • Auth — when consumers is configured, stdio command/args require an authenticated consumer with mcp_register.

Config-file mcp_servers remain admin-trusted and skip these gates. Prefer caller-spawned HTTP/SSE for dynamic registration (DR-025). Documented as DR-038.

Config

security:
  max_dynamic_mcp_servers: 10
  stdio_command_allowlist:
    - npx
    - uvx
  stdio_require_approval: true

API / UI

Surface Purpose
GET /api/mcp/stdio-spawns Pending approvals + recent denials
POST /api/mcp/stdio-spawns/:requestId { "decision": "allow" | "deny" }
Dashboard → MCP Servers Pending spawn cards + denial list

Denials return clear gRPC PERMISSION_DENIED reasons and are logged as [StdioMCP] DENIED: ….

Test plan

  • Unit: non-allowlisted /bin/sh rejected; StdioMCPTransport not constructed
  • Unit: allowlisted command + approve → spawn; deny / missing handler → no spawn
  • Unit: empty allowlist fail-closed; config-based connect() skips allowlist
  • Integration E2E (mcp-stdio-spawn-policy.test.ts):
    • Register /bin/sh → denied, no spawn, denial visible via API
    • Register allowlisted npx → pending approval → approve → process starts
    • No token / wrong capability when consumers enabled → PERMISSION_DENIED
  • Manual: set allowlist in config.yaml, start web dashboard, approve a pending stdio spawn from MCP Servers tab
  • Manual: confirm denial reason appears under “Recent stdio spawn denials”

Related

Require command allowlist and operator approval before spawning stdio MCP
processes, and reject command/args from unauthenticated callers when
consumers are configured.
@sudhirverma
sudhirverma requested a review from cidrblock July 20, 2026 02:45
sathyapramod added a commit that referenced this pull request Jul 21, 2026
Renumber self-connection decision to DR-039 (avoids collision with #81),
refuse local MCP URLs when listen ports are unknown, normalize
IPv4-mapped loopback, record failed status on self-reject, and drop
unintentional package-lock engines churn.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant