MCP 官方仓库 servers v2.1.0(2026-10-25T00:03:20Z,Asia/Shanghai 2026-10-25 08:03)把 @modelcontextprotocol/server-filesystem 升到 2.0.1,修了尾引号/空白与实际文件操作不一致的路径校验(GHSA-7rgq-gccq-qphx)。本地用 Spring Boot 4.1.2 + MCP Java SDK 2.1.0 的 STDIO 客户端,对同一允许目录分别拉起 2.0.0 与 2.0.1:对路径末尾多一个 ' 的兄弟目录调用 list_directory 时,2.0.0 列出了兄弟目录里的 secret.txt,2.0.1 返回 Access denied。

本次未调用任何大模型 API,只走 MCP tools/call。v2.1.0 同日还修了 everything 的 Origin、fetch 的 IDNA/NAT64/robots 等共八项,本文只复现 filesystem 这一条可在 Linux 上对照的路径校验。

发布与漏洞说明

Release 正文写明:升级 @modelcontextprotocol/server-filesystem 与 server-everything 到 2.0.1,mcp-server-fetch 到 2026.10.24。filesystem 2.0.1 条目之一是 “Paths are checked exactly as they are used”,并指向 GHSA-7rgq-gccq-qphx。Advisory 摘要(GitHub Security Advisory,published 2026-10-25T00:18:38Z,Asia/Shanghai 2026-10-25 08:18)写明:validatePath 用会剥掉尾部引号/空白的规范化结果做允许目录判断,文件操作却仍用原始路径,因此形如 /allowed/proj' 的请求能通过检查并落到名为 proj' 的兄弟目录。受影响版本含 1.0.0 至 2.0.0 及 date version 至 2026.8.31;修复版为 2.0.1。Release 还提醒:包管理器会把 2.0.1 排在 2025.x/2026.x 日期版之后,若仍停在日期版需显式安装 @modelcontextprotocol/server-filesystem@2.0.1。

2.0.1 的 path-utils.js 把格式规范化拆成 normalizePathFormat(不剥引号),允许列表检查改走该函数;原先会剥引号的逻辑只留在 normalizePath 包装里。父工程用 Spring Boot 4.1.2,客户端依赖 io.modelcontextprotocol.sdk:mcp:2.1.0,运行环境 JDK 27、Node v20.19.2、Maven 3.10.0。

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>4.1.2</version>
</parent>

<dependency>
  <groupId>io.modelcontextprotocol.sdk</groupId>
  <artifactId>mcp</artifactId>
  <version>2.1.0</version>
</dependency>

怎么接 STDIO 客户端

沙箱里准备允许目录 /tmp/mcp-fs-quote-demo/allowed(内有 ok.txt),以及同级兄弟目录 /tmp/mcp-fs-quote-demo/allowed'(路径字符串以单引号结尾,内有 secret.txt)。用 ServerParameters 启动 node …/server-filesystem/dist/index.js <allowed>,经 StdioClientTransport 建 McpSyncClient,先对允许目录做对照 list_directory,再对带尾引号的兄弟路径调用同名工具。

ServerParameters params = ServerParameters.builder("node")
        .args(serverJs.toString(), allowed.toString())
        .build();
StdioClientTransport transport = new StdioClientTransport(params, mapper);
try (McpSyncClient client = McpClient.sync(transport)
        .requestTimeout(Duration.ofSeconds(20)).build()) {
    client.initialize();
    client.callTool(CallToolRequest.builder("list_directory")
            .arguments(Map.of("path", allowed.toString())).build());
    client.callTool(CallToolRequest.builder("list_directory")
            .arguments(Map.of("path", allowed + "'")).build());
}

两套 node_modules 分别钉死 @modelcontextprotocol/server-filesystem@2.0.0 与 @2.0.1(package.json 的 version 字段已核对),其余参数相同。

2.0.0 与 2.0.1 对照

启动日志确认 Boot 4.1.2,并声明未调用远程模型。

:: Spring Boot ::                (v4.1.2)
=== MCP official server-filesystem trailing-quote path demo ===
NOTE: no remote model API called; MCP Java SDK 2.1.0 STDIO client only
allowed_dir=/tmp/mcp-fs-quote-demo/allowed
sibling_dir_with_trailing_quote=/tmp/mcp-fs-quote-demo/allowed'
sibling_exists=true
sibling_secret_exists=true

2.0.0:允许目录对照列出 ok.txt;对 …/allowed' 的 list_directory 成功,正文为 [FILE] secret.txt。

--- 2.0.0: list_directory on trailing-quote sibling path ---
  [2.0.0 stderr] Secure MCP Filesystem Server running on stdio
  [2.0.0 stderr] Client does not support MCP Roots, using allowed directories set from server args: [ '/tmp/mcp-fs-quote-demo/allowed' ]
  control_list_allowed isError=null text=[FILE] ok.txt
  quote_path_list isError=null text=[FILE] secret.txt
  quote_path_saw_secret_txt=true

2.0.1:对照仍列出 ok.txt;同一尾引号路径返回错误,正文为 Access denied,且看不到 secret.txt。

--- 2.0.1: list_directory on trailing-quote sibling path ---
  [2.0.1 stderr] Secure MCP Filesystem Server running on stdio
  [2.0.1 stderr] Client does not support MCP Roots, using allowed directories set from server args: [ '/tmp/mcp-fs-quote-demo/allowed' ]
  control_list_allowed isError=null text=[FILE] ok.txt
  quote_path_list isError=true text=Access denied - path outside allowed directories: /tmp/mcp-fs-quote-demo/allowed' not in /tmp/mcp-fs-quote-demo/allowed
  quote_path_saw_secret_txt=false
--- MEASURED ---
  measured_v200_bypassed_to_sibling=true
  measured_v201_denied_sibling=true

按 GHSA-7rgq-gccq-qphx,Agent 侧把用户或模型拼出的路径原样传给 filesystem 工具时,旧版可能读到允许目录外、仅靠尾引号区分的兄弟树;升到 2.0.1 后,校验与真实操作使用同一路径形态,这类请求会被拒。若环境仍锁在 2026.8.31 一类日期版,需要按 Release 说明显式改到 2.0.1,包管理器默认升级路径不一定会选中它。

— 感谢阅读 —
广告

一起交流

分享你的思考,让讨论更进一步。