Community编程与开发github.com

hadifarnoud/供应链安全

供应链与CI/CD安全标准——涵盖npm/pnpm卫生、14项攻击分类(打字欺骗、账户接管、缓存投毒、标签重定向、锁文件篡改、蠕虫式恶意软件)、GitHub Actions加固、Docker固定、IOC快速扫描命令以及项目级审计发现。

供应链安全 是什么?

这是一个全面的AI代理技能,旨在执行供应链和CI/CD安全标准。它适用于任何与npm/pnpm/锁文件相关的工作、.github/workflows更改、Dockerfile或镜像固定、MCP/开发工具配置审查以及供应链审计。该技能基于一个详细的SKILL.md文档,其中包含了机器级npmrc默认配置、14项供应链攻击分类(如打字欺骗、账户接管、CI/CD管道注入等)、项目级npm/pnpm卫生规则、GitHub Actions加固规则(包括pull_request_target限制、工作流运行保护和操作固定)、Docker/容器卫生要求(特别是安全工具镜像的摘要固定),以及一个用于检测妥协指标的快速扫描检查清单。它特别关注已知的攻击向量,如Shai-Hulud蠕虫等。该技能支持Claude Code、Coder、Cursor和Codex等AI代理平台,用于安全地审查和构建代码。

兼容平台✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/hadifarnoud/supply-chain-security/tree/HEAD

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

供应链安全标准

适用于所有项目。规则审核于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.new URL——这些是临时的 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

相关技能