test_safe_join_rejects_symlink_escape fails on Windows without elevation or Developer Mode

这个报错通常出现在 Windows 普通(非管理员、未开启开发者模式)用户首次运行 MCP Python SDK 测试套件时,具体失败于 test_safe_join_rejects_symlink_escape 。它并不是你的代码或安全实现有问题,而是测试本身无条件创建符号链接,而 Windows

快速结论:这个报错通常出现在 Windows 普通(非管理员、未开启开发者模式)用户首次运行 MCP Python SDK 测试套件时,具体失败于 test_safe_join_rejects_symlink_escape。它并不是你的代码或安全实现有问题,而是测试本身无条件创建符号链接,而 Windows 默认不允许普通用户创建符号链接,所以应优先排查测试环境权限,而非修改 src/mcp/shared/path_security.py。

适用环境:已在 Windows 11(10.0.26200)上复现;Python 3.14.7 与 Python 3.12.10 均有相关记录;mcp 2.0.1.dev23+56af447、mcp-types 2.0.1.dev23+56af447;pytest 8.4.2;使用 uv sync 与 uv run pytest。Issue 未确认 CUDA、显卡或 PyTorch 相关环境,不涉及这些项目。

最快修复方案:暂无确认的一步修复方案。Issue 中给出的处理方向是修改测试文件,让符号链接创建失败时跳过测试而非直接失败:将 tests/shared/test_path_security.py:145 的 symlink_to() 包进 try/except OSError,捕获后调用 pytest.skip()。该方案作者已在本地验证,套件从 5791 passed / 1 failed 变为 5790 passed / 0 failed,但截至 Issue 关闭尚无合并的 PR 证据。

注意事项:跳过该测试会让该机器上失去符号链接逃逸覆盖;Issue 评论建议另外增加一个无需提权的 Windows 目录 junction(mklink /J)测试来补足这一点,但该补充测试同样属于建议,并非已验证合并的方案。不要为了跑通测试而盲目以管理员身份运行全套件,也不要把这个失败误判为 safe_join 运行时实现存在缺陷。

问题场景

在 Windows 上以普通非提权用户身份克隆 modelcontextprotocol/python-sdk 并运行测试套件时触发。典型操作为:git clone 仓库、uv sync 安装依赖,然后执行 uv run pytest tests/shared/test_path_security.py,或运行完整套件。失败只出现在本地普通 Windows 机器,项目 CI 的 Windows 任务反而是绿的,因此该测试呈现“CI 永远通过、本地永远报错”的状态。Issue 报告者统计:其余 5791 个测试通过,仅此 1 个失败,16 个跳过。

报错原文

tests\shared\test_path_security.py:145: in test_safe_join_rejects_symlink_escape
    (sandbox / "escape").symlink_to(outside)
E   OSError: [WinError 1314] A required privilege is not held by the client

原因分析

最可能的原因是测试在 tests/shared/test_path_security.py:145 无条件创建符号链接,而 Windows 只允许提权进程,或开启了 Developer Mode 的普通用户创建符号链接,这两者都不是 Windows 机器的默认状态。异常在测试 setup 阶段、进入 safe_join() 之前就已抛出,因此失败点并非被测试的安全逻辑,而是测试的前置环境假设。

CI 之所以没有暴露该问题,是因为 .github/workflows/shared.yml 中的 Windows runner 明显具备创建符号链接的权限,所以该测试在 CI 上总能通过。Issue 评论进一步指出:WinError 1314 在 Python 中映射为普通 OSError,并不属于 FileExistsError、PermissionError 那类子类,因此按 .winerror 做窄类型捕获既不可移植也很脆弱。另一条评论补充了一个覆盖盲点:Windows 上还有无需提权的目录 junction(mklink /J),而 Path.is_symlink() 看不到它,所以若只是跳过符号链接测试,在这些机器上会完全没有逃逸覆盖;不过 safe_join 经由 Path.resolve() 已能正确处理 junction,这属于覆盖缺口而非运行时缺陷。

环境排查

  • 确认操作系统是否为 Windows,以及当前进程是否提权、系统是否开启 Developer Mode。
  • 确认是否使用 uv sync 与 uv run pytest 运行测试。
  • 确认触发文件是否为 tests/shared/test_path_security.py,失败行是否为第 145 行附近的 symlink_to() 调用。
  • 确认 mcp 与 mcp-types 版本(Issue 中为 2.0.1.dev23+56af447,属 2.x 线)。
  • 确认 pytest 版本(Issue 中为 8.4.2)。
  • 确认 Python 版本(Issue 中出现 3.14.7 与 3.12.10)。
  • Issue 未涉及 CUDA、PyTorch、显卡或额外自定义节点,无需排查这些项。

解决步骤

  1. 先单独运行触发文件,确认失败是权限导致的符号链接创建失败:uv run pytest tests/shared/test_path_security.py,查看报错是否包含 [WinError 1314]。
  2. 若确认是符号链接权限问题,可优先尝试的修复是修改测试文件本身:将 tests/shared/test_path_security.py 中第 145 行的 (sandbox / "escape").symlink_to(outside) 用 try/except OSError 包裹,在 except 分支调用 pytest.skip(f"symlink creation is not permitted here: {exc}")。
  3. 保留原有的 PathEscapeError 断言,仅包裹 symlink_to(),使符号链接可用的环境(包括现有 CI)仍完整执行逃逸断言。
  4. 按 Issue 评论意见,捕获范围应使用宽泛的 OSError,不要仅捕获特定权限错误,因为 WinError 1314 没有对应的窄类型。
  5. (可选,覆盖增强建议)可额外增加一个在 Windows 上使用 cmd /c mklink /J 创建目录 junction 的兄弟测试,并用 @pytest.mark.skipif(sys.platform != "win32", ...) 限定平台;若 mklink 返回非零码则跳过。该方案在 Issue 中属于建议,未经合并验证。
  6. 无需修改 src/mcp/shared/path_security.py;Issue 双方均确认运行时实现不需要改动。

验证方法

在 Windows 普通非提权机器上重新运行 uv run pytest tests/shared/test_path_security.py:修复后 test_safe_join_rejects_symlink_escape 应显示为 skipped,且跳过原因是符号链接创建不被允许,而不是通过 pytest.mark.skip 之类方式被无条件屏蔽。运行完整套件时应看到 Issue 作者验证的结果(5790 passed / 0 failed),而不是原来的 5791 passed / 1 failed。若增加了 junction 补充测试,应确认该测试在 Windows 上实际执行并断言 safe_join 抛出 PathEscapeError,而非被错误跳过。

参考来源

modelcontextprotocol/python-sdk #3408

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 26753

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注