供应链安全标准
适用于所有项目。规则审核于2026-11-11(Shai-Hulud / Mini Shai-Hulud 蠕虫之后)。
机器级默认配置(~/.npmrc)
minimum-release-age=1440 # 仅 pnpm — 分钟(1440 = 24小时)
min-release-age=1 # 仅 npm 12+ — 天数(1 = 24小时)
两个包管理器使用不同的键名和不同的单位。 已于2026-08-21通过阅读各自工具的源代码验证:pnpm 计算 Date.now() - minimumReleaseAge * 60 * 1e3(分钟);npm 的 min-release-age 配置定义带有 hint: '<days>'。两行都需要——一个不能覆盖另一个,将 1440 复制到 npm 键中意味着 1440 天,会阻止几乎所有安装。
npm 在安装时会打印 Unknown user config "minimum-release-age" 并警告该配置将来会停止工作。这是不可避免的,因为键存在于 ~/.npmrc 中:pnpm 11 移除了全局配置写入(pnpm config set --global 现在重定向到项目级 pnpm-workspace.yaml),因此 ~/.npmrc 是唯一可以设置全局 pnpm 限制的地方。接受这个警告;不要删除该键。
不要依赖这些——已验证无效
-
block-exotic-subdependencies— 在 npm 或 pnpm 的源代码中均未出现。它从未发挥任何作用。外部依赖必须通过审查来捕获,而不是靠这个键。 -
ignore-scripts=true— 此技能之前声称它是全局设置的。它并未设置,npm config get ignore-scripts返回false,因此生命周期脚本在安装时确实会运行。决定于 2026-08-21:保持关闭。不要启用它。 Postinstall 是 npm 的主要妥协向量,因此这是一个已知的、可接受的风险缺口——而不是需要修复的疏忽。全局启用它会破坏跨所有项目的原生构建(esbuild、swc、sharp、node-gyp),并且破坏的表现形式与根本原因相去甚远。上面的 24 小时发布年龄限制承担了这个风险。如果某个特定项目需要脚本隔离,请在那里限定范围——绝不要全局设置。
应针对工具本身而非本文件检查此类声明——审计时发现上述三个条目都是错误的。
供应链攻击分类(审计前参考)
1. 打字欺骗 / 依赖混淆
包名与流行包几乎相同,或者公共包匹配内部作用域名。仅 2025 年就发布了 454,648 个恶意 npm 包。
检测: 对不熟悉的包执行 npm info <pkg> | grep -E 'created|repository'。与内部注册表名称交叉引用。
2. 维护者账户接管
攻击者钓鱼获取维护者信息,发布带有 postinstall 载荷的新版本。2025 年 9 月的 Shai-Hulud 攻击了 chalk、debug、ansi-styles(每周 26 亿次下载)。2026 年 3 月 Axios(每周 1 亿+ 次下载)通过朝鲜社会工程攻击被攻破。
检测: npm diff <pkg>@<last-known-good> <pkg>@latest -- package.json scripts — 检查补丁版本中新增的生命周期脚本。
3. 仓库劫持 / 命名空间抢注
攻击者注册废弃的 GitHub 用户名或 npm 命名空间,重建仓库,提供恶意代码。
检测: 检查 package.json 中的 repository 字段;标记创建时间晚于包首次 npm 发布日期的 GitHub 账户。
4. CI/CD 管道注入(pull_request_target / 缓存投毒)
攻击者针对带有 pull_request_target 的仓库发起 PR,该事件与有权限的发布作业共享缓存。投毒 pnpm 存储;下一次发布工作流泄露 npm OIDC 令牌。TanStack 攻击向量(2025 年 11 月,2026 年 5 月 Mini Shai-Hulud,300+ 包,每周 1600 万次下载)。
检测:
grep -r "pull_request_target" .github/workflows/
grep -r "cache" .github/workflows/ | grep -i "pnpm\|npm\|store"
硬性规则: 如果工作流检出 PR 代码或与有权限的作业共享缓存,则永远不要使用 pull_request_target。
5. Docker 镜像投毒
攻击者泄露 Docker Hub 凭据并用恶意二进制文件覆盖现有镜像标签(包括 latest)。Trivy aquasec/trivy 标签 0.69.4–0.69.6 和 latest 于 2026 年 3 月被投毒。KICS 于 2026 年 4 月——五个标签被覆盖;扫描了包含机密信息的 Terraform/K8s 配置。
检测: docker inspect <image> --format '{{.Id}}' 与配置中的固定摘要对比。
硬性规则: 将所有镜像固定到 @sha256: 摘要。安全工具镜像永远不要使用 latest。
6. GitHub Actions 标签重定向
攻击者泄露操作维护者的令牌,将现有版本标签指向恶意代码。tj-actions/changed-files CVE-2025-30066(2025 年 3 月,23000+ 仓库)将所有 CI 机密转储到日志。2026 年 5 月:流行的操作标签被重定向到假冒提交。 检测:
grep -r "uses:.*@v[0-9]" .github/workflows/ # 标记标签固定(错误)
grep -r "uses:.*@[a-f0-9]\{40\}" .github/workflows/ # SHA 固定(正确)
硬性规则: 将所有第三方操作固定到完整的 40 字符提交 SHA,而不是版本标签。actions/*(第一方)可以使用主版本标签。
7. 锁文件篡改 / 完整性绕过(PackageGate 类)
攻击者修改锁文件以替换恶意包版本或移除完整性哈希,以便下次安装静默获取不同的 tarball。2026 年 1 月记录了六个零日锁文件漏洞(Koi Security)。 检测:
grep -E '"resolved".*git\+|tarball' package-lock.json pnpm-lock.yaml
grep -v "integrity" pnpm-lock.yaml | grep "resolved"
硬性规则: CI 必须使用 pnpm install --frozen-lockfile 或 npm ci——永远不要使用裸 npm install。
8. 子依赖遮蔽
攻击者针对 3 层或更深的传递依赖。@ctrl/tinycolor 是 Shai-Hulud 的传播节点。
检测: npm ls --all 2>/dev/null | grep -E "postinstall|preinstall"。在 PR 上使用 socket scan。
9. 社会工程维护者交接
攻击者冒充招聘人员或商业伙伴以获取 npm 发布权限。UNC1069(朝鲜)于 2026 年 3 月针对 Axios 维护者,还试图攻击 Socket CEO、Lodash 创建者和 Fastify 负责人。
检测: 对关键依赖执行 npm owner ls <pkg>;在可疑发布前监控新协作者。
10. 抗议软件(维护者蓄意破坏)
维护者故意注入破坏性代码。colors.js v1.4.44–1.4.2(2022 年 1 月,无限循环),node-ipc v10.1.1–10.1.3(2022 年 3 月,在俄罗斯 IP 上删除文件)。2026 年 5 月出现了三个新的恶意 node-ipc 版本。
检测:
npm diff <pkg>@<prev> <pkg>@<new> -- lib/
grep -r "networkInterfaces\|geoip\|fs.rmdir\|fs.unlink" node_modules/<pkg>/
将 node-ipc、colors、faker 固定到确切的已知良好版本。
11. 蠕虫式自复制恶意软件
载荷窃取 npm 令牌/PAT 并自动发布受害者维护的其他包的恶意版本。首次观察到于 2025 年 9 月的 Shai-Hulud;2026 年 5 月的 Mini Shai-Hulud(300+ 包,每周 1600 万次下载,同时影响 npm 和 PyPI)。 检测 IOCs:
find .github/workflows -name "*.js" # 工作流中的 bundle.js
grep -ri "shai-hulud" . # 蠕虫字符串
find node_modules -name "data.json" -newer package.json # 数据外泄暂存
find node_modules -name "cloud.json"
12. 休眠 / 环境条件触发载荷
载荷仅在存在 AWS_SECRET_ACCESS_KEY、GITHUB_TOKEN 或 CI=true 时激活——逃避沙箱扫描器。
检测:
grep -r "process.env.AWS_SECRET\|process.env.GITHUB_TOKEN\|process.env.NPM_TOKEN" node_modules/
标记任何读取这些变量且没有合法理由的包。
13. 安全工具作为攻击向量
攻击者专门针对安全扫描工具(Trivy、KICS、Semgrep),因为它们在 CI 中运行并具有对机密和基础设施配置的提升访问权限。Trivy 和 KICS 均在 2026 年 3–4 月被攻击。 硬性规则: 将所有安全工具 Docker 镜像固定到摘要,而不是标签。与供应商发布说明通过带外方式验证摘要。
14. 非注册表 / CDN 依赖
从 git URL、GitHub tarball 或自定义 CDN(例如 cdn.sheetjs.com)解析的包绕过注册表安全门和来源验证,也不受发布年龄限制的保护——该限制仅适用于注册表版本。有些包完全没有完整性哈希。没有任何机制可以自动阻止这些;它们必须在审查中被捕获。
检测:
grep -E '"resolved".*github\.com|cdn\.' package-lock.json pnpm-lock.yaml
grep -E '"version":\s*"(git\+|https?:)' package.json
项目级 npm/pnpm 卫生
- 提交锁文件。CI 必须使用
pnpm install --frozen-lockfile(或npm ci)。绝不要在 CI 中使用裸npm install。 - 在
package.json中固定packageManager以锁定 pnpm/npm 版本。 - 将受损命名空间中的包固定到确切版本:
@tanstack/*、posthog-node、@mistralai/*、@langchain/mistralai、@ctrl/*、ngx-*、rxnt-*、@zapier/*、@postman/*、@uipath/*、agent-mcp、@semgrep/*、node-ipc、colors、faker。 package.json中禁止通配符"*"版本。- 禁止
pkg.pr.newURL——这些是临时的 PR 预览构建,不是不可变制品。 - 对于 npm/yarn 项目,在它们本地的
.npmrc中镜像相同的 npmrc 限制。 - 新项目首选 pnpm。不要仅仅为了安全而迁移正在工作的项目。
GitHub Actions 加固
硬性规则
- 永远不要在检出 PR 代码、从 PR 代码运行 install/build/test 或与有权限的工作流共享缓存的工作流中使用
pull_request_target。 pull_request_target仅在读取 PR 元数据(标签、评论、标题)且不进行检出时才可接受。- 不要在 PR 触发作业和发布/发布作业之间共享缓存。
- 永远不要在 CI 中针对可变分支(
@main、@master)使用curl | bash或wget | sh。始终将安装程序脚本固定到特定版本并验证校验和。
workflow_run 保护
当使用 workflow_run 在父工作流之后链接一个有权限的作业时,if: 必须断言以下所有条件:
if: >
github.event_name == 'workflow_dispatch' ||
(github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_branch == 'main' &&
github.event.workflow_run.head_repository.full_name == github.repository &&
github.event.workflow_run.event == 'push')
操作固定
- 将所有第三方操作固定到完整的 40 字符提交 SHA,而不是
@v1/@v6。 - 添加包含人类可读标签的注释以便审计:
docker/login-action@65d0a9e # v3 - 第一方
actions/*可以使用主版本标签。 - 获取当前 SHA:
gh api repos/<owner>/<action>/git/ref/tags/<tag> - 使用以下命令自动化:
npx pin-github-action .github/workflows/*.yml - 最小化每个作业的
permissions:。默认为contents: read。仅在发布步骤添加id-token: write。
npm 发布工作流
- 在 npm 账户上要求 2FA:
npm profile enable-2fa auth-and-writes。 - 使用可信发布(OIDC)而不是长期存在的
NPM_TOKEN机密。 - 添加
--provenance(或provenance: true),以便消费者可以验证来源。 - 仅从由
main分支标签推送触发的release.yml发布。永远不要从 PR 触发的工作流发布。
Docker / 容器卫生
- 将每个
FROM固定到摘要:FROM node:22-slim@sha256:<hash> - 在生产 Dockerfile 或 CI 中永远不要使用
latest或无标签镜像。 - 安全工具镜像(Trivy、KICS、Semgrep、Snyk、Grype、Hadolint)风险最高——首先固定这些。
- 获取当前摘要:
docker buildx imagetools inspect node:22-slim --format '{{json .Manifest.Digest}}' - 对于私有注册表镜像:使用不可变标签和摘要发布。
开发者工具和配置文件
- 永远不要在
~/.cursor/mcp.json、~/.claude/settings.json或类似配置文件中以纯文本形式存储 API 令牌或机密。使用环境变量或密钥链。 - 将 MCP 服务器包固定到确切版本(
[email protected],而不是mcp-remote@latest)。 - 在任何供应链安全事件后检查
~/.vscode/tasks.json和~/.cursor/中是否有意外的自动运行钩子。
检测 / 监控
- 在任何包含工作流的仓库的 CI 中安装 zizmor 或
snyk-labs/github-actions-scanner。 - 在任何发布包的仓库(任何可发布的库)上添加 socket.dev GitHub App。
- 订阅 npm 安全公告 RSS 和 PyPI 恶意软件源。
IOC 快速扫描检查清单
# Shai-Hulud / 蠕虫 IOCs
grep -ri "shai-hulud" .
find .github/workflows -name "*.js"
find node_modules -name "cloud.json"
# 被攻破的工作流模式
grep -r "pull_request_target" .github/workflows/
grep -r "curl.*|.*sh\|wget.*|.*sh\|base64.*eval" .github/workflows/
# 未固定的操作
grep -r "uses:.*@v[0-9]" .github/workflows/
# 锁文件外部依赖
grep -E '"resolved".*git\+|cdn\.' package-lock.json pnpm-lock.yaml
grep -v integrity pnpm-lock.yaml | grep resolved
# Docker
grep -r "FROM.*:latest\|FROM.*[^@]$" Dockerfile* docker-compose*.yml