关于 pkgtruth
pkgtruth 可在安装前检查 npm 和 PyPI 依赖,适用于编码 Agent、CI 和开发者。它结合注册表信息,通过 MCP 工具、命令行工具、Claude Code 钩子和 GitHub Action 标记虚构或有风险的包名。
主要功能
- 01检查 npm 和 PyPI 包名
- 02检查清单文件和安装命令
- 03识别仿冒包及风险信号
- 04为每个判定提供依据
- 05支持 MCP 服务、命令行、钩子和 Action
适用场景
- 01安装前核验 Agent 提议的依赖
- 02在 CI 中扫描项目清单
- 03在 Claude Code 中拦截可疑安装
MCP 相关
作为 MCP 服务,pkgtruth 提供检查单个包、依赖列表或完整安装命令的工具。编码 Agent 可在添加或安装依赖前读取检查结果及依据。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y pkgtruthPKGTRUTH_REGISTRYAlternate npm registry to verify against. Defaults to https://registry.npmjs.org.PKGTRUTH_DOWNLOADS_APIAlternate downloads API used for adoption figures. Defaults to https://api.npmjs.org.PKGTRUTH_CACHE_DIRWhere adoption figures are cached between runs. Defaults to ~/.cache/pkgtruth.PKGTRUTH_TIMEOUT_MSPer-request timeout in milliseconds. Defaults to 8000.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
3收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 是 | 要验证的确切包名,即安装时使用的名称,例如 "express"、"@scope/pkg",或 PyPI 包的 "requests"。不带版本后缀。 |
| ecosystem | enum | 否 | 要查询的注册表。默认为 npm。npm | pypi |
详细介绍
pkgtruth
为 AI 编码 Agent 和 CI 提供关于 npm 和 PyPI 软件包的真实信息。

