code-of-kai/vet
GitHub: code-of-kai/vet
Elixir 依赖的 AST 静态扫描工具,在编译前检测供应链攻击与拼写欺骗包。
Stars: 13 | Forks: 1
# Vet
Elixir 依赖项的静态分析工具。在依赖项代码到达生产环境之前,扫描项目依赖代码中的供应链攻击指标——凭据窃取、代码执行、数据泄露、混淆等。
## 为什么
你的依赖项在与你的应用程序相同的 BEAM 上运行。一个被破坏的包可以在编译时调用 `System.get_env("AWS_SECRET_ACCESS_KEY")`,将其通过 HTTP 泄露出去,而你永远不会察觉。Elixir 生态系统信任其包,而这种信任在很大程度上是值得的。但信任无法随着现代项目中传递依赖项的数量而扩展。
Vet 遍历 lock 文件中每个依赖项的 AST,并标记在库中没有合法理由出现的模式:编译时系统命令、敏感密钥的环境变量访问、指向可疑端点的网络调用、混淆负载等。
它还解决了一个更新的问题:大型语言模型会以可衡量的频率“产生幻觉”包名。攻击者已经开始在软件包注册表中注册这些幻觉名称并植入恶意载荷——一种称为“slopsquatting”的技术。Vet 会在你安装之前检测这些幻影包和拼写欺骗包。
## 安装
在项目的依赖项中添加 `vet_cli`:
```
def deps do
[
{:vet_cli, "~> 0.1", only: :dev, runtime: false}
]
end
```
## 用法
### 完整依赖扫描
```
mix vet
```
这会解析你的 `mix.lock`,遍历每个依赖项的源代码,并报告带有风险分数的发现结果。依赖项根据发现的严重性和包元数据按 0 到 100 进行评分。
选项:
* `--path`, `-p` — 项目路径(默认为当前目录)
* `--format`, `-f` — 输出格式:`terminal`(默认)、`json`、`diagnostics`
* `--threshold`, `-t` — 如果任何依赖项的风险分数达到或超过此值,则以错误退出(默认:50)
* `--skip-hex` — 跳过 hex.pm 元数据检查(适用于离线或无网络的 CI)
* `--no-diff` — 跳过自动版本差异比较(更快的扫描,不获取旧版本)
* `--verbose`, `-v` — 详细输出
### 安装前检查
```
mix vet.check
```
在运行 `mix deps.get` *之前* 执行此操作。它直接读取你的 `mix.exs` —— 不需要 lock 文件或已获取的依赖项 —— 并检查每个声明的依赖项与 hex.pm 的匹配情况:
* 该包是否存在?(幻影包检测)
* 名称是否与流行包过于相似?(拼写欺骗/Slopsquatting 检测)
* 是否是最近发布且采用率极低的包?(元数据信号)
如果某个依赖项在 hex.pm 上不存在,该任务会以错误退出。
### 版本差异比较
Vet 会自动将每个依赖项与 Hex 上的上一个版本进行比较。Hex 会永久保留每个已发布的版本,因此无论你之前安装了什么版本,这都能正常工作。
如果版本变更引入了可疑模式(测试文件以外的新文件、增加的安全发现,或包的安全概况变化),Vet 会将其标记为 `[VERSION DIFF]` 发现。这些发现会绕过允许列表。允许列表表示“我们信任该包的现有行为”。版本差异则表示“行为已改变”。
这就是 Vet 如何捕获对允许列表中包的恶意更新。如果一个受信任的依赖项推送了新增编译时凭据窃取的新版本,与上一版本的差异会暴露该问题,即使该包已在允许列表中。
使用 `--no-diff` 可禁用以加快离线扫描速度。
## 检查内容
Vet 通过 AST 分析对每个依赖项的源代码运行 10 项检查:
| 检查 | 类别 | 检测内容 |
|---|---|---|
| `SystemExec` | `:system_exec` | `System.cmd`、`System.shell`、`:os.cmd`、`Port.open` |
| `CodeEval` | `:code_eval` | `Code.eval_string`、`Code.compile_string`、`:erlang.binary_to_term`、`Module.create` |
| `EExEval` | `:code_eval` | `EEx.eval_string`、`EEx.eval_file`、`EEx.compile_string` |
| `CompilerHooks` | `:compiler_hooks` | `@before_compile`、`@after_compile`、mix.exs 中的自定义编译器 |
| `EnvAccess` | `:env_access` | `System.get_env` —— 关键敏感变量(`SECRET`、`KEY`、`TOKEN`、`AWS_*`、`DATABASE_URL`) |
| `FileAccess` | `:file_access` | `File` 操作 —— 关键敏感路径(`~/.ssh`、`~/.aws`、`/etc/passwd`) |
| `NetworkAccess` | `:network_access` | `:httpc.request`、`:gen_tcp.connect`、`Req`、`HTTPoison`、`Finch`、`Mint.HTTP` |
| `Obfuscation` | `:obfuscation` | 与 eval 搭配的 Base64 解码、高熵字符串(>5.5 香农熵)、动态 `apply/3` |
| `ShadyLinks` | `:shady_links` | 可疑 TLD、隧道服务(ngrok、serveo)、外泄端点(pastebin、Telegram、Discord Webhook) |
| `AtomExhaustion` | `:dos_atom_exhaustion` | `String.to_atom`、`:erlang.binary_to_atom` —— 通过原子表耗尽导致 DoS |
每个发现都会区分编译时和运行时上下文。编译时发现的评分显著更高,因为它们在 `mix deps.compile` 期间执行——在你的应用程序代码运行之前。
## 评分机制
每个依赖项会获得一个 0–100 的风险分数,综合代码发现结果和包元数据:
**发现结果** — 编译时关键:+40,编译时警告:+20,运行期关键:+15,运行期警告:+5,信息:+1。
**元数据** — 非 Hex 源(Git/路径):+10,下载量 <100:+20,下载量 <1000:+10,最近 7 天发布:+15,单一所有者:+5,无描述:+5,依赖深度 3-4:+5,深度 5+:+10。
**流行度调整** — 下载量 >1000 万:得分 ×0.3,>100 万:得分 ×0.5。广泛采用的包不太可能是恶意的;其发现结果通常是框架的正常模式。
**风险等级** — 危急(>=80),高(>=50),中(>=20),低(<20)。
## 允许列表
许多合法的库会触发发现结果。Phoenix 使用 `@before_compile`,Ecto 运行 `Code.eval_quoted` 进行查询编译,Rustler 执行系统命令来构建原生代码。Vet 自带一个内置的允许列表,涵盖 Phoenix 1.7+ 生态系统和常见 Elixir 包(约 100 条抑制规则)。
你可以通过项目根目录下的 `.vet.exs` 文件扩展它:
```
%{
allow: [
{:my_package, :system_exec, "Runs native build toolchain"},
{:another_package, :network_access, "Fetches remote config at compile time"}
]
}
```
## AI 代理集成
Vet 附带一个 `AGENTS.md` 文件,告知 AI 编码助手如何使用其函数。如果你的项目使用 [Tidewave](https://github.com/tidewave-ai/tidewave_phoenix),代理可以通过 `project_eval` 调用 Vet 的函数,且无需任何配置。
代理的关键函数:
```
VetCore.PreInstallCheck.check_package(:some_package)
```
该函数会检查一个包在 hex.pm 上是否存在,检测拼写欺骗和 Slopsquatting 目标,并返回元数据信号 —— 全部在包安装之前完成。一个在建议依赖项之前调用此功能的 AI 助手可以在推荐阶段拦截 Slopsquatting,而不是在安装之后。
通过 `project_eval` 可用的其他函数:
* `VetCore.scan(path)` — 完整项目扫描,包含风险分数和发现结果
* `VetCore.VersionDiff.diff(path, :pkg, "1.0.0", "1.1.0")` — 比较包版本以发现可疑变更
* `VetCore.PreInstallCheck.check_deps(path)` — 一次性检查所有 mix.exs 依赖项
请参阅 `AGENTS.md` 获取完整用法。
## 架构
Vet 的结构是一个“伞形”项目:
* `vet_core` — 扫描器、检查、AST 遍历器、评分、获取元数据、拼写欺骗检测
* `vet_cli` — Mix 任务(`mix vet`、`mix vet.check`)
* `vet_reporter` — 输出格式化(终端、JSON、诊断)
* `_service` — 扫描历史记录和社区证明的持久化层
## 限制
* **Vet 只能降低风险,无法消除风险。** 干净的扫描结果不是安全性的证明。不要将 Vet 作为信任依赖项的唯一依据。
* 仅支持 Elixir/Erlang。不扫描 npm、Python 或其他生态系统。
* 静态分析。无法检测隐藏在运行时条件、后期解密的加密载荷或从外部来源动态加载的代码中的恶意行为。
* 拼写欺骗语料库是一个约包含 200 个流行包的静态列表。列表之外的包不会触发邻近性检查。
* 元数据检查需要访问 hex.pm 的网络连接。离线运行时请使用 `--skip-hex`。
* Vet 信任 hex.pm API 响应。如果 hex.pm 自身被破坏,基于元数据的检查将变得不可靠。
请参阅 [SECURITY.md](SECURITY.md) 了解 Vet 自身的安全边界和信任范围。
## 许可证
MIT
标签:BEAM, CI/CD安全, dev依赖, Elixir, Hex.pm, JSON报告, Llama, LLM幻觉, Mix, Slopsquatting, Typosquatting, 云安全监控, 依赖安全扫描, 包名校验, 命令执行, 安全阈值, 幻包检测, 恶意包检测, 数据外泄, 混淆代码, 终端输出, 编译期分析, 网络信息收集, 静态分析, 风险评分