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, 依赖审计, 域名收集, 暗色界面, 自动化攻击, 配置审计