Skip to content

Internal header re-injection allows virtual server scope bypass #1082

Description

@jasonmadigan

Description

Two related issues combine to allow a client to bypass virtual server scoping.

M1: Internal header re-injection on broker passthrough path

HandleNoneToolCall (request_handlers.go:860-866) re-injects x-mcp-authorized and x-mcp-virtualserver from the original client-supplied headers after they were stripped in the headers phase (line 233). The stripping tells Envoy to remove them, but localRequestHeaders (line 104) retains the original client headers, which are copied to mcpRequest.Headers (line 242). The re-injection reads from this stale copy.

Data flow:

  1. Client sends x-mcp-virtualserver: attacker-value
  2. Headers phase strips it (Envoy removes it)
  3. Body phase: HandleNoneToolCall reads it from mcpReq.Headers (original headers still in memory) and re-injects it
  4. Broker receives the client-controlled value

L4: Virtual server filter fail-open

When GetVirtualSeverByHeader fails (unknown virtual server name), applyVirtualServerFilter (filtered_tools_handler.go:222-226) returns the full unfiltered tool list instead of an empty list.

Combined impact

A client injects x-mcp-virtualserver: nonexistent, the router re-injects it, the broker's lookup fails, and all tools are returned unfiltered -- bypassing virtual server scoping entirely.

Proposed Fix

  1. M1: Compute x-mcp-authorized and x-mcp-virtualserver server-side from validated session state and routing config, not from client-supplied headers
  2. L4: Return empty tools on virtual server lookup failure (fail-closed)

Review Guidance

  • internal/mcp-router/request_handlers.go:860-866 -- the re-injection path
  • internal/broker/filtered_tools_handler.go:212-226 -- the fail-open filter
  • Check all other uses of internalOnlyHeaders for similar patterns

Manual Verification

  1. Deploy with virtual server configured
  2. Send a tools/list request with header x-mcp-virtualserver: nonexistent
  3. Before fix: all tools returned. After fix: empty or error

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/authn-authzarea/securityIndicates the issue is particularly relevant to securitykind/bugtriage/needs-triageIssues that need review and decisions about their milestones and priority

    Type

    No type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions