Skip to content

fix: reject cross-origin endpoint events in SSE client - #958

Open
ez-lbz wants to merge 1 commit into
modelcontextprotocol:mainfrom
ez-lbz:fix/reject-cross-origin-endpoint-in-sse-client
Open

fix: reject cross-origin endpoint events in SSE client#958
ez-lbz wants to merge 1 commit into
modelcontextprotocol:mainfrom
ez-lbz:fix/reject-cross-origin-endpoint-in-sse-client

Conversation

@ez-lbz

@ez-lbz ez-lbz commented Aug 16, 2026

Copy link
Copy Markdown

In the HTTP+SSE transport, the server's endpoint event tells the client where to POST subsequent JSON-RPC messages. The client currently accepts a full http(s):// URL as-is, so a compromised server could redirect all subsequent traffic — including auth headers — to an attacker-controlled host. The TypeScript and Python clients already reject endpoints whose origin differs from the connection origin.

This change rejects full-URL endpoints whose origin (scheme, host, and port, with default ports normalized) does not match the SSE connection's origin: the endpoint future completes exceptionally and the transport fails to start. Relative and root-relative paths keep their existing resolution, and same-origin full URLs keep their existing behavior.

Copilot AI lite review requested due to automatic review settings August 16, 2026 15:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@devcrocod
devcrocod force-pushed the fix/reject-cross-origin-endpoint-in-sse-client branch from 6fc1be3 to fa252df Compare September 10, 2026 10:43
Copilot AI review requested due to automatic review settings September 10, 2026 10:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new security boundary isn’t fully covered by tests (notably the “same host but different port” origin-mismatch case), which increases regression risk for a security-sensitive change.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment on lines +71 to 75
fun `full url endpoint with a different origin is rejected`() = runTest {
// Given
val sseUrl = "http://example.com/api/mcp/sse"

// And

@devcrocod devcrocod left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are a few critical comments, please take a look
also note that ktlint failed on ci

* The comparison uses the authority string, so default ports are ignored (e.g. `http://host:80`
* and `http://host` are considered the same origin).
*/
private fun Url.hasSameOrigin(): Boolean = protocolWithAuthority == origin

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A raw string is compared, which can also include UserAndPassword. This can cause rejection in two cases, when credentials are present or when there is a case mismatch.

Also, why is this moved to a separate function? It is not used anywhere else, and it also increases complexity

val endpointOrigin = Url(eventData)
if (!endpointOrigin.hasSameOrigin()) {
val error = IllegalArgumentException(
"Endpoint origin ${endpointOrigin.protocolWithAuthority} does not match connection origin $origin",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

credentials might leak into error messages

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find a test for port mismatch, which seems like a frequent use case

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.

3 participants