tallyguard/vetguard
GitHub: tallyguard/vetguard
vetguard 是一款检测 AI 辅助开发引入的 npm 供应链威胁的开源本地扫描工具,覆盖虚构包、拼写抢注和新注册恶意包等传统 CVE 扫描器无法发现的风险。
Stars: 0 | Forks: 0
# vetguard
一款免费、开源、本地优先的扫描工具,用于检测由 AI 辅助开发引入的 npm 供应链威胁:虚构的(slopsquatting)依赖包、typosquatting(拼写抢注)以及新注册的恶意包。无需账户,无需服务器,无需遥测。
## 为什么
标准扫描器回答的是“这个依赖项是否存在已知的 CVE?”。这忽略了 AI 编程助手带来的新型攻击风险:助手建议了一个不存在的包名,攻击者注册了它,随后助手便将其安装。新注册的恶意包没有任何安全公告记录,因此以 CVE 为主的工具根本无法察觉它们。vetguard 正是为了填补这一空白。
## 安装
已发布到 npm。无需安装即可运行:
```
npx vetguard scan # scan the current project
npx vetguard check # vet a package before installing
```
或者通过 `npm install --save-dev vetguard` 将其添加到项目中。需要 Node.js 20 或更高版本。
## 用法
```
vetguard scan [dir] Scan a project's dependencies (defaults to cwd)
vetguard check Vet a single package before installing
(e.g. vetguard check some-package, foo@1.2.3)
vetguard diff --base [--head ]
Scan only the dependencies a change introduces
(head defaults to ./package-lock.json)
vetguard baseline [dir] Record current findings so later scans fail only on new
ones (adopt on an existing project, ratchet down later)
vetguard --help Show help
vetguard --version Show version
--offline Do not contact the registry
--json Print the report as JSON (for CI and tooling)
--sarif Print SARIF 2.1.0 for GitHub code scanning
--markdown Print compact markdown for a PR comment or summary
--quiet Print only findings and the verdict
--no-color Disable ANSI colors (also respects NO_COLOR)
--fail-on Exit non-zero only at or above this severity
(critical|high|medium|low|info); default: any finding
```
仅当输出到终端时,文本输出才会根据严重程度进行着色;通过管道或重定向的输出保持纯文本,并且 `--no-color` 或 `NO_COLOR` 环境变量可以关闭颜色显示。
`scan` 会从 `package-lock.json`(v2 或 v3)中读取已解析的依赖树,因此它涵盖了间接依赖项和确切的已安装版本。如果没有受支持的 lockfile,它会回退到清单中声明的依赖项,并明确提示这一点;系统会检测到 yarn 和 pnpm 的 lockfile,并将其报告为“暂不支持”,而不是默默地跳过。
退出代码:`0` 表示干净或无法验证,`1` 表示发现风险,`2` 表示用法或读取错误。`check` 使 vetguard 能够作为安装前的拦截门,包括针对那些会添加依赖项的编程代理。
## 配置
在被扫描的项目中放置一个可选的 `vetguard.config.json` 文件即可设置默认值并抑制风险报告。命令行参数会覆盖其配置。
```
{
"failOn": "high",
"offline": false,
"ignore": [
{
"rule": "young-package",
"package": "our-internal-lib",
"reason": "first-party package published last week; reviewed"
}
]
}
```
每个 `ignore`(忽略)条目都需要提供一个 `reason`(理由),没有理由的忽略属于配置错误,而不是静默跳过。被抑制的风险项仍会显示在报告中(标记为已抑制及其理由),但不会影响最终结论或退出代码。其核心意义在于审计追踪:你始终可以查看哪些风险被放行以及为什么放行。
### 在现有项目中采用(基线)
一个之前从未被扫描过的代码库通常会有一些已有的风险报告。为了不必在开启 vetguard 前被迫修复所有问题,你可以记录一个基线,并随着时间推移逐步收紧规则:
```
vetguard baseline # writes .vetguard-baseline.json (commit it)
```
随后的扫描会将处于基线中的风险报告标记为已抑制并予以通过;只有基线中**不包含**的风险才会导致构建失败。提交该文件,以便整个团队共享同一个起点,然后随着你清理代码逐步缩减该文件。基线中某个风险的标识包含其确切版本,因此依赖项版本升级会被重新评估,而不是永远被沿袭保留。
## 在 Pull Request 中使用
vetguard 可在 GitHub Actions 中运行,无需服务器且零成本:扫描在 GitHub 的 runner 上执行(对公开仓库免费),通过内置的 `GITHUB_TOKEN` 发布结果,并上传 SARIF,从而使风险报告能在 PR 中以批注形式显示,并出现在 Security(安全)选项卡中。
```
# .github/workflows/dependency-scan.yml
name: Dependency scan
on:
pull_request:
permissions:
contents: read
security-events: write # upload SARIF
pull-requests: write # only if comment: true
jobs:
vetguard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: tallyguard/vetguard@v0.1.0
with:
fail-on: high # fail the check only on high/critical findings
comment: true # post/update a single sticky PR comment (optional)
- if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: vetguard.sarif
```
`comment: true` 会发布一条置顶评论,并在后续推送时原地更新,因此绝不会出现评论堆叠的情况。它需要 `pull-requests: write` 权限;将其关闭(这也是默认设置)即可仅依赖 SARIF 批注和作业摘要。对于来自 fork 的 pull request,该 token 是只读的,因此会直接跳过评论而不会报错失败。
### 仅扫描 Pull Request 的变更内容
`diff` 模式仅评估变更引入的依赖项(对 head lockfile 来说是全新的,或者是新版本),这是信号价值最高的时刻,也能保持报告内容聚焦。拉取基础分支的 lockfile 并将其与工作树进行对比:
```
name: Dependency diff
on:
pull_request:
permissions:
contents: read
jobs:
vetguard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # so the base branch's lockfile is available
- run: git show "origin/${{ github.base_ref }}:package-lock.json" > /tmp/base-lock.json
- run: npx vetguard@0.1.0 diff --base /tmp/base-lock.json --fail-on high
```
## 检测内容
每一条风险报告都包含规则 ID、严重程度和确凿证据,因此最终的判定结论总是可以追溯到具体原因。目前已支持的功能:
- **nonexistent-package**:依赖项名称在 registry(包注册表)中没有任何记录,这是在攻击者注册之前最明显的 AI 幻觉信号。
- **young-package**:近期首次发布的包名,且采用率较低或未知,这符合新注册包的特征,通常用来替代虚构的或长相相似的名称。
- **install-scripts**:运行 `preinstall`/`install`/`postinstall` 脚本(经典的木马执行载体)且尚未被广泛认可的包。合法构建原生代码的流行包不会被标记;而运行 install 代码的新包或冷门包则会被标记。
- **unpublished-version**:包存在,但确切锁定的版本不在 registry 中。当 npm 移除恶意软件时,这些版本就会消失,因此 registry 不再提供的锁定版本是一个强烈的篡改信号。
- **typosquat**:包名与某个流行包极其相似(只有一个拼写错误、字母交错或分隔符不同)。流行包绝不会被标记为另一个包的 typosquat,而已被认可的相似名称也会被忽略;该信号仅会在针对不存在、年轻或低采用率的包时触发风险报告。
- **hallucination-name**:包名重新组合了流行包的 token,这是 AI 重新排列 token 或丢弃约定前缀的 slopsquatting 模式(例如将 `eslint-plugin-unused-imports` 变成 `unused-imports`)。其风险触发条件与 typosquat 相同,因此仅仅是共享部分 token 的成熟包并不会被标记。
### 关于后门
vetguard 针对的是后门*行为*:如今的安装时代码执行,以及未来的能力信号(意外的网络、文件系统和进程访问)、代码混淆和针对编程代理的提示词注入。没有任何静态扫描器能证明一个包绝对没有后门,新颖的或经过重度混淆的包可能会规避启发式检测,因此 vetguard 只会提供基于证据的信号,并报告“未发现风险”,而永远不会报告“安全”。
## vetguard 本身安全吗?
对于任何安全工具来说这都是一个合理的问题,而扫描器恰恰是供应链攻击者最希望用来伪装恶意软件的工具。以下每一项声明都是可验证的,而非仅仅口头宣称:
- **带有来源签名的发布版本。** 每个版本都是通过带有 npm provenance 的 CI 发布的:这是一种加密证明,表明发布的字节码是由此公开仓库中标记的源代码构建的,且在此过程中未被篡改。可在安装后通过 `npm audit signatures` 进行验证,或者在 [npm 页面](https://www.npmjs.com/package/vetguard) 查看已验证徽章。
- **零运行时依赖项。** vetguard 除了自身之外不会安装任何东西,因此不存在可能被入侵的间接依赖包,并且每一行运行的代码都是你可以阅读的。验证方法:`npm view vetguard dependencies`(结果为空)。
- **无 install 脚本。** `npm install vetguard` 不会执行任何代码,不存在任何 `preinstall` / `install` / `postinstall` 钩子。验证方法:阅读它的 `package.json`。
- **绝不执行其扫描的代码。** 它将清单、lockfile 和包元数据作为数据读取;它从不 `require`、import 或 eval 被扫描的包。
- **无遥测,不进行外部请求。** 唯一的网络调用是扫描所需的 registry 查询,而 `--offline` 甚至会禁用这些调用。验证方法:使用 `--offline` 运行任何命令,观察它在没有网络的情况下正常工作。
- **诚实的结论。** 当某些内容无法验证时(离线状态、非 registry 源、不支持的 lockfile),vetguard 会报告“无法验证”,而绝不报告“安全”。
- **小巧、可审计、开源。** 发布的 tarball 只有少数几个文件(打包的 CLI、类型声明、`LICENSE`、`README`、`package.json`),由本仓库中 Apache-2.0 许可下的源代码构建而成。
- **它会扫描自身。** vetguard 会在每次测试运行(离线状态)以及每个 pull request([.github/workflows/pr-scan.yml](.github/workflows/pr-scan.yml))时针对自身的依赖项进行扫描;如果其自身的供应链中发现风险,将导致其自身的构建失败。
## 开发
```
npm install
npm run typecheck
npm run lint
npm run format:check
npm test
npm run build
```
要求 Node >= 20(见 `.nvmrc`)。贡献指南:[CONTRIBUTING.md](CONTRIBUTING.md)。安全策略:[SECURITY.md](SECURITY.md)。设计文档:[docs/](docs/)。
## 许可证
Apache-2.0。请参阅 [LICENSE](LICENSE)。
标签:GNU通用公共许可证, LNA, MITM代理, Node.js, npm, 依赖审计, 域名收集, 暗色界面, 自动化攻击, 配置审计