-
Notifications
You must be signed in to change notification settings - Fork 746
fix: prevent tools invocation without valid session initialization #607
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- 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.
WalkthroughImplements stateful session tracking in the HTTP server by adding internal maps for active and terminated sessions to InsecureStatefulSessionIdManager, updating Generate/Validate/Terminate logic. Updates server construction to accept a WithStateLess option. Expands tests to cover session lifecycle validation, termination behavior, and stateless construction in sampling tests. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate 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. Comment |
Merging this branch will increase overall coverage
Coverage by fileChanged files (no unit tests)
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
|
There was a problem hiding this 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
📒 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)
func (s *InsecureStatefulSessionIdManager) Terminate(sessionID string) (isNotAllowed bool, err error) { | ||
if _, exists := s.sessions.Load(sessionID); !exists { | ||
return false, fmt.Errorf("session not found: %s", sessionID) | ||
} | ||
s.terminated.Store(sessionID, true) | ||
s.sessions.Delete(sessionID) | ||
return false, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make session termination idempotent
Terminate
now returns an error once the session has been deleted from sessions
, so a client that retries DELETE /...
after succeeding immediately hits a 500 (handleDelete turns this error into Session termination failed
). Termination endpoints must be idempotent—network retries shouldn’t look like server failures. Please allow already-terminated IDs to short‑circuit as success (or map them to a 404) before returning an error.
func (s *InsecureStatefulSessionIdManager) Terminate(sessionID string) (isNotAllowed bool, err error) {
- if _, exists := s.sessions.Load(sessionID); !exists {
- return false, fmt.Errorf("session not found: %s", sessionID)
- }
- s.terminated.Store(sessionID, true)
- s.sessions.Delete(sessionID)
+ if _, exists := s.sessions.Load(sessionID); !exists {
+ if _, terminated := s.terminated.Load(sessionID); terminated {
+ return false, nil
+ }
+ return false, fmt.Errorf("session not found: %s", sessionID)
+ }
+ s.terminated.Store(sessionID, true)
+ s.sessions.Delete(sessionID)
return false, nil
}
🤖 Prompt for AI Agents
In server/streamable_http.go around lines 1050–1056, Terminate currently returns
an error when the session ID is not found in s.sessions, causing retries to
surface as server failures; change the logic to make termination idempotent by
first checking whether the session is already marked terminated (or if not
found, consult s.terminated) and if so return (false, nil) to indicate success,
otherwise mark terminated and delete the session and return (false, nil); ensure
you do not treat a missing sessions entry as an internal error when the session
is already terminated.
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
Changes Made
Core Implementation
InsecureStatefulSessionIdManager
to track active sessions usingsync.Map
sessions
map to store generated session IDsterminated
map to track terminated sessionsGenerate()
to store session IDs when createdValidate()
to check session existence in addition to format validationTerminate()
to properly mark sessions as terminated and remove from active sessionsTesting
TestStreamableHTTP_SessionValidation
with comprehensive test cases:TestInsecureStatefulSessionIdManager
for unit testing the session manager:TestStreamableHTTPServer_SamplingErrorHandling
to use stateless mode for compatibilityChecklist
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