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,包管理器默认升级路径不一定会选中它。
一起交流
分享你的思考,让讨论更进一步。