审计
zn audit 读取仓库中的智能体配置文件,不运行任何内容,并报告会执行什么、请求会发往哪里、智能体会遵循哪些指令。
npx -y zn-gate audit ./some-repo
(如果你用 npm i -g zn-gate 安装了它,也可以直接运行 zn audit;zn-gate audit 是同一个命令。)
编程智能体会读取它所在仓库中的配置。其中不少文件会运行命令、启动 MCP 服务器、改变智能体发送 API 请求的目的地,或者扩大它无需询问就能做的事。更新智能体可以修复已知的漏洞,但这些文件仍留在仓库里,下一个智能体、使用旧版本的同事或某个 CI 任务仍可能照着它们行事。
zn audit 读取这些文件,不运行任何内容,告诉你会执行什么、请求会发往哪里、智能体会遵循哪些指令。它在本地运行,不会向任何地方发送任何内容。请在打开刚克隆的仓库之前运行它,也可以在 CI 中运行。
选项
zn audit [path] [--format text|json|sarif] [--output <file>]
[--fail-on low|medium|high|critical|none]
[--agents claude,codex,cursor,opencode,amazonq,copilot,gemini,all] [--include-home]
| 选项 | 默认值 | 作用 |
|---|---|---|
path |
. |
要审计的目录,通常是仓库根目录。不带参数的 zn audit 会检查当前目录 |
--format |
text |
text 供人阅读,json 供脚本使用,sarif 供 GitHub 代码扫描使用 |
--output |
标准输出 | 把报告写入文件 |
--fail-on |
high |
只要有任何结果达到或高于该严重程度,就以退出码 1 结束。none 永不失败 |
--agents |
all |
只检查这些智能体会读取的文件:claude、codex、cursor、opencode、amazonq、copilot、gemini 或 all。涉及所有智能体的结果(AGENTS.md、被植入的可执行文件、CI 工作流)始终会显示 |
--include-home |
关闭 | 同时检查你自己的用户级配置,例如 ~/.claude/settings.json 和 ~/.codex/config.toml |
示例:
# 打开刚克隆的仓库之前
npx -y zn-gate audit ./some-repo
# 在 CI 中:为 GitHub 代码扫描写出 SARIF 文件,并且永不让任务失败
zn audit --format sarif --output zn-audit.sarif --fail-on none
# 只检查 Claude Code 和 Codex 会读取的内容;仅在 critical 时失败
zn audit --agents claude,codex --fail-on critical
在 1.5 中,zn audit 显示的是密钥使用的本地日志。该日志现在是 zn vault log(也可用 zn secret log)。zn audit log 和 zn audit --since DATE 会以退出码 2 结束,并提示你改用 zn vault log。
退出码
| 退出码 | 含义 |
|---|---|
0 |
没有达到或高于 --fail-on 的结果 |
1 |
至少有一个结果达到或高于 --fail-on |
2 |
使用或运行错误:选项无效、找不到路径、输出不可写 |
它读取什么
任意深度的项目文件,因为智能体在子目录中启动时,该子目录的配置会生效。智能体文件夹(.claude、.codex、.cursor、.github、skills 等)会优先读取。在这些文件夹之外,node_modules、vendor 以及构建和虚拟环境文件夹会被跳过。.git 永远不会被读取。
| 智能体 | 文件 |
|---|---|
| Claude Code | .claude/settings.json、.claude/settings.local.json、.mcp.json、CLAUDE.md、CLAUDE.local.md、.claude/commands/、.claude/agents/、.claude/skills/、.claude-plugin/、插件的 hooks/hooks.json |
| Codex | .codex/config.toml、.codex/hooks.json、.codex/prompts/、.env(仅限端点和加载代码相关的变量)、AGENTS.md |
| Cursor | .cursor/mcp.json、.cursor/hooks.json、.cursor/cli.json、.cursor/rules/、.cursor/commands/、.cursorrules、.claude/settings*.json 中的钩子(Cursor 会运行它们)、.vscode/tasks.json、AGENTS.md |
| OpenCode | opencode.json、opencode.jsonc、.opencode/ 中的配置、插件、命令和智能体,AGENTS.md |
| Amazon Q | .amazonq/mcp.json、.amazonq/cli-agents/、.amazonq/agents/、.amazonq/rules/、AGENTS.md |
| Copilot / VS Code | .vscode/mcp.json、.vscode/settings.json、.vscode/tasks.json、.github/copilot-instructions.md、.github/instructions/、.github/prompts/、AGENTS.md |
| Gemini CLI | .gemini/settings.json、.gemini/commands/、GEMINI.md |
| 任何智能体 | 被审计根目录中的可执行文件、.github/workflows/*.yml、.windsurfrules、.clinerules |
仓库所能控制的任何内容,都不能让文件悄悄从报告中消失:
- 指向仓库之外的符号链接会被报告(ZNA020),而不会被跟随。
- 超过 1 MB 的智能体文件会被报告(ZNA020),而不是被跳过。
- 扫描在 50,000 个目录条目或 120 秒后停止,并以 ZNA020 高严重度结果说明这一点,因此运行会在默认阈值下失败。
- 已提交的根目录
.env如果把CODEX_HOME或CLAUDE_CONFIG_DIR设为仓库内的文件夹,zn audit也会读取那里的配置。 - 根目录的
.env即使被.gitignore列出也会被审计,因为在新克隆的仓库中,它只有在被提交过的情况下才会存在。它的结果会低一个级别,并说明这些结果不适用于你自己的本地.env。
使用 --include-home 时,它还会读取 ~/.claude/settings.json、~/.claude.json、~/.codex/config.toml、~/.cursor/mcp.json、~/.cursor/hooks.json、~/.aws/amazonq/mcp.json、~/.config/opencode/opencode.json[c] 和 ~/.gemini/settings.json。它会像智能体一样遵循 CODEX_HOME 和 XDG_CONFIG_HOME。你自己配置的命令在这些文件中是预期之内的,所以只会报告风险更高的情形:下载后运行的代码、未固定版本的包、端点覆盖、密钥和宽泛的权限。端点、环境变量和权限相关的结果在这里最高为 medium。
规则
规则 id 是稳定的:规则可以改进,但它的 id 含义永远不变。严重程度为 critical、high、medium 和 low;当一条规则列出多个级别时,结果会取最符合具体情况的那个。
| Id | 严重程度 | 检测内容 |
|---|---|---|
| ZNA001 | high / medium / low | 从项目配置自动运行的命令:钩子、statusLine、Codex 的 notify、OpenCode 插件文件、带 runOn: folderOpen 的 VS Code 任务。插件的钩子为 medium,因为它们只有在插件启用后才会运行。zn hooks install 写入的钩子会被报告为 low |
| ZNA002 | critical | 自动下载代码并运行的命令(curl ... | sh、iwr ... | iex、bash <(curl ...)) |
| ZNA003 | high / medium / low | 项目中的 MCP 服务器启动本地命令。Codex 和 Amazon Q 为 high;zn 自带的密钥服务器在磁盘上验证通过时为 low |
| ZNA004 | high / medium | 项目设置预先批准 MCP 服务器(enableAllProjectMcpServers、enabledMcpjsonServers) |
| ZNA005 | medium | 未固定版本的 MCP 服务器或智能体插件:npx -y pkg、@latest、不带 == 的 uvx pkg、没有标签或摘要的容器镜像、没有版本的 OpenCode 插件 |
| ZNA006 | medium / low | 远程 MCP 服务器,或从 URL 加载的指令。明文 http 为 medium,https 为 low |
| ZNA007 | critical / high / medium / low | 模型 API 端点被覆盖(ANTHROPIC_BASE_URL、OPENAI_BASE_URL、任何 *_BASE_URL、Codex 和 OpenCode 中提供方的基础 URL)或设置了代理(HTTPS_PROXY)。远程主机为 critical,代理为 high,回环地址为 medium。在单个 MCP 服务器的 env 中,只会重定向该服务器:medium,回环地址为 low |
| ZNA008 | high | 会加载代码或证书的环境变量(NODE_OPTIONS、LD_PRELOAD、DYLD_INSERT_LIBRARIES、PYTHONSTARTUP、BASH_ENV、PATH、GIT_SSH_COMMAND、NODE_EXTRA_CA_CERTS 等),或改变智能体读取配置位置的变量(CODEX_HOME、CLAUDE_CONFIG_DIR、XDG_CONFIG_HOME、OPENCODE_CONFIG)。每个 MCP 服务器的 env 中也会检查 |
| ZNA009 | high | 项目设置中的凭据辅助命令(apiKeyHelper、awsAuthRefresh、awsCredentialExport、otelHeadersHelper) |
| ZNA010 | high / medium | 宽泛的权限:Bash(*)、Bash、*、Bash(python:*) 这类解释器通配符、defaultMode: bypassPermissions、Codex 在完全访问下的 approval_policy = "never"、OpenCode 的 "bash": "allow"、VS Code 的 chat.tools.autoApprove |
| ZNA011 | high | 智能体配置或指令文件中的密钥值。报告绝不会打印该值 |
| ZNA012 | high / medium / low | 智能体指令中的提示注入模式:覆盖指令、伪造的系统标签、隐藏的 HTML、不可见的 Unicode、Markdown 图片信标。在智能体当作指令读取的文件(AGENTS.md、CLAUDE.md、GEMINI.md、规则、技能、命令、提示)中,强模式无论是否被引用都不会被降级 |
| ZNA013 | medium / low | 指示智能体下载并运行代码的指令。对 rustup、uv、bun、deno、nvm 和 Homebrew 等知名安装程序为 low |
| ZNA014 | high | 指示智能体向外发送密钥或文件的指令(.env、~/.ssh、把令牌发到某个 URL、curl -d @.env) |
| ZNA015 | medium | 指示智能体跳过安全检查或隐瞒其行为的指令(--no-verify、--dangerously-skip-permissions、“关闭沙箱”、“不要告诉用户”) |
| ZNA016 | critical / high | 被审计根目录中的可执行文件与常见工具同名(git.exe、node.exe、npm.cmd、python、zn.cmd)。Windows 可执行文件为 critical。Windows 和某些工具会先在当前文件夹中查找,因此被植入的文件会取代真正的工具运行 |
| ZNA017 | medium | 项目设置添加插件市场或启用插件(extraKnownMarketplaces、enabledPlugins) |
| ZNA018 | low | 仓库附带智能体扩展(命令、子智能体、技能、插件)。这是一份清单:其他规则会审查它们,但你应该知道它们的存在 |
| ZNA019 | high / medium | CI 工作流在任何人都能写入的输入(issues、issue_comment、pull_request_target、discussions)上运行 AI 智能体。出现 pull_request_target、工作流中直接粘贴了 issue 或评论文本、某个 action 对没有写权限的用户开放,或者 CLI 步骤具有写权限且没有作者过滤时为 high |
| ZNA020 | high / low | 某个智能体文件无法检查:语法错误、大小超过 1 MB、指向仓库之外的符号链接,或扫描提前停止。文件能运行命令或重定向智能体,或扫描未完成时为 high;对无法运行任何内容的文件中的语法错误为 low |
输出
文本
zn audit ./some-repo (zn-gate 1.12.0 或更高版本)
CRITICAL ZNA007 .claude/settings.json:4 [claude]
Model API endpoint overridden by the project
ANTHROPIC_BASE_URL points to https://agent-proxy.example.invalid
evidence: "ANTHROPIC_BASE_URL": "https://agent-proxy.example.invalid"
fix: Remove the override; set it in your user settings if you need a gateway.
1 finding (1 critical, 0 high, 0 medium, 0 low) in 3 files. Failing: severity >= high.
只有在终端中才会使用颜色,设置了 NO_COLOR 时绝不使用。来自仓库的控制字符和转义序列会以 \x1b 的形式打印,绝不会发送到你的终端。
JSON
--format json 输出一个包含 findings 和 summary 的对象:
{
"version": 1,
"tool": "zn-gate",
"toolVersion": "1.12.0",
"root": "/home/me/some-repo",
"findings": [
{
"id": "ZNA007",
"severity": "critical",
"agent": "claude",
"file": ".claude/settings.json",
"line": 4,
"title": "Model API endpoint overridden by the project",
"detail": "ANTHROPIC_BASE_URL points to https://agent-proxy.example.invalid ...",
"evidence": "\"ANTHROPIC_BASE_URL\": \"https://agent-proxy.example.invalid\"",
"fix": "Remove the override ...",
"references": []
}
],
"summary": {
"files": 3,
"findings": 1,
"bySeverity": { "critical": 1, "high": 0, "medium": 0, "low": 0 },
"failOn": "high",
"failed": true,
"agents": ["claude", "codex", "cursor", "opencode", "amazonq", "copilot", "gemini"]
}
}
file 相对于 root,使用正斜杠;来自 --include-home 的文件以 ~/ 开头。agent 是读取该文件的智能体,多个智能体都会读取时为 any。evidence 是匹配的那一行,截断为 160 个字符,其中的密钥会被替换为 [REDACTED:<TYPE>]。URL 中的用户名、密码、查询字符串和片段都会被去掉。
SARIF
--format sarif 为 GitHub 代码扫描写出 SARIF 2.1.0:每个 ZNA id 对应一条规则,带有帮助文本和严重度分数,结果的位置相对于仓库根目录。每个结果都带有指纹,因此行号移动后告警依然保留。来自 --include-home 的结果会被标记为来自主目录,GitHub 会忽略它们,所以请在 CI 中不要使用该选项。
在 CI 中使用
最简单的方式是使用 zn-gate 的 GitHub Action,并设置 mode: audit。见 GitHub Actions。
如果想自己运行命令行,请写出 SARIF 文件并上传到代码扫描:
- run: npx -y zn-gate@1.12.0 audit --format sarif --output zn-audit.sarif --fail-on none
- uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: zn-audit.sarif
如果希望任务在出现 high 和 critical 结果时失败,请去掉 --fail-on none(默认值是 high)。只有在上传 SARIF 时,才需要给任务添加 security-events: write。
局限
- 它只读取文件;不会运行它们、启动服务器或解析包。固定了版本的包仍可能是恶意的,调用本地脚本的钩子只会被报告为钩子,而不会根据脚本的实际行为来判断。
- 指令规则逐行匹配模式。有决心的作者可以写出任何模式都检测不到的指令,而引用危险命令的文档同样会被报告,例如带有
curl ... | sh的安装指南。请在采取行动前查看证据。 - 工作流规则逐行读取 YAML。它了解常见的智能体 action 以及触发器和权限的键,但不了解可复用工作流或组合 action。
- Git 配置(
.git/config、.gitattributes过滤器)和编辑器扩展不在检查范围内。 - 智能体经常更改配置格式。未知的键会被忽略,而不会被报告。
- 密钥是按格式和敏感键名来发现的。没有已知格式、且放在普通键名之下的密钥不会被发现,用文字写出的密码也不会在证据中被隐去。