Skip to content

fix(mcp): block cross-origin credential redirects#396

Merged
kevincodex1 merged 1 commit into
Gitlawb:mainfrom
GautamBytes:fix/mcp-redirect-auth-leak
Jul 2, 2026
Merged

fix(mcp): block cross-origin credential redirects#396
kevincodex1 merged 1 commit into
Gitlawb:mainfrom
GautamBytes:fix/mcp-redirect-auth-leak

Conversation

@GautamBytes

@GautamBytes GautamBytes commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Hardens remote MCP HTTP/SSE traffic so auth-bearing requests stay on the configured server origin.
  • Rejects cross-origin redirects and cross-origin SSE endpoint events before secrets can be forwarded.
  • Keeps same-origin redirects/endpoints working, including default-port normalization.

Security report was submitted privately first.

Summary by CodeRabbit

  • Bug Fixes
    • Strengthened HTTP redirect safeguards to block cross-origin hops and limit redirect chains, preventing header leakage.
    • Hardened SSE endpoint and URL resolution to require the endpoint stays on the same configured origin.
    • Improved OAuth redirect handling to ensure bearer authorization is never sent to cross-origin redirect targets.
  • Tests
    • Added coverage for same-origin enforcement, default-port normalization, and redirect/SSE/OAuth rejection cases.

Copilot AI review requested due to automatic review settings July 2, 2026 15:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens MCP’s HTTP and SSE networking so requests that carry credentials (custom headers or OAuth bearer tokens) cannot be redirected or pointed to a different origin than the configured server, preventing accidental secret forwarding.

Changes:

  • Added an HTTP redirect guard that enforces same-origin redirects (including default-port normalization) and caps redirect chains.
  • Restricted SSE endpoint event URLs to the configured server origin before any authenticated follow-up requests are made.
  • Updated and expanded tests to cover same-origin redirects, cross-origin redirect blocking, and cross-origin SSE endpoint blocking.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
internal/mcp/network_redirect.go Adds same-origin logic + redirect guard used by MCP HTTP clients to prevent cross-origin credential redirects.
internal/mcp/network_client.go Enforces same-origin SSE endpoints and ensures non-OAuth clients also use the redirect guard.
internal/mcp/network_client_test.go Adds targeted unit tests for redirect blocking, origin normalization, and SSE endpoint origin restrictions.
internal/mcp/client_test.go Adds integration-style tests validating same-origin redirects still work and cross-origin redirects/endpoints are blocked before headers leak.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/mcp/network_redirect.go
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c6063469-def2-4d6f-ba72-b0b4b59776f3

📥 Commits

Reviewing files that changed from the base of the PR and between fd0ae06 and 7ddee33.

📒 Files selected for processing (4)
  • internal/mcp/client_test.go
  • internal/mcp/network_client.go
  • internal/mcp/network_client_test.go
  • internal/mcp/network_redirect.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/mcp/network_redirect.go
  • internal/mcp/client_test.go
  • internal/mcp/network_client_test.go
  • internal/mcp/network_client.go

Walkthrough

Adds MCP redirect and SSE origin enforcement, wires the guarded client into OAuth and non-OAuth paths, and expands tests for same-origin redirects, cross-origin rejection, and origin normalization.

Changes

MCP Origin Enforcement

Layer / File(s) Summary
Redirect policy and origin comparison helpers
internal/mcp/network_redirect.go
Adds guarded redirect handling, same-origin comparison, effective port normalization, and formatted MCP origin errors.
Wiring mcpHTTPClient into OAuth and SSE resolution
internal/mcp/network_client.go
Routes OAuth and non-OAuth HTTP clients through the MCP-guarded client and rejects SSE endpoints outside the configured origin.
Tests for redirect origin enforcement and SSE endpoint resolution
internal/mcp/client_test.go, internal/mcp/network_client_test.go
Adds coverage for redirect header preservation, cross-origin rejection, bearer-token leakage prevention, default-port normalization, and SSE endpoint origin checks.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant mcpHTTPClient
  participant checkMCPRedirect
  participant OriginServer
  participant RedirectTarget

  Client->>mcpHTTPClient: send request
  mcpHTTPClient->>OriginServer: initial request
  OriginServer-->>mcpHTTPClient: redirect response
  mcpHTTPClient->>checkMCPRedirect: evaluate redirect
  checkMCPRedirect->>checkMCPRedirect: compare origins
  alt same origin
    checkMCPRedirect-->>mcpHTTPClient: allow redirect
    mcpHTTPClient->>RedirectTarget: follow redirect with headers
  else cross-origin
    checkMCPRedirect-->>mcpHTTPClient: return error
    mcpHTTPClient-->>Client: error
  end
Loading

Possibly related PRs

  • Gitlawb/zero#67: Related SSE endpoint resolution changes in internal/mcp/network_client.go.
  • Gitlawb/zero#179: Related OAuth round-tripper wiring that now uses the guarded MCP HTTP client.

Suggested reviewers: Vasanthdev2004

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the core change: blocking cross-origin credential-bearing redirects in MCP traffic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
internal/mcp/network_client.go (1)

880-883: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fix garbled comment.

// not declare OAuth use the default transport with the same MCP redirect guard. reads as broken English — looks like leading words (e.g. "Servers that do") were dropped.

✏️ Proposed fix
-// not declare OAuth use the default transport with the same MCP redirect guard.
+// Servers that do not declare OAuth use the default transport with the same MCP redirect guard.
 func oauthHTTPClient(server Server) (*http.Client, error) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/mcp/network_client.go` around lines 880 - 883, The comment above
oauthHTTPClient is garbled and appears to have lost its opening words; replace
it with a clear sentence that accurately describes the non-OAuth case and the
use of the default transport with the MCP redirect guard. Update the comment
text only, keeping the behavior in oauthHTTPClient and the mcpHTTPClient call
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/mcp/network_client.go`:
- Around line 880-883: The comment above oauthHTTPClient is garbled and appears
to have lost its opening words; replace it with a clear sentence that accurately
describes the non-OAuth case and the use of the default transport with the MCP
redirect guard. Update the comment text only, keeping the behavior in
oauthHTTPClient and the mcpHTTPClient call unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b6329b5a-1948-4b45-8836-c638d6765958

📥 Commits

Reviewing files that changed from the base of the PR and between 960db96 and fd0ae06.

📒 Files selected for processing (4)
  • internal/mcp/client_test.go
  • internal/mcp/network_client.go
  • internal/mcp/network_client_test.go
  • internal/mcp/network_redirect.go

@GautamBytes GautamBytes force-pushed the fix/mcp-redirect-auth-leak branch from fd0ae06 to 7ddee33 Compare July 2, 2026 15:19

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Verdict: Approve ✅ — correct, complete for its scope, and it doesn't break same-origin flows

Reviewed this as a security change (checked out the branch, audited the whole client surface, ran the suite). The hardening is sound and the design choices are the right ones.

Security logic is correct

  • Origin comparison (sameMCPOrigin) is scheme + host + effective port, case-insensitive, with default-port normalization (80/443). So https://h/xhttps://H:443/y, and an https→http downgrade is correctly treated as cross-origin. The table test covers all four cases.
  • Redirect target is compared to the original URL (via[0].URL), not just the previous hop — so a multi-hop chain can't walk off-origin one same-origin step at a time. This is the subtle thing a naive implementation gets wrong; this one gets it right.
  • The bearer/custom header never reaches a cross-origin target: CheckRedirect returns the error before the client issues the next request, so the OAuth RoundTripper never runs for the target. All three no-leak tests assert targetHits == 0, and confirm the header still reaches the configured origin.
  • SSE endpoint is origin-pinned (resolveSSEEndpointURL) before any message POST, closing the "malicious event: endpoint points cross-origin" vector — and the POST itself also rides the guarded client, so it's belt-and-suspenders.
  • Redirect chain is bounded (maxMCPRedirects).

Coverage is complete for the data plane — I checked for bypasses

Both remote entry points build their client through oauthHTTPClientmcpHTTPClient (HTTP at connectNetwork, SSE at connectRemoteSSE), and every client.client.Do(...) uses it. The non-OAuth path now gets the guard too (previously bare http.DefaultClient). I grepped the package for other client construction — no MCP request path bypasses the guard.

It doesn't break legitimate behavior

  • TestHTTPClientFollowsSameOriginRedirect proves same-origin redirects (incl. default-port equivalence) still work end to end with the header intact.
  • Non-OAuth servers switching from the shared http.DefaultClient to a per-server mcpHTTPClient(server, nil) keeps http.DefaultTransport (nil transport), so connection pooling and timeout behavior are unchanged.
  • Full internal/mcp suite passes locally; go build ./..., go vet, gofmt clean. (Race detector runs in CI on Linux — the new tests use sync/atomic correctly.)

One optional follow-up (non-blocking, out of this PR's scope)

The OAuth control-plane clients — token exchange / metadata discovery in oauth.go and the refresh storeTokenSource (network_client.go:892) — still use http.DefaultClient without this guard. A 307/308 redirect there preserves method+body, so in principle a hostile authorization-server-metadata could bounce a token request cross-origin. Different threat model (requires compromising AS discovery) and clearly outside "MCP data traffic stays on origin", but extending the same guard to those clients would be a tidy defense-in-depth follow-up.

Nice, careful work — approving. Thanks @GautamBytes, and good call reporting privately first.

@kevincodex1 kevincodex1 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.

LGTM

@kevincodex1 kevincodex1 merged commit f915f70 into Gitlawb:main Jul 2, 2026
7 checks passed
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.

4 participants