内容
# pkgxray
本地 CLI + MCP 服务器,用于评估 AI 编码代理扩展、Codex 插件、Claude Code 扩展或 npm 包的安全性 - 根据提供的证据或通过将实际的 npm tarball 获取到沙盒隔离区。
零依赖节点,完全本地运行。
## 安装
```bash
npm install -g pkgxray
# 或通过 npx 进行一次性安装:
npx pkgxray guard npm:some-package@1.2.3
```
它故意保持保守 - 只报告它可以从元数据或源文本中引用的证据。每个判决结果都是:
- `安全` - 没有高风险或中风险指标
- `审查` - 不完整的证据或特权能力需要手动审查
- `阻止` - 高严重性指标(提示注入、凭证访问、持久性、混淆 + 执行、可能的窃取)
## 它能检测到什么
pkgxray 在代码到达您的机器之前会发现具体的供应链风险:
- **已知 CVE(OSV)** - 查询 OSV 以获取确切的 `name@version`,并在下载 tarball 之前阻止。
- **凭证和秘密访问** - 读取 `.ssh`、`.aws`、`.npmrc`、`.env`、密钥链、浏览器存储、钱包和批量 `process.env` 收集。
- **窃取形式** - 环境/文件读取与文件中的出站网络、硬编码 IP 地址、Webhook、短网址、下载后执行,以及 **跨多个文件传播的分割令牌窃取** 和 **评估不透明数据 blob 的第二阶段加载器**。
- **持久性** - 编写 shell rc 文件、cron、启动代理、systemd、注册表运行键。
- **混淆 + 执行** - 使用 `eval`、`new Function`、`vm`、`atob` + exec 编包/编码 blob。
- **提示注入** - README/文档/元数据中隐藏的指令,旨在操纵 AI 代理(视为不信任证据,永不遵循)。
- **npm 与 GitHub 分歧** - 将已发布的 tarball 与标记的 GitHub 源进行比较,并标记仅存在于(或与)npm 工件中的文件。**审查** 信号,非自动阻止:它无法区分正常的构建/转译/缩小步骤和篡改,因此它会为人类标记而不是对每个具有编译输出的软件包都发出狼来了的警告。
- **出处** - 验证 npm 的 sigstore/SLSA 声明链接包到其声称的源存储库。
由于它将软件包分阶段到沙盒隔离区,从不运行安装脚本或软件包代码,因此您可以在 ~1 秒/软件包内获得此分类,且无执行风险。
## CLI
```bash
# 审核提供的证据
pkgxray --file examples/evidence.json
pkgxray --format json --file examples/evidence.json
# 审核整个项目的锁定文件(批量 OSV 查询)
pkgxray audit package-lock.json # 还支持:yarn.lock、pnpm-lock.yaml、package.json
pkgxray audit package-lock.json --deep # 对每个阻止的依赖项进行完整的静态/GitHub 层
```
守卫流程将扩展程序分阶段到私有隔离目录中,审核分阶段的副本,并在策略允许时仅将其推广。 它**不**运行 `npm install`、生命周期脚本、构建步骤或扩展程序代码。
对于 npm 引用,顺序为:解析注册表元数据 → 查询 OSV 以获取确切版本 → 如果存在漏洞,则在下载之前阻止 → 否则下载并提取 tarball 到隔离区 → 收集源并运行静态审核(除非 `--no-source-scan`)。
防护决策:`允许`(安全,推广 ok)、`审查`(检查隔离区首先)、`阻止`(不安装)。 默认情况下,仅 `安全` 推广;使用 `--policy allow-review` 还可以推广审查级别的软件包。
输入证据 JSON:
```json
{
"packageName": "example-extension",
"npmMetadata": {},
"githubMetadata": {},
"webPresence": {},
"sourceFiles": {
"package.json": "{\"name\":\"example-extension\"}",
"index.js": "module.exports = {}"
}
}
```
## MCP 服务器
使用任何支持 MCP 的代理的标准输入/输出服务器:
```json
{
"mcpServers": {
"pkgxray": { "command": "pkgxray-mcp" }
}
}
```
公开的工具:
- `audit_agent_extension_supply_chain` - 对提供的证据进行静态启发式检查(需要 `sourceFiles`;可选 `packageName`、`npmMetadata`、`githubMetadata`、`webPresence`、`knownVulnerabilities`、`provenanceAttestation`、`npmVsGithubDiff`、`outputFormat`)
- `guard_agent_extension_install` - 隔离、分阶段漏洞检查和审核实际软件包(需要 `reference`;可选 `quarantineRoot`、`promoteTo`、`policy`、`force`、`sourceScan`、`vulnerabilityCheck`、`deep`、`outputFormat`)。自动获取 npm 出处声明并进行交叉检查。
- `audit_lockfile_supply_chain` - 批量 OSV 扫描锁定文件中的每个依赖项(需要 `lockfilePath`;可选 `deep`、`deepAll`、`vulnerabilityCheck`、`outputFormat`)
- `triage_lockfile_supply_chain` - 非交互式分类,将每个标记的依赖项记录为 `allow`/`block` 到配套的 `.pkgxray.lock`(需要 `lockfilePath` 和 `auto`;可选 `includeSafe`、`outputFormat`)
## 静态启发式和严重性策略
调整后,合法软件包不会被阻止,而实际攻击仍会被阻止。针对 47 个最常用的 npm 软件包进行了验证:**0 次错误阻止**。
- **阻止**(高)- 文档中的提示注入文本;凭证读取接近文件系统读取基元(包括从**组装的字符串片段**中构造的路径 - `".s"+"sh"`、`[".s","sh"][0]+…`);持久性写入(shell rc / cron / 启动代理);执行或出站网络加硬编码公共 IP 地址 / 短网址 / Webhook;批量 `process.env` 收集与出站网络(现在包括 `sendBeacon` / `EventSource` / `dns.*` / `dgram` / 远程 `import()` / 图片信标);**动态 `require`/`import`** 计算名称与批量环境收集;**第二阶段加载器** 读取不透明数据 blob(`.dat` / `.bin` / `.txt` / `.enc` …)并 `eval` 它;**分割令牌窃取** 环境收集和已知窃取/回调域生活在不同的文件中。
- **审查**(中)- 安装/安装后/准备脚本;动态 `eval` / `new Function` / vm;**动态 `require`/`import`** 通过计算名称;批量 `process.env` 收集(序列化、迭代或通过 `{...process.env}` / `Object.assign` 克隆整个环境);敏感路径或域**从分割片段组装**;**特洛伊木马源** 双向覆盖 / 零宽度 Unicode 代码;**地理/地区限制破坏操作**(逻辑炸弹/抗议软件形状);**下载后执行**(网络内容直接馈送到解释器 - `curl | sh`,`eval` 获取的正文);剪贴板访问;**孤立的引用到高置信度窃取/回调域**;**npm 与 GitHub 分歧**;缺少 package.json 或入口点。
- **信息** - 单独的子进程/获取/网络。已记录,不会阻止。
### 设计以避免误报
- **文档不被视为代码扫描。** README / markdown / `.rst` / `.txt` 只运行提示注入检查 - Node 永远不会执行它们,因此 README 中的说明性 `process.env` + `fetch` 示例不会被视为窃取。
- **测试/固定/示例/基准测试文件降级为审查。** 硬编码 IP 地址和 `eval` 在非运行时文件中很常见。两个保持高优先级:环境收集 + 网络窃取形状,以及**生命周期脚本实际运行**的任何文件(因此通过 `postinstall` 布线的有效载荷无法隐藏在 `examples/` 下)。
- **网址缩短器是双重用途的。** `bit.ly` / `tinyurl` / … 仅在与功能结合时计算;粘贴 / Webhook / OAST / 隧道域名单独标记。
- **分歧是审查信号,不是阻止。** 它无法区分构建步骤和篡改;文件**内容**仍由上述代码参数扫描。
`.d.ts`、`.map`、`.min.js` 和 `.lock` 文件被跳过。最多扫描 20,000 个条目 / 256 MB 未压缩的 tarball。
### 已知盲点:安装后网络执行
pkgxray 是一个静态扫描器 - 它会推理 tarball 中包含的字节。软件包在安装后下载并运行其实际有效载荷(`curl | sh`、`eval` 获取的正文、子进程提取第二阶段)可以发送干净的树。pkgxray 在其形状明确时标记**功能**(`remote-code-load` 审查信号),但它无法看到不在工件中的代码。将安装后网络执行视为任何 tarball 扫描的保证范围之外,并将 pkgxray 与运行时/安装时沙盒结合使用时,该风险很重要。
## JSON 输出
所有 JSON 输出都带有 `schemaVersion: 1`。在 `0.x` 内,字段**仅为附加字段** - 可能会出现新字段;现有字段保持其类型和含义。
为下游工具构建对抗该保证。
运行任何命令并使用 `--format json` 以查看完整形状。顶级字段:
- **审核 / `--file`** - `verdict`、`grade`、`score`、`parameters`、`summary`、`riskBands[]`、`findings[]`
- **guard** - `decision`、`resolved`(类型/sha256/integrity/tarballUrl)、`githubMetadata`、`npmVsGithubDiff`、`vulnerabilityPrecheck`、`timings`、`quarantinePath`、`promotedPath`、`report`(上面的审核形状)
- **audit `<lockfile>`** - `file`、`format`、`totalDeps`、`uniqueDeps`、`summary`、`worstDecision`、`results[]`(每个依赖项的决策 + 漏洞)
退出代码:`0` = 安全/允许,`2` = 阻止,`3` = 审查。
## 性能
pkgxray 足够便宜,可以在每次安装时运行。在 Apple M1(Node 26)上测量,冷缓存,每个软件包运行一次 `guard`:
| 软件包 | 每周下载次数 | `guard` 时间 |
| --- | ---: | ---: |
| `is-number@7.0.0` | ~170M | ~1.3 秒 |
| `express@4.21.0` | ~110M | ~1.4 秒 |
| `commander@12.1.0` | ~444M | ~1.5 秒 |
| `chalk@5.3.0` | ~451M | ~1.5 秒 |
其他路径:
| 操作 | 时间 |
| --- | --- |
| 静态审核提供的证据(`--file`,无网络) | ~140 毫秒(~25 毫秒扫描) |
| `guard` 重复运行,暖缓存 | ≈25% 比冷缓存快 |
| `guard` 已知漏洞软件包 | 在 OSV 预检查时阻止,*在下载前* |
几乎所有 `guard` 的时钟时间都是网络往返(npm 注册表、OSV、GitHub 元数据、出处)- 本地静态分析本身为 ~25 毫秒。
将 CI 指向共享的[缓存服务器](#自托管缓存服务器),以减少跨运行器的重复 GitHub 获取。数字因网络和机器而异;将其视为大致估算。
## 浏览器扩展
`browser-extension/` 是一个与 Chrome 兼容的 Manifest V3 解包扩展程序。它完全本地运行,不需要浏览器权限。通过 `chrome://extensions` → 启用开发者模式 → **加载解包** → 选择 `browser-extension/` 文件夹进行加载。(在 Dia 中,如果支持解包的 Chromium 扩展程序,请尝试相同的流程;否则使用 Chrome。)
## 自托管缓存服务器
每个 `guard` 和 `audit --deep` 运行都会获取 GitHub 存储库元数据和 tarball。
在 CI 中,这会复制跨运行器的流量。在您的私有网络上运行共享缓存,并将其折叠为每个(存储库,引用)每个 TTL 窗口的单个获取:
```bash
# 在缓存主机上(零依赖,只有 Node):
pkgxray-cache --port 8819 --cache-dir /var/cache/pkgxray
# 将客户端指向它(无需代码更改):
export PKGXRAY_CACHE_URL=http://cache.internal:8819
pkgxray audit package-lock.json --deep
```
路由:`GET /github/repos/{owner}/{repo}`(1 小时 TTL),`GET /github/tarball/{owner}/{repo}/{ref}`(24 小时 TTL,流式传输),`GET /healthz`。
对同一未缓存资源的并发请求共享一个上游获取;404 错误被转发但不持久化。如果设置了 `PKGXRAY_CACHE_URL`,客户端会运行默认路径,零开销。设置 `PKGXRAY_CACHE_GITHUB_TOKEN` 在服务器上(或转发 `x-pkgxray-github-token` 每个客户端)以获取 5000 次请求/小时。
> **信任模型 - 部署前阅读。** 缓存服务器是一个透明的缓存代理,**不是**身份验证边界。它没有登录、速率限制或每个客户端的身份验证。在私有网络或反向代理(nginx,Caddy,Cloudflare Access)上运行它,以强制执行您自己的身份验证。像任何构建工件缓存一样对待缓存目录 - 永远不要将其放在公共网络上。
## 本地开发
```bash
npm run build:browser
npm test
npm run audit:evidence -- --file examples/evidence.json
```