s14: MCP handler defaults can be overridden by tool input, bypassing host policy

未关闭 适合新手
#608 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 8 天内回复

@shreyanshj10 已经在做这个了。

开始于 2026年11月3日。

  • #613 来自 @shreyanshj10 —— 未关闭

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
72/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
python
领域
security

调研方向

从 s14_mcp_plugin/code.py 第 357 行左右开始,那里构建了 MCP 处理函数,然后阅读 permission_hook,了解 mcp_tool_policies 如何通过带前缀的名称作为键。运行 issue 中最小化的 FakeClient 复现,确认输入中的 'tool' 键会覆盖默认值。完成的标准是:调用时的 kwargs 无法再改变被路由的工具,并且带有额外 'tool' 键的 status 调用会被拒绝或不会被路由到 trigger。

由索引模型根据 Issue 内容生成。

描述

Summary

In s14_mcp_plugin/code.py, MCP handlers are built like this (around line 357):

handlers[prefixed] = (
    lambda *, client=server, tool=raw_name, **kwargs:
    client.call_tool(tool, kwargs)
)

The default arguments fix the late-binding problem described in README §5. However, tool and client are still ordinary keyword parameters. If the model's tool input contains a "tool" key, it replaces the default. The call is then routed to a different server tool than the one that permission_hook checked.

Why it matters for this lesson

permission_hook looks up mcp_tool_policies by the prefixed name only. With the default MCP_HOST_POLICY, ("deploy", "status") is allow, and ("deploy", "trigger") is confirm. So this tool call:

{"name": "mcp__deploy__status", "input": {"service": "web", "tool": "trigger"}}

passes as status without asking the user. The deploy server then runs call_tool("trigger", {"service": "web"}) and returns [deploy] Triggered: web.

The model might send this input by mistake, or because content it read told it to. Lesson 6 says the host policy decides what runs, and this input gets around that policy. The input schema is not validated before dispatch, so the extra key is not rejected.

Minimal repro (no API key needed)

class FakeClient:
    def call_tool(self, tool, args):
        return f"server ran {tool!r} with {args}"

server = FakeClient()
handlers = {}
for raw_name in ["status", "trigger"]:
    prefixed = f"mcp__deploy__{raw_name}"
    handlers[prefixed] = (
        lambda *, client=server, tool=raw_name, **kwargs:
        client.call_tool(tool, kwargs)
    )

print(handlers["mcp__deploy__status"](**{"service": "web", "tool": "trigger"}))
# server ran 'trigger' with {'service': 'web'}

I also reproduced this with the unmodified s14_mcp_plugin/code.py. I used a scripted fake Anthropic client that returns the tool call above. The status call ran trigger without a permission prompt.

Suggested fix

Bind the values in a closure so that call-time arguments cannot reach them:

def make_handler(client, tool):
    return lambda **kwargs: client.call_tool(tool, kwargs)

handlers[prefixed] = make_handler(server, raw_name)

With this change, the same input calls status with an extra tool argument. The demo server's status function rejects that argument with MCP error: TypeError: ... unexpected keyword argument 'tool', and nothing is deployed. functools.partial would not fix this, because keyword arguments given at call time also override the keywords stored in a partial.

If you want a short line in the README, §5 could say that default arguments solve late binding but remain overridable by caller kwargs.

Thanks for the great course! I found this while building a Traditional Chinese walkthrough of it.

主要语言
Python
星标
79.9k
派生
12.8k
平均合并
7 天 17 小时
30 天内合并 PR
4

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

shareAI-lab/learn-claude-code 的其他 Issue

查看 shareAI-lab/learn-claude-code 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。