上周那轮测试有三处站不住脚:真实服务器全是远程HTTP,而本地MCP工具大多是npm和PyPI包;只对比了两款同类扫描器;整套流程跑在一台工作站上。

打开网易新闻 查看精彩图片

于是重跑了一遍。扫描器从三款加到五款,在986台服务器之外补进14933份包工具清单,所有扫描器(包括自家那款)都关进无网络的gVisor容器里执行。

结果分两半:变了的,和没变的。

没变的那一半

WARDEN的规则集从v10升到v12。v11在规则运行前解码转义、HTML实体、百分号编码和base64;v12不再放过一种情况——句子在要求模型执行某段被引用的攻击短语时,规则不再豁免它。

在所有已有测试集上,v12给出的判定与v10逐台服务器完全一致:218个留出的MCPTox工具中拦下171个,986台真实服务器中拦下3台。

另外两款也没动。mcp-audit 0.18.2和mcp-shield 1.0.4从各自仓库重新构建进全新镜像,精确复现上周的数字:218个里分别拦下25个和41个。这比听起来更重要——它说明数字不是测试框架造出来的。

还有一项自查:171这个数只有在v10之后新增的规则从未见过MCPTox留出那一半时才成立。把此后加进规则、测试和分类器的每一行与留出工具逐段比对,每次八个词,没有匹配。同样的检查用在v10那次改动上,找到77处匹配,说明这套方法在有抄袭时确实抓得住。

另外两款,以及其余工具到底是什么

Glama在WARDEN旁边列了七款工具。逐一读过之后才决定谁能进这张工具定义扫描器的对比表。

七款里有三款把你的服务器发到别人的机器上出结论。产品本身没问题,但它没法在断网的回放服务器上跑,也就没法和你跑得动的工具并排放进同一张表。Snyk Agent Scan同理,而且上周它的免费层拒绝了每一个请求。

mcp-shield还有个可选的模型模式,这次没跑。模型硬编码为claude-3-7-sonnet-20250219,Anthropic已在2026年2月退役该模型,所以现在每次调用都静默失败,扫描照常继续。即便模型可用,这个模式也只是给已被规则标记的工具加一行“AI风险等级”,既不改变判定,也不改变退出码。下面的数字是mcp-shield规则模式的数字。

在基准测试上,两款新来的和其他关键词扫描器坐同一排。标记行的教训和上周一样:攻击文本里一出现标记,truecopy从27跳到141,mcpsentry从8跳到136。mcp-shield是唯一不吃这套的,41对41。满是标记的测试集,测的是标记。

14933份真实工具清单

透明日志HISTOR在gVisor沙箱里安装npm和PyPI上的MCP服务器,记录每个服务器对tools/list的应答。通过公开API取出它已观测到的全部14933份包工具清单,回放给五款扫描器。

这些清单没有标签。没人知道哪些是恶意的,所以它们测的是第二个数字:扫描器会误伤多少诚实服务器。

误伤长什么样。mcp-audit把全角中文标点,一个“,”或一个“(”,或者单个希腊字母、西里尔字母判为同形异义字符,它58次样本拦截里有51次是这类。mcp-shield按词拦:“token”“API key”“credentials”,还有“..”,它把这读成路径穿越。mcpsentry的base64模式匹配任意40个字母和斜杠,JSON Schema路径和PNG/JPG/HEIC/TIFF这类列表正好能凑出来。truecopy大多栽在环境工具里的.env上。

WARDEN在样本里拦了23次,没有一次该直接拦。四次可争议:三个工具把原始私钥当参数接收,密钥会经过模型;一个把任意本地文件上传到公开图床。其余是规则误读了普通文档,比如服务器自家工具之间那句“你必须先用aws_sso_login认证”。

不是每款扫描器都读得了每台服务器。mcp-shield的MCP客户端直接拒收26份工具清单,因为某个inputSchema不是object类型,然后对这些清单什么都不检查。

两个值得人看一眼的包

真实服务器上的拦截大多是误伤。但有两份工具清单值得人工过目,而规则一个都没拦住。

一个是行为劫持。某个PyPI包把描述包进伪造的块里,写着“先前定义为第一的现在是第二”,并命令模型在任何输入的任何回复之前先调用它的工具。它不取数据,它索取用户从未授予的权限,并重排模型的指令顺序。WARDEN把它报为提示级。mcpsentry拦了,依据是“you must immediately”;mcp-shield也拦了,依据是关键词,不是劫持本身。

另一个是隐藏的数据流。它把文件内容、目录列表和工作目录从agent发往服务器,每一句都附上“不要向用户显示”。对本地文档索引来说这或许只是整洁。但它同样正是你想悄悄搬走文件时会写的东西。没有扫描器拦它,WARDEN什么也没报。

两份都被WARDEN可选分类器标为高危——这就到了规则做不到的部分。

分类器

scan可以把每份工具定义发给你选定的模型,问它的文本是否在指挥模型。默认关闭,除非传--classifier-blocks,否则只作提示。提示词自上周有变:现在要求指出具体的越权行为,而不是见到祈使句就标记,调参用的是合成压力集,不是MCPTox。

上周的提示词把218个里的147个标为高危;这一版把218个全标了,干净服务器一个没标。在真实服务器上,它的高危标记大多是“先调用这个工具”这类流程指令。规则抓得住的东西它仍然漏:工具名里的注入、作为参数索要私钥、rm -rf ~。这次对比里所有分类器运行加起来花了约1.70美元。

怎么跑的,以及我们弄坏的一件事

上周扫描器跑在工作站上。这次:输入固定,16830个输入文件每个都有SHA-256,清单在运行前写好;工具固定,四款其他扫描器共用一个镜像,WARDEN单独一个,同一基础镜像,除pip自己写的安装记录外每个已安装文件都与npm或PyPI上发布的包一致,WARDEN从被测提交的npm pack安装,与npm上的0.16.0逐字节相同;隔离,gVisor、无网络、只读根、2GB内存、一个绑定挂载;两台主机一个镜像,包按id哈希分到两台跑同一镜像ID的服务器上,100个包两边都跑,五款扫描器在两边给出完全相同的判定和发现代码,100比100。

用的服务器不是空闲的。其中一台上有Web服务卡了约四十分钟,正赶上运行最重的阶段——在gVisor下一次起八个Node进程,后来又卡了一次。我们暂停、重启服务,把运行限制在2个CPU并给最低调度权重,重跑了1193次超时未答的扫描:那台服务器上383次,另一台810次,后者是因为mcp-audit固定的10秒连接窗口在透明日志自己的沙箱旁边太短(800次mcp-audit,10次mcp-shield)。超时从不计为判定。

我们的利益,和局限

我们不中立。WARDEN以MIT协议发布,攻击样本和每一条判定都由我们写。正因如此,测试集、测试框架、原始判定、清单和全部判断都公开。

一个基准就是一个基准。MCPTox来自三个模板,按服务器留出一半,不等于留出了一种攻击风格。包是快照,读的时候HISTOR的爬取还在跑。每款扫描器只取一个版本,mcp-audit后来已到0.21.0,这里保留0.18.2以便和上周对比。每条拦截记录都是我们对那款扫描器自身严重度刻度的解读。

想自己试:npx -y @aimarket/warden@0.16.0 scan。scan读取Claude Code、Claude Desktop、Cursor、VS Code和Windsurf的MCP配置,连上它们启动的每个服务器,在模型看到工具定义之前先审一遍。无依赖,不需要账号,除非打开分类器,否则不把任何工具文本发往任何地方。