菜单

是哪份代码检查了你:suite_commit 与 suite_digest

2026-10-12MCP Checkup

本文译自 2026-09-24 的英文原文。

(原文写于 2026-09-24;之后公开包从六个增加到七个:2026-10-06 新增 toolset-diff(逐个工具比较两份工具列表),部署脚本第 3 条要求树相同的「全部公开包」也随之变为七个;另外,自 2026-10-05 起,镜像快照还会带上签名公钥文件的一份副本(keys/mcpcheckup-keys.json)。)

本站的签名结果说明检查对一台 server 观测到了什么。它还得说明是哪些检查。检查会变:这个月(2026 年 9 月),error_taxonomy 检查学会了记录七种不属于规范错误的答复,版本从 0.4.0 升到 0.5.0。上周签名的结果和今天签名的结果出自不同的代码,比较两者的读者需要从结果本身得知这一点,而不是去翻我们的更新日志。

所以从 0.3 版起,每个签名 payload 都带着两个字段。

  • suite_commit 是公开仓库 github.com/mcpcheckup/checks 里的一个 commit。
  • suite_digest 是该 commit 下 packages/checks 目录的 git ls-tree 输出的 SHA-512,以 base64 编码。

自 payload 0.3 起,任何签名结果的证据页都会印出这两个值,并印出由前者复算后者的命令:

git ls-tree -r -z --full-tree <suite_commit> -- packages/checks | openssl dgst -sha512 -binary | openssl base64 -A

在输出前面加上 sha512- 再比对。这条命令只需要一个检出到该 commit 的克隆、git 和 openssl,别的都不需要:不用安装,不用构建,除了克隆之外不用联网。

为什么用目录树清单,而不是一个包

git ls-tree -r 会为 packages/checks 下的每个文件打印它的模式、类型、blob 哈希和路径。对这份清单取哈希,就确定了检查套件里每个源文件的确切字节;而且它只读已提交的对象,所以结果不可能取决于工作区、操作系统、换行符,或装的是哪个版本的 Node。两个人在同一个 commit 上得到的清单逐字节相同。-z 这个参数很要紧:它让路径名保持原始字节,不加引号转义,所以即使某个路径不是合法的 UTF-8,参与哈希的也是它本身。

我们第一次实现这个字段时不是这样做的。在 payload 0.1 和 0.2 版里,suite_digest 的定义是检查套件已发布的 npm 包的完整性哈希。可那个包从来没有发布过。这个值在 2026-08-22 算过一次,贴进探测器的配置文件,然后就一直留在那里,而检查在它底下改了不止一次。按这两个版本签名的结果,按签名时的原样显示摘要,证据页也直说:它指向的不是任何人能取到的目录树,无法复算。我们不会重新签它们。

怎样防止探测器签上过期的值

手贴那个值给我们的教训是:靠人手维护的身份标识会腐烂。现在的值从不写进文件。它在部署时由被部署的 commit 算出,作为两个变量传给 Worker;做这件事的脚本是部署探测器唯一受支持的方式。

在运行任何东西之前,这个脚本要求四件事同时成立,否则拒绝执行:

  1. 工作区是干净的:没有改动过的已跟踪文件,也没有未跟踪文件。被忽略的文件不在检查之列,但 Worker 是从 HEAD 的 git archive 导出构建的,所以它们永远进不了 Worker。
  2. HEAD 是刚刚拉取的私有仓库 main 的最新提交。
  3. 公开仓库的 main 上有一个 commit,它的公开包与 HEAD 的目录树完全相同。是全部六个公开包,不只是摘要覆盖的那一个。
  4. 那个公开 commit 的摘要等于 HEAD 的摘要。

然后它把那个 commit 用 git archive 导出到一个临时目录,从那里构建,而不是从检出的工作区构建;这样,一个被已跟踪文件引用的被忽略文件,就不可能被打进包里,而签名里的身份却指向一个从未包含它的 commit。探测器自己的配置文件里,这两个变量刻意填的是无效的占位值。如果有人绕过脚本部署,探测器会发现这些占位值,干脆拒绝探测,而不是把它们签进结果。

同一个脚本在 CI 里对 main 的每个 commit 都会运行,并把结果作为以该 commit 命名的构建产物上传,摘要旁边附上清单的条目数和字节数。每次部署前,执行部署的人会把脚本打印的值和那个产物比对;我们的部署流程规定,两者不一致就不部署。这个比对是流程中的一步,不是脚本强制执行的。

哪些是公开的,公开仓库是什么

私有仓库里放着网站、探测器和数据库迁移。其中六个包是公开的:检查套件、它的测试夹具(fixtures)、规范化器(canonicalizer)、证明 schema、验证脚本和 SSRF 防护。一个镜像脚本把恰好这六个目录原样(目录树相同)复制到公开仓库;只有评审同意后,才会推送一个带标签的快照。标签形如 snapshot-2026-09-23-a31d17a88c5a。如果一个快照改变了检查套件的目录树,从而改变了摘要,它还会有一个 GitHub Release,说明里写明版本、改了什么,以及新旧两个摘要。第一个这样的 Release 于 2026-09-23 随检查套件 0.5.0 发布。只改了 README 的快照没有 Release。

公开仓库里还有一个 SECURITY.md,提供私下报告漏洞的渠道;以及一个 CONTRIBUTING.md,说明我们接受和不接受哪些贡献。

这些不能给你什么

它告诉你跑的是哪份检查套件代码:那个 commit 下的 packages/checks 目录树。运行它的探测器,以及和它一起安装的包,都不在摘要范围内。它本身也不能让你验证签名。那是另一项核对:公开仓库里的验证脚本,用我们发布在 /.well-known/mcpcheckup-keys.json 的公钥核对签名,证据页印出了做这件事的两条命令。

它也不能告诉你代码是对的。摘要表明,跑的检查套件代码就是那个 commit 下的 packages/checks 目录树。这份代码有没有得出正确的结论,要由代码在公开场合自己回答,这正是它公开的原因。

事实从哪里来

复算命令是在一台装有 git 2.34.1、无法访问我们部署环境的 Linux 机器上,针对上面链接的证据页所写的 commit 运行的,输出与该页的 suite_digest 逐字节一致。标签名、Release 及其日期都在公开仓库上。部署脚本的四项拒绝条件和导出步骤在私有仓库的 scripts 目录里;每一项拒绝都有一个测试,让脚本对着一个用完即弃的仓库运行,并断言 wrangler 从未被调用。