你的 Agent 刚刚写下了 npm install unused-imports。这个软件包并不是它想要的那个代码检查插件。它是攻击者注册的名称,因为模型一直在凭空编造这个名称——此后 npm 已用安全占位包取代了它。
pkgtruth 会在它进入锁文件之前将其拦截——它可以作为 MCP 服务供 Agent 查询,也可以作为用于 CI 的命令行工具、拉取请求中的 GitHub Action,或拒绝安装命令本身的 Claude Code hook。
为什么要做这个项目
大模型会编造软件包名称。对多个模型的测量结果显示,生成的软件包名称中有 19.7% 是幻觉;研究人员重新运行提示词时,其中 43% 的名称每次都会再次出现。
这种可复现性正是整个攻击的关键。攻击者无需入侵维护者、污染构建服务器,也不必寻找漏洞。他们只需观察模型编造了什么,注册该名称,然后等待。这种技术称为垃圾包抢注(slopsquatting),现实中已经发生。
通常的安全建议是,具备软件包管理能力的 Agent 不应在没有审核关卡的情况下安装任何内容。pkgtruth 就是这样的关卡,而且 Agent 可以自行调用。
现实中发现的案例
以下两个名称都是模型可能生成的,而且如今都在 npm 上:
| 名称 | 实际情况 | 每周安装量 | 正确的软件包 |
|---|---|---|---|
types-node | 0.0.1-security——npm 清除恶意软件后留下的占位包 | 10 | @types/node(429M) |
socket-io | 自 2022 年起已弃用,“请改用 socket.io 软件包” | 1,486 | socket.io(18M) |
模型丢掉 @types/node 的作用域后,就会得到 types-node。npm 于 2024 年 12 月因恶意代码将其移除,但它每周仍有十次安装。
socket-io 并无恶意——它是一个名称容易混淆、已被弃用的软件包。即便如此,它每周仍有 1,486 次安装,这正说明了问题:一个点号和一个连字符就足以造成混淆。
PyPI 上也有同样的情况,只是没有占位包:
| 名称 | 实际情况 | 每周安装量 | 正确的软件包 |
|---|---|---|---|
sklearn | 已弃用的兼容包——它自己的声明写着“请改用 scikit-learn” | 321,887 | scikit-learn(42.6M) |
pytorch | 一个诱饵包,唯一内容是“以 PyTorch 命名的软件包是 torch” | 41,026 | torch(14.4M) |
这两个名称的拼写都与其所冒充的软件包相去甚远,所以 pkgtruth 也会读取弃用声明写了什么:如果声明提到一个更受欢迎的软件包,那么这个名称就是证据。
npx pkgtruth check types-node socket-io
npx pkgtruth check -e pypi sklearn pytorch
这两个案例来自对 20 个名称的人工检查。系统化版本覆盖 242 个知名软件包,枚举每个软件包的所有可能拼错形式,并在每周一重新生成,详见 SLOPSQUATS.md。其中 A 节仅列出 npm 的安全占位包:这些名称曾因恶意软件被 npm 清除,但本周仍有人安装。新增或移除名称的每周变动会作为发布版本公布,因此可以通过发布动态源关注实时列表,无需持续查看代码仓库。
安装
作为 MCP 服务(供编码 Agent 使用)
{
"mcpServers": {
"pkgtruth": {
"command": "npx",
"args": ["-y", "pkgtruth"]
}
}
}
现在可使用三个工具:
| 工具 | 使用时机 |
|---|---|
check_package | 准备添加、导入或推荐一个依赖项时(ecosystem: "npm" | "pypi",默认为 npm) |
check_dependencies | 准备编写 package.json / requirements.txt 时 |
check_install_command | 准备运行 npm install …、npx …、pip install … 时——将完整命令传给它 |
作为 Claude Code 钩子(安装命令本身会被拦截)
MCP 工具只有在 Agent 记得调用时才有用。PreToolUse
钩子会改为检查每条 shell 命令,并在包名是虚构的或有危险时,拒绝执行 npm install、npx、
pnpm add、yarn add、bun add、pip install、uv add、poetry add
等命令。将以下内容添加到 ~/.claude/settings.json(适用于每个项目)或 .claude/settings.json
(适用于单个仓库,可提交到仓库):
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": "npx -y pkgtruth hook" }]
}
]
}
}
不指定包名、不安装任何内容的命令——例如单独的 npm install、git、测试,以及对 node_modules 中已有工具运行的 npx <bin>——都会直接通过,不会发起网络请求。被拦截的命令会连同证据一起作为拒绝结果返回给 Agent,
这样它就可以改用真实的包:
pkgtruth blocked this command: 1 of 2 package(s) must not be installed as-is.
✖ crossenv DANGER — npm replaced this package with a security placeholder (0.0.2-security).
The name was used to publish malicious code. Package is 1 edit(s) from "cross-env",
which has 22,155,059 weekly downloads (15,418x this one). Confirm you meant this
package and not that one.
添加 --fail-on caution,也会拦截无法验证的包。使用 "command": "pkgtruth hook" 执行 npm install -g pkgtruth,可在每次调用时跳过 npx
解析(实测:通过 npx 每条命令约需 0.2 秒,直接执行约需 0.03 秒)。钩子会从 Claude
Code 的 JSON 中读取 tool_input.command,或从任何其他通过管道传入命令的 Agent 读取普通命令字符串;拦截命令时会以状态码 2 退出,并返回 permissionDecision: "deny"——没有可关闭此功能的环境变量,因为 Agent 可以控制其运行命令的环境。
作为 Claude Code 插件(一次安装,同时提供服务和钩子)
/plugin marketplace add hxckya/pkgtruth
/plugin install pkgtruth@pkgtruth
该插件(plugin/)会一并注册 MCP 服务和上述钩子,并固定使用随插件发布的 npm 版本。
作为 CLI(供个人和 CI 使用)
npx pkgtruth check express unused-imports # npm (default)
npx pkgtruth check -e pypi requests sklearn # PyPI
npx pkgtruth scan . # every manifest in the directory
scan 会读取 package.json、requirements*.txt 和 pyproject.toml
(PEP 621 和 Poetry),对照各自的注册表逐项检查;如果有任何阻止项,就会以非零状态码退出,因此可直接用于 CI。
作为拉取请求检查门禁(GitHub Action)
hxckya/pkgtruth-action(位于
GitHub Marketplace)会在每个拉取请求上运行扫描,发布一条置顶评论并附上证据;当检查结果为 HALLUCINATED 或 DANGER 时,检查会失败:
on:
pull_request:
paths: ['package.json', '**/package.json']
permissions:
contents: read
pull-requests: write
jobs:
pkgtruth:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: hxckya/pkgtruth-action@v1
本仓库也使用它作为门禁——参见
.github/workflows/gate.yml。该工作流还会通过对一个故意配置错误的测试样例运行门禁,证明门禁确实可以失败。
检查内容
| 信号 | 含义 |
|---|---|
| 未在注册表中 | 包名是虚构的,没有可安装的内容。 |
| npm 安全占位符 | npm 已移除以此名称发布的恶意代码。 |
| 冒充热门包 | 名称几乎相同,但采用率低得多;或者弃用通知中点名了你实际想要的包(sklearn → scikit-learn、pytorch → torch)。 |
| 已撤回(PyPI) | 最新版本的所有文件均已被其维护者撤回。 |
| 安装时脚本 | preinstall/install/postinstall 会在 npm install 时运行代码。 |
| 已弃用 | 上游已建议停止使用。 |
| 非常新 / 几乎无人采用 | 发布仅数天,安装量只有个位数。 |
| 没有仓库 | 没有可供审查的源代码。 |
| 无人维护 | 多年来没有发布过新版本。 |
判定结果为 SAFE、CAUTION、DANGER、HALLUCINATED 或 UNKNOWN。每个结果都会附上相应证据——Agent 不应盲目信任 DANGER,你也一样。
设计说明
网络故障绝不会放行。 如果无法访问注册表,判定结果将是 UNKNOWN,绝不会是 SAFE。网络降级不能悄无声息地让安全检查变成空操作。
热门包不会被标记——这是经过测量的结果,而非断言。误报审计 使用检测器检查了 240 个知名包的直接依赖闭包:经过内置的第二轮检查后,1,523 个真实包中,阻止级判定为 0,UNKNOWN 为 0,CAUTION 为 40(已弃用或无人维护——这些判断属实,默认不会阻止)。乱报警的门禁会被关闭;这个门禁有数据支撑。
无需构建步骤。 只有两个直接依赖——MCP SDK 和 zod,且仅服务端需要。运行 npx pkgtruth 即可立即启动。
限制
请在信任它之前阅读以下内容:
- 仅支持 npm 和 PyPI。 暂不支持 crates.io、Go modules 和 RubyGems。
- PyPI 没有清除标记。 npm 会在移除恶意软件后留下
-security占位符;PyPI 则会删除项目,因此已清除的 PyPI 名称只会被判定为HALLUCINATED。PyPI 也没有搜索 API——系统会根据每日更新的下载量最高的 15,000 个项目快照查找近似名称,因此不会发现冒充冷门包的项目。 - 仅检查注册表元数据。 它不会分析包的源代码,因此带有恶意载荷、但看起来合法的包仍可能通过检查。
- 不能替代
npm audit或 Snyk。 它们会查找你已信任的代码中的已知 CVE。pkgtruth关注的是更早一步的问题:这个包到底该不该出现在这里? - 新发布的合法包会被标记为
CAUTION。 这是有意为之。新近发布本身确实是一个风险信号;请使用--fail-on danger,这样就不会因此阻止安装。
选项
--json Machine-readable output
--fail-on <level> danger (default) | caution
--fail-on caution 也会阻止无法验证的包,因为“我们无法检查”不等于通过检查。
退出代码:0 表示无问题,1 表示发现阻止级包,2 表示用法或运行时错误。
配置
| 变量 | 默认值 | 用途 |
|---|---|---|
PKGTRUTH_TIMEOUT_MS | 8000 | 每个请求的超时时间 |
PKGTRUTH_RETRIES | 3 | 对 429/5xx/网络错误进行重试 |
PKGTRUTH_MAX_CONCURRENCY | per-host | 覆盖请求节流设置 |
PKGTRUTH_REGISTRY | npm | 替代注册表 |
PKGTRUTH_DOWNLOADS_API | npm | 替代下载量 API |
PKGTRUTH_PYPI / PKGTRUTH_PYPISTATS | pypi.org / pypistats.org | 替代 PyPI 端点 |
PKGTRUTH_PYPI_TOP | hugovk top-pypi-packages | PyPI 相似包检查所用的替代热门度快照 |
PKGTRUTH_CACHE_DIR | ~/.cache/pkgtruth | 缓存采用数据的位置 |
PKGTRUTH_DISK_TTL_MS | 6 hours | 缓存数据的有效时长 |
PKGTRUTH_NO_DISK_CACHE | unset | 设为 1 可禁用缓存 |
关于速度和速率限制
采用数据来自 npm 的下载量 API,该 API 会限制突发请求,且无法批量查询带作用域的名称——如果项目依赖了多个 @scope/pkg,每次扫描都会耗尽全部请求额度。
以下三种措施可控制这一问题:批量端点可通过一次请求解析所有不带作用域的名称;对该主机的请求会串行节流;数据会在磁盘上缓存六小时。每周下载量变化缓慢,因此六小时前的数据同样可靠。
热缓存扫描约 18 个依赖项需要约 1.4 秒。大型扫描如果从下载量 API 收到 429 响应,部分包会在第一轮检查中被标记为 UNKNOWN;随后,每个入口点都会在短暂等待后,仅串行复查这些名称。在对 1,523 个包进行的审计中,第二轮检查将它们全部确认。被限流的查询绝不会变成 SAFE。
缓存数据会按来源 API 区分,因此将 PKGTRUTH_DOWNLOADS_API 指向私有注册表时,不会复用 npm 的数据。
贡献
欢迎在 github.com/hxckya/pkgtruth 提交问题和拉取请求。
有两类报告尤其有用:被误报的合法软件包,以及漏网的恶意软件包。两者都值得编写回归测试。
npm test # offline
npm run test:online # includes live registry checks
许可证
MIT © hxckya
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。