Skip to content

feat: add notification isolation tests for GHSA-345p-7cg4-v4c7#137

Draft
pcarleton wants to merge 8 commits intomainfrom
pcarleton/session-isolation
Draft

feat: add notification isolation tests for GHSA-345p-7cg4-v4c7#137
pcarleton wants to merge 8 commits intomainfrom
pcarleton/session-isolation

Conversation

@pcarleton
Copy link
Member

Adds conformance test scenarios for the cross-client data leak vulnerability (GHSA-345p-7cg4-v4c7), focused on Issue 2 (server re-use / this._transport overwrite).

New scenarios

Scenario What it tests Report issue
session-isolation Response cross-wiring via shared transport (deterministic, 2 clients) Issue 1
notification-isolation Progress notification leak via shared server (deterministic, 2 clients) Issue 2
notification-isolation-fuzz Same as above with 10 concurrent clients, unique progress tokens Issue 2

How to run

# Against any MCP server implementing test_tool_with_progress:
node dist/index.js server --url http://localhost:3000/mcp --scenario notification-isolation
node dist/index.js server --url http://localhost:3000/mcp --scenario notification-isolation-fuzz

Server requirements

The server under test needs to implement test_tool_with_progress which sends 3 notifications/progress events (0/100, 50/100, 100/100) with ~50ms delays, using the progressToken from _meta.

Context

Built to share with Cloudflare so they can test their createMcpHandler (stateless Workers path) against the notification cross-talk vulnerability.

Add two new conformance test scenarios for server re-use notification
cross-talk (Issue 2 from the cross-talk CVE report):

- notification-isolation: deterministic 2-client test that verifies
  progress notifications from a slow tool don't leak to a second
  client that connects mid-handler

- notification-isolation-fuzz: N=10 concurrent clients each calling
  test_tool_with_progress with unique progress tokens, verifying no
  cross-contamination across any pair of clients

Also adds a vulnerable server example (no-notification-isolation.ts)
that mirrors Cloudflare's createMcpHandler pattern: shared McpServer
with per-request transports.
@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 5, 2026

Open in StackBlitz

npx https://pkg.pr.new/modelcontextprotocol/conformance/@modelcontextprotocol/conformance@137

commit: 6e40296

- Each fuzz client now emits its own check so you can see per-client
  results (token, notification counts, pass/fail)
- Client count defaults to 10, overridable via FUZZ_CLIENT_COUNT env var
…st server

- parseSSEStreamFull now reads all events until stream closes (not just
  until first response), catching leaked notifications after the response
- Both notification-isolation and notification-isolation-fuzz now fail
  when a client receives zero own notifications (indicates they were
  routed to another client's stream)
- Remove stagger between fuzz requests (fire all concurrently)
- Add naive-shared-state-server.ts: a minimal non-SDK server with a
  subtle bug (inFlightRequests keyed by request ID, not session+request
  ID). Verified to fail both notification isolation tests.
- Tests now verify each client receives all 3 expected progress
  notifications (not just > 0)
- Failure messages explain what happened:
  - 'Received 1/3 expected progress notifications (2 likely routed
    to another client's stream)'
  - 'Received 4 notification(s) from other clients: fuzz-client-0
    (2x), fuzz-client-1 (2x) — the server wrote them to this
    client's SSE stream'
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