2026 年 5 月 11 日,TanStack 的 npm 包被打了一次供应链攻击。数字不小:42 个 @tanstack/* 包,84 个恶意版本,发布时间集中在 19:20-19:26 UTC。

最反常的地方不是攻击多新,而是它多“顺”。没有证据显示 TanStack 的 npm 凭据被盗。攻击者也不是攻破了正常 publish step。它借的是 GitHub Actions release workflow 里的 id-token: write,直接走 npm OIDC trusted publishing 发包。

门没被撬。门禁系统被拿来开门。

谁受影响,应该立刻做什么

这次不是所有 @tanstack/* 包都确认中招。TanStack 扫描了 295 个包,确认受影响的是 42 个包、84 个版本。

已确认干净的系列包括:@tanstack/query@tanstack/table@tanstack/form@tanstack/virtual@tanstack/store,以及 @tanstack/start meta-package。

问题当前信息
攻击时间2026-05-11 19:20-19:26 UTC
影响范围42 个 @tanstack/* npm 包,84 个恶意版本
发现方式发布后约 20 分钟,由外部研究者报告
恶意行为install 生命周期执行,抓取本机和 CI 凭据
处置状态受影响版本已 deprecated,已请求 npm security 移除 tarball
干净系列query、table、form、virtual、store、start meta-package

安装端风险不能轻描淡写。

恶意 payload 在 install 生命周期里跑。也就是说,只要开发者机器、CI runner、构建服务器在当天装到了受影响版本,就要按潜在失陷处理。

它会尝试抓 AWS、GCP、Kubernetes、Vault、GitHub、npm、SSH 等凭据。还会枚举受害者名下维护的其他 npm 包,尝试继续传播。

最该动手的是两类人。

对象该做什么
5 月 11 日安装过受影响版本的团队视安装主机为潜在失陷,轮换云、GitHub、npm、SSH、Vault、Kubernetes 相关凭据
使用 GitHub Actions 发布 npm 包的维护者检查 pull_request_target、Actions cache、OIDC trusted publishing 权限边界

这里不要赌“我应该没事”。供应链攻击最麻烦的地方,就是你很难从安装日志里一眼看出凭据有没有被拿走。

攻击链:三个合理功能,拼成一条发布通道

这次攻击链分三段。每一段单独看都不罕见,连起来就麻烦了。

环节发生了什么风险点
PR 阶段fork PR 触发 pull_request_target,并执行 fork 代码不可信代码进入 base repo 信任环境
缓存阶段恶意代码污染 pnpm store,Actions cache 保存到主仓库作用域PR 脏缓存可被 main 分支复用
发布阶段release workflow 恢复脏缓存,恶意代码从 runner 内存提取 OIDC token不走正常 publish step,也能向 npm 发包

攻击者先开了一个看似无害的 PR。恶意 commit 进入 PR 后,bundle-size.ymlpull_request_target 语境下跑构建,执行了 fork 代码。

接着,污染过的 pnpm store 被写进 GitHub Actions cache。

更阴的是,攻击者又把 PR 强推回 main HEAD,让表面上变成 0 文件改动,然后关掉分支。代码痕迹淡了,缓存还在。

几个小时后,维护者合并正常 PR,main 分支触发 release workflow。它恢复了那份被污染的缓存。

release workflow 本来合法配置了 npm OIDC trusted publishing 所需的 id-token: write。恶意代码就在 runner 内存里提取 OIDC token,绕过失败的正式发布步骤,把 84 个恶意版本发到 npm registry。

这不是全新 0day。材料里提到的关键手法,能在既有公开研究里找到影子:2024 年的 Actions cache poisoning 研究,2025 年 tj-actions 事件里的内存提取 token 技术。

攻击者做的事更像拼装。把公开拼图拼到一条开源发布流水线上。

真问题:默认便利之间没有硬墙

我不太买账“某个维护者手滑”这种解释。

具体失误当然有。pull_request_target 不该随便跑 fork 代码。cache 不该跨信任边界复用。发布 workflow 的 OIDC 权限也不该让被污染依赖轻易碰到。

但只盯一个 YAML 文件,反而会看小这件事。

pull_request_target 有正当理由。机器人要评论,要打标签,要读 base repo 上下文。Actions cache 也有正当理由,能省构建时间。OIDC trusted publishing 甚至是安全改进,因为它避免长期保存 npm token。

坏就坏在这些“好东西”之间缺少隔离。

古话说,天下熙熙,皆为利来。放到 CI/CD 里,利不只是钱,也是少配一个密钥、少等几分钟、少维护一套发布流程。攻击者盯的就是这些省事路径。

这件事也给 npm OIDC trusted publishing 提了一个现实限制:它减少了长期 token 泄露,不等于消灭发布权限风险。只要 runner 上有不可信代码,短期凭据一样能变成武器。

接下来最该观察三件事。

观察点为什么重要
npm 是否移除恶意 tarballdeprecated 只能阻止误装,已缓存和镜像里的包仍要处理
GitHub Actions cache 是否收紧跨边界复用cache 是这次从 PR 走到 release 的关键桥
开源项目是否重审 pull_request_target 和 OIDC 权限这是维护者最能立刻改的部分

对维护者来说,短期动作很明确:不要让 fork PR 写入 base repo cache;不要让 release workflow 复用不可信路径产生的缓存;OIDC 权限按 workflow 收窄;publish 结果要监控,不能等外部研究者报案。

对工程团队来说,采购或引入依赖时也该多问一句:这个包怎么发布?谁能触发发布?发布机器上会不会跑 PR 代码?

过去大家看开源包,常看 stars、下载量、维护活跃度。现在还得看发布流水线。包本身没问题,不代表发包路径没问题。

这次 TanStack 的响应不算慢,外部社区发现也快。真正刺眼的是,攻击者没有跑赢技术前沿,只是沿着默认便利走了一遍。

自动化发布越顺,越要查清楚:这条路只给维护者走,还是也给攻击者留了入口。