Skip to content

Conversation

@ezynda3
Copy link
Contributor

@ezynda3 ezynda3 commented Oct 2, 2025

Description

This PR fixes a security vulnerability where tools could be invoked without proper session initialization. The InsecureStatefulSessionIdManager was only validating session ID format but not checking if the session actually existed, allowing any well-formatted fake session ID to bypass authentication.

Fixes #579

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • MCP spec compatibility implementation
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring (no functional changes)
  • Performance improvement
  • Tests only (no functional changes)
  • Other (please describe):

Changes Made

Core Implementation

  • Modified InsecureStatefulSessionIdManager to track active sessions using sync.Map
  • Added sessions map to store generated session IDs
  • Added terminated map to track terminated sessions
  • Updated Generate() to store session IDs when created
  • Updated Validate() to check session existence in addition to format validation
  • Updated Terminate() to properly mark sessions as terminated and remove from active sessions

Testing

  • Added TestStreamableHTTP_SessionValidation with comprehensive test cases:
    • Reject tool calls with fake but well-formatted session IDs (returns 400)
    • Reject tool calls with malformed session IDs (returns 400)
    • Accept tool calls with valid session IDs from initialization (returns 200)
    • Reject tool calls with terminated session IDs (returns 404)
  • Added TestInsecureStatefulSessionIdManager for unit testing the session manager:
    • Session generation and validation
    • Rejection of non-existent sessions
    • Rejection of malformed session IDs
    • Proper termination handling
    • Concurrent session generation safety
  • Updated TestStreamableHTTPServer_SamplingErrorHandling to use stateless mode for compatibility

Checklist

  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the documentation accordingly

Additional Information

Security Impact

Before this fix, an attacker could invoke tools using any well-formatted session ID like mcp-session-ffffffff-ffff-ffff-ffff-ffffffffffff without going through the initialization flow. This fix ensures that only session IDs that were actually generated by the server are accepted.

Verification

The original exploit from issue #579 now properly returns a 400 Bad Request with "Invalid session ID" error message instead of executing the tool.

All existing tests pass, demonstrating backward compatibility with legitimate use cases.

Summary by CodeRabbit

  • New Features

    • Added configurable stateless mode for the HTTP streaming server.
    • Added stateful session lifecycle support: initialize, validate, and terminate sessions.
  • Behavior Changes

    • Invalid or malformed session IDs now return 400.
    • Terminated or unknown sessions now return 404.
    • Session handling is more reliable and concurrency-safe.
  • Tests

    • Added extensive tests covering session lifecycle, validation, termination, idempotency, and concurrent access.

- Modified InsecureStatefulSessionIdManager to track active sessions using sync.Map
- Added session existence validation in addition to format validation
- Implemented proper session termination tracking
- Added comprehensive regression tests for session validation scenarios
- Updated sampling tests to use stateless mode for compatibility

This fixes a security issue where tools could be invoked with any well-formatted
session ID without going through proper initialization, allowing unauthorized access.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 2, 2025

Walkthrough

Adds stateful session tracking to the streamable HTTP server by giving InsecureStatefulSessionIdManager internal maps for active and terminated sessions, updates Generate/Validate/Terminate behavior, adds a WithStateLess option to NewStreamableHTTPServer, and expands tests to exercise session lifecycle and stateless construction.

Changes

Cohort / File(s) Summary
Session manager & server construction
server/streamable_http.go
InsecureStatefulSessionIdManager now stores sessions and terminated (sync.Map); Generate registers IDs, Validate checks prefix/UUID and active/terminated state, Terminate marks/cleans sessions. NewStreamableHTTPServer accepts options and supports WithStateLess(bool).
Sampling tests (stateless option)
server/streamable_http_sampling_test.go
Tests updated to construct server with WithStateLess(true) and use generic session IDs in test cases.
Session lifecycle tests
server/streamable_http_test.go
New/expanded tests covering invalid/malformed session IDs, valid initialized sessions, termination behavior/404 after termination, InsecureStatefulSessionIdManager behavior (generate/validate/terminate), and concurrency checks.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • pottekkat
  • rwjblue-glean

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The pull request introduces a new stateless configuration option and corresponding API signature changes in streamable_http_sampling_test.go and related server code that are unrelated to the security vulnerability fix for session validation and extend functionality beyond the scope of issue #579. Please remove the stateless mode option and associated API signature changes or isolate them in a separate pull request so that this change focuses solely on enforcing session existence checks as required by issue #579.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title precisely describes the primary change by stating that tool invocations are prevented without valid session initialization, matching the implementation details added to the session manager and server behavior.
Linked Issues Check ✅ Passed The implementation and accompanying tests satisfy the objectives from issue #579 by enforcing session existence checks in Generate, Validate, and Terminate, returning 400 for non-existent or malformed IDs and 404 for terminated sessions, and providing a stateful session manager that aligns with the issue’s requirements.
Description Check ✅ Passed The description adheres to the project’s template by providing a concise overview, linking the fixed issue, specifying the change type, detailing implementation and testing changes, and including a checklist and additional context, with non-applicable sections appropriately omitted.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/session-validation-security

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 61b5d9e and a0f4e56.

