fix: enforce toolset/promptset boundary on tools/call and prompts/get#3036
fix: enforce toolset/promptset boundary on tools/call and prompts/get#3036sjhddh wants to merge 1 commit intogoogleapis:mainfrom
Conversation
tools/call and prompts/get resolved tools and prompts via the global resourceMgr without verifying membership in the current toolset or promptset. This allowed clients connected to a scoped toolset to invoke any tool by name, bypassing toolset access boundaries (IDOR). Add ContainsTool/ContainsPrompt membership checks and enforce them in all four MCP protocol versions before delegating to resourceMgr. Fixes googleapis#2755 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
There was a problem hiding this comment.
Code Review
This pull request introduces ContainsPrompt and ContainsTool methods to the Promptset and Toolset structs, respectively, along with corresponding unit tests. These methods are used to verify that a requested prompt or tool belongs to the current set before global resolution in the MCP handlers. The review feedback points out that the current linear search implementation ($O(N)$) for these lookups could become a performance bottleneck as the number of items grows, suggesting a map-based approach for
| func (p Promptset) ContainsPrompt(name string) bool { | ||
| for _, n := range p.PromptNames { | ||
| if n == name { | ||
| return true | ||
| } | ||
| } | ||
| return false | ||
| } |
There was a problem hiding this comment.
The ContainsPrompt method uses a linear search ($O(N)$), which is executed on every prompts/get request. While likely acceptable for small promptsets, this could impact performance as the number of prompts grows. Consider using a map for Initialize method.
| func (t Toolset) ContainsTool(name string) bool { | ||
| for _, n := range t.ToolNames { | ||
| if n == name { | ||
| return true | ||
| } | ||
| } | ||
| return false | ||
| } |
Summary
tools/callpreviously resolved tools viaresourceMgr.GetTool()without verifying the requested tool belongs to the current toolset, allowing clients to invoke any tool by name regardless of their toolset scope.prompts/getwithresourceMgr.GetPrompt().ContainsTool()/ContainsPrompt()membership checks and enforce them in all four MCP protocol versions (v20241105, v20250326, v20250618, v20251125) before delegating to the global resource manager.Fixes #2755
Test plan
Toolset.ContainsTool()andPromptset.ContainsPrompt()(both positive and negative cases, including empty sets)go test ./internal/tools/... ./internal/prompts/... ./internal/server/mcp/...)tools/callrejects tools from the other toolset🤖 Generated with Claude Code