zn-gate 1.12.0 新功能:zn-gate reach、签名发布和一个安全修复
zn-gate 1.12.0 新增 zn-gate reach,一份只读报告,列出你机器上的编程智能体能接触到什么,并为每个发现给出修复它的命令。它还把发布的构建和签名移到一个单独的 workflow 中,让 zn audit 隐藏更多密钥值,并修复了智能体 hook 中影响 1.5.0 至 1.11.0 版本的一个安全问题。用 npm install -g zn-gate@1.12.0 和 pip install -U zn-gate 更新。
zn-gate reach 会显示什么?
它只回答一个问题:这台机器上的编程智能体能接触到什么?在你工作的文件夹中运行:
npx -y zn-gate reach
它在一个屏幕里列出:
- 找到哪些智能体(Claude Code、Codex CLI、Cursor、OpenCode),以及 zn 是否在保护它们;
- 该文件夹中的
.env文件,只显示其中密钥的名称,从不显示值; - 你主目录中的凭据文件,如
~/.aws/credentials、SSH 密钥和~/.npmrc。它只检查这些文件是否存在,从不打开; - 含有密钥的环境变量的名称;
zn audit在该文件夹和你的用户级别的 MCP 服务器与智能体设置中发现的问题。
每个发现都附有修复它的命令。报告是只读的:不显示任何值,不发送任何内容,不启动任何 hook,除非你用 --remember 要求,否则也不写入任何内容。--remember 会把本次运行的计数(高、中、低和日期)保存到 ~/.zn/reach-last.json,这样下次运行可以显示上次的数字。--json 以 JSON 输出同一份报告。完整说明见 reach 文档。
README 和 npm 页面现在展示四步流程:zn-gate reach、zn-gate init、zn-gate check 和 zn-gate env shadow。
能在 CI 中运行 reach 并发送到代码扫描吗?
可以。zn-gate reach --sarif 把报告写成 SARIF 2.1.0。关于被检查文件夹中某个文件的发现是结果:zn audit 的发现保留其规则,智能体可以读取、且含有密钥的 .env 是 ZNR002。属于机器本身的内容(未受保护的智能体、凭据文件、含密钥的环境变量)以及你主目录中的文件,是本次运行的通知。
默认情况下 reach 以代码 0 退出,所以它仍然只是一份报告。当发现该级别或更高级别的问题时,--fail-on high|medium|low 以 1 退出,用法错误或失败以 2 退出。
发布版本的签名方式有什么变化?
有两点,都面向会验证所安装内容的人。
发布版本现在由一个单独的可复用 workflow build-release.yml 构建、证明和签名。发布 workflow 调用其构建和签名任务,但无法访问它们。这使 npm 和 Python 包的构建来源证明达到 SLSA Build Level 3。
发布标签由一个 workflow tag-release.yml 用 gitsign(Sigstore 的无密钥签名)创建并签名,因此签名指向该 workflow,而不指向任何个人的身份。它只从 main 运行,检查该提交是其发布分支的最新提交、所有版本号一致、所有检查都已通过,并在签名前等待批准。从 1.12.0 起,发布 workflow 会拒绝发布没有 tag-release.yml 有效签名的标签。
如果你要验证发布版本,请注意,从 1.12.0 起,GitHub 证明和 Sigstore 签名的签名者 workflow 是 usezn/zn/.github/workflows/build-release.yml(之前的版本为 release.yml)。npm 来源证明和 PyPI 证明仍然指向 release.yml。仓库中的验证页面列出了两者,并说明如何检查标签。
zn audit 现在隐藏什么?
zn audit 会引用发现所在的那一行作为证据。它原本就会替换已知格式的密钥。在 1.12.0 中,它还会隐藏:
- 当某个未知格式的密钥值跟在凭据选项(
--token、--api-key、--password)、认证头(Authorization: Bearer ...、x-api-key: ...)或API_TOKEN=这样的名称之后时,该值; - 在凭据选项之后作为 JSON 数组下一项给出的密钥,例如 MCP 服务器的参数中。
$TOKEN 或 ${API_KEY} 这类引用会保留。早期版本可能在发现的证据中引用这样的值。如果某个发现曾向你显示过这样的值,请轮换该凭据。
还修复了什么?
- 作为 shell 命令发送的长补丁(Codex 中的
apply_patch)重新能在几分之一秒内被拒绝。为了在消息中指出涉及的密钥文件,zn 现在只检查补丁中的前 32 个文件,而不是磁盘上的每一个;对于接近 100 KB 上限的补丁,原来要花几秒钟,在较慢的机器上可能触及智能体对 hook 的超时。决定本身不变。 - 在 Windows 上,已经通过带密钥占位符的
zn-gate run -- ...运行的 PowerShell 命令,不再被错误地拒绝。 zn-gate check和zn hooks install codex现在会告诉你 Codex 不会运行 zn 的 hook,因为它们在requirements.toml、managed_config.toml或你自己的config.toml中被关闭了,或requirements.toml中设置了allow_managed_hooks_only而受管 hook 中没有 zn。Codex 本身不会给出任何提示。
安全修复是什么?
GHSA-rccq-h3xc-j9qj 的严重程度为中等,影响 zn-gate 1.5.0 至 1.11.0。在这些版本中,当命令以间接方式接触受保护文件(如含有值的 .env 文件或凭据文件),或命令非常长、检查很慢时,智能体 hook 的读取防护可能不拒绝对该文件的读取。1.12.0 在做决定之前先解析这些形式,检查链中的每条命令,并拒绝在远低于智能体 hook 超时的时间限制内无法检查完的命令。它还会拒绝保存在 ~/.ssh 之外(例如放在项目中)的私钥。
zn 团队在一次内部审查中发现了该问题。我们在另一篇文章中介绍了这些路径的类别和修复的设计:编程智能体曾如何间接读取你的 .env 文件,以及 1.12.0 如何关闭这些路径。
你该怎么做:
-
更新软件包并重新安装 hook:
npm install -g zn-gate@1.12.0 pip install -U zn-gate npx -y zn-gate@1.12.0 init -
在每个含有带值
.env文件的文件夹中运行zn env shadow .env。被影子化的文件只包含标记,所以通过任何路径读取它都不会泄露任何值。 -
运行
zn-gate check,查看哪些智能体已安装 hook。
如果你使用 Claude Code 插件,待插件目录中列出 1.12.0 后从中更新。长命令链和超时这两种形式在旧版本中没有临时解决办法,所以更新是对它们唯一的修复。
常见问题
zn-gate 1.12.0 有哪些新内容?
zn-gate reach:一份只读报告,列出你机器上的编程智能体能接触到什么,也可输出为用于代码扫描的 SARIF。发布版本在单独的 workflow 中构建和签名(SLSA Build Level 3),发布标签用 gitsign 签名。zn audit 隐藏更多密钥值,智能体 hook 重新能快速拒绝很长的 shell 补丁,并且一个安全修复关闭了 GHSA-rccq-h3xc-j9qj。
如何更新到 zn-gate 1.12.0?
Node 包运行 npm install -g zn-gate@1.12.0,Python 包运行 pip install -U zn-gate。要重新安装智能体 hook,运行 npx -y zn-gate@1.12.0 init。如果你使用 Claude Code 插件,待插件目录中列出 1.12.0 后从中更新。
zn-gate reach 会把我的密钥发到别处吗?
不会。它是只读的:从不显示任何值,不发送任何内容,不启动任何 hook,除非你传入 --remember,否则也不写入任何内容。它会列出 .env 文件中密钥的名称,检查 ~/.aws/credentials 等凭据文件是否存在(不会打开它们),并显示 zn audit 在你的 MCP 服务器和智能体设置中发现的问题。
GHSA-rccq-h3xc-j9qj 影响哪些版本?
zn-gate 1.5.0 至 1.11.0。已在 1.12.0 中修复。该公告的严重程度为中等。
如果我要验证 zn-gate 的发布版本,有什么变化?
从 1.12.0 起,GitHub 证明和 Sigstore 签名的签名者 workflow 是 build-release.yml,而不是 release.yml。仓库中的验证页面同时列出两者,以及检查已签名标签的方法。
完整的变更列表见 1.12.0 发布说明。zn-gate 采用 MIT 许可,在本地运行,无需账号:从 npx -y zn-gate reach 开始,或在安装中查看每个智能体的说明。