📒 Files selected for processing (3)
  • server/streamable_http.go (1 hunks)
  • server/streamable_http_sampling_test.go (3 hunks)
  • server/streamable_http_test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
server/streamable_http_test.go (4)
mcp/tools.go (3)
  • NewTool (679-701)
  • CallToolRequest (54-58)
  • CallToolResult (40-51)
mcp/utils.go (1)
  • NewToolResultText (271-280)
server/streamable_http.go (2)
  • NewTestStreamableHTTPServer (1060-1064)
  • InsecureStatefulSessionIdManager (1021-1024)
server/constants.go (1)
  • HeaderKeySessionID (5-5)
server/streamable_http_sampling_test.go (1)
server/streamable_http.go (2)
  • NewStreamableHTTPServer (152-167)
  • WithStateLess (45-51)

@github-actions
Copy link

github-actions bot commented Oct 6, 2025

Merging this branch will increase overall coverage

Impacted Packages Coverage Δ 🤖
github.com/mark3labs/mcp-go/server 71.79% (+1.22%) 👍

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
github.com/mark3labs/mcp-go/server/streamable_http.go 63.80% (+5.83%) 395 (+12) 252 (+30) 143 (-18) 👍

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

Changed unit test files

  • github.com/mark3labs/mcp-go/server/streamable_http_sampling_test.go
  • github.com/mark3labs/mcp-go/server/streamable_http_test.go

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
server/streamable_http.go (1)

1050-1060: Termination is now idempotent—addresses past review.

The implementation correctly makes termination idempotent by:

  1. Returning success (false, nil) if the session is already terminated
  2. Returning success (false, nil) if the session doesn't exist

This ensures that retry requests won't fail with 500 errors.

Consider adding terminated session cleanup.

The terminated map grows indefinitely as sessions are terminated, which could cause memory issues in long-running servers. Consider implementing periodic cleanup or a TTL-based eviction strategy.

For example, you could add a background cleanup goroutine:

// In NewStreamableHTTPServer or a dedicated initialization function
go func() {
	ticker := time.NewTicker(1 * time.Hour)
	defer ticker.Stop()
	for range ticker.C {
		// Clean up terminated sessions older than 24 hours
		// Would require storing timestamps with terminated entries
	}
}()

Alternatively, consider using a TTL cache library or limiting the terminated map size with an LRU eviction policy.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0f4e56 and 878264a.

📒 Files selected for processing (2)
  • server/streamable_http.go (1 hunks)
  • server/streamable_http_test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
server/streamable_http_test.go (5)
server/server.go (1)
  • NewMCPServer (335-363)
mcp/tools.go (4)
  • NewTool (679-701)
  • WithDescription (722-726)
  • CallToolRequest (54-58)
  • CallToolResult (40-51)
server/streamable_http.go (2)
  • NewTestStreamableHTTPServer (1063-1067)
  • InsecureStatefulSessionIdManager (1021-1024)
client/transport/constants.go (1)
  • HeaderKeySessionID (5-5)
server/constants.go (1)
  • HeaderKeySessionID (5-5)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (5)
server/streamable_http.go (3)

1018-1024: LGTM! Session tracking structure is well-designed.

The use of sync.Map for both sessions and terminated appropriately handles concurrent access patterns. The updated comment accurately describes the manager's behavior.


1028-1032: LGTM! Generation and registration logic is correct.

The method correctly generates a session ID with the expected format and registers it in the active sessions map.


1034-1048: LGTM! Validation logic correctly enforces both format and existence.

The method properly validates session IDs through multiple checks:

  1. Format validation (prefix and UUID)
  2. Termination status (returns isTerminated=true)
  3. Existence in active sessions (returns error if not found)

This addresses the security vulnerability by preventing the use of well-formatted but non-existent session IDs.

server/streamable_http_test.go (2)

1019-1187: LGTM! Comprehensive session validation testing.

The test suite thoroughly validates the security fix by covering all critical scenarios:

  1. Rejection of fake but well-formatted session IDs (400)
  2. Rejection of malformed session IDs (400)
  3. Acceptance of valid session IDs from initialization (200)
  4. Rejection of terminated session IDs (404)

Each subtest properly verifies both status codes and error messages, ensuring the fix works as intended.


1189-1316: LGTM! Excellent unit test coverage for the session manager.

The test suite provides comprehensive coverage of the InsecureStatefulSessionIdManager:

  • Generation and validation of session IDs
  • Rejection of malformed and non-existent IDs
  • Termination marking and validation
  • Idempotent termination for both non-existent and already-terminated sessions
  • Thread-safety with concurrent generation and validation (100 goroutines)

The idempotency tests (lines 1257-1289) are particularly valuable as they verify the fix for the past review comment.

@ezynda3 ezynda3 merged commit 4a76607 into main Oct 6, 2025
5 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.

bug: Tools can be invoked without initialization

2 participants