Sponsored
また npm のサプライチェーン攻撃です。今度は TanStack。
2026年5月12日早朝、TanStack の npm パッケージ群にマルウェア入りのバージョンが公開されました。
先日の axios の件と似ている部分もあります。
ただし、今回の中身はかなり違います。
メンテナーの npm トークンを盗んで公開したのではなく、GitHub Actions の pull_request_target、Actions キャッシュ、OIDC Trusted Publishing がつながって、正規の公開経路から不正パッケージが出てしまった、という話です。
ただし、マルウェア自体はインストール先の npm トークンも狙います。
「TanStack 側の公開に npm トークンは使われていない」と「入れてしまった環境の npm トークンは盗まれうる」は、分けて確認したほうがいいです。
しかも、@tanstack/query-core や @tanstack/react-query ではありません。
ここ、名前だけで早とちりしやすいので先に書いておきます。
何が起きたか
TanStack 公式のポストモーテムによると、UTC 2026-05-11 19:20〜19:26 の約6分間に、42個の @tanstack/* パッケージへ合計84個の不正バージョンが公開されました。
日本時間では 2026年5月12日 04:20〜04:26 頃です。
(寝てる人が多い時間で、まずよかったです)
GitHub Security Advisory では CVE-2026-45321、Critical、CVSS 9.6 として扱われています。
不正バージョンは公開から約20分で外部研究者に検知され、TanStack 側も対応を開始しています。ただし、npm の仕様上すぐに全パッケージを unpublish できるとは限らないため、「公開窓が6分だったから安全」とは言い切れません。
今回の攻撃経路はざっくりこうです。
- 攻撃者が TanStack/router の fork から PR を作る
pull_request_targetで動く GitHub Actions が、fork 側のコードを実行してしまう- その実行で GitHub Actions のキャッシュが汚染される
- 後続の正規リリースワークフローが汚染済みキャッシュを復元する
- ランナー上で攻撃コードが動き、OIDC トークンをメモリから抜く
- その OIDC トークンを使って npm に不正バージョンを公開する
ポイントは、TanStack 側の公開経路としては、npm トークンが盗まれたわけではないこと。
npm から見ると、正規の GitHub Actions OIDC Trusted Publisher 経由で公開されているように見えます。ここがかなり嫌なところです。
影響を受けたパッケージ
影響を受けたのは Router / Start 系が中心です。よく見かけるものだけ抜き出すと、以下のようなパッケージが含まれます。
| パッケージ | 不正バージョン |
|---|---|
@tanstack/react-router | 1.169.5, 1.169.8 |
@tanstack/router-core | 1.169.5, 1.169.8 |
@tanstack/history | 1.161.9, 1.161.12 |
@tanstack/router-cli | 1.166.46, 1.166.49 |
@tanstack/router-plugin | 1.167.38, 1.167.41 |
@tanstack/router-vite-plugin | 1.166.53, 1.166.56 |
@tanstack/react-start | 1.167.68, 1.167.71 |
@tanstack/start-client-core | 1.168.5, 1.168.8 |
全42パッケージの一覧は GitHub Security Advisory に載っています。
逆に、TanStack 公式は以下のファミリーを「確認済みでクリーン」としています。
@tanstack/query*@tanstack/table*@tanstack/form*@tanstack/virtual*@tanstack/store@tanstack/start(メタパッケージ。@tanstack/start-*ではない)
なので、@tanstack/react-query だけを使っているプロジェクトを見て「TanStack だから全部アウト」と判断する必要はありません。
もちろん、同じリポジトリ内で @tanstack/react-router なども使っているなら別です。
マルウェアは何をするか
不正パッケージの package.json には、次のような optionalDependencies が入っていました。
"optionalDependencies": {
"@tanstack/setup": "github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c"
}
@tanstack/setup は npm に存在する正規パッケージではありません。
GitHub の tanstack/router fork network 上にある孤立コミットを参照し、そこで prepare が走ります。最終的に、パッケージ内に紛れ込んだ約2.3MBの難読化 JavaScript router_init.js が実行される仕組みです。
狙われる情報はかなり実務寄りです。
- AWS の IMDS / Secrets Manager
- GCP の metadata service
- Kubernetes の service account token
- HashiCorp Vault token
~/.npmrcの npm トークン- GitHub token(環境変数、
ghCLI、.git-credentials) - SSH 秘密鍵
さらに、被害環境が npm パッケージのメンテナーであれば、そのメンテナーが持つ別パッケージを列挙し、同じ注入を広げようとします。
つまり単なる情報窃取ではなく、自己増殖する npm ワーム寄りの挙動です。
実行後にバックグラウンドで動き続ける挙動も報告されています。
なので、node_modules を消して入れ直しただけで終わり、とは考えないほうがいいです。
影響を受けているか確認する
まずは lockfile を見ます。
# package-lock.json / pnpm-lock.yaml / yarn.lock をまとめて見る
grep -R '79ac49eedf774dd4b0cfa308722bc463cfe5885c' \
package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null
grep -R '@tanstack/setup' \
package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null
ヒットしたら、かなり強い疑いがあります。
次に、代表的な影響パッケージを見ます。
grep -E '@tanstack/(react-router|router-core|history|router-cli|router-plugin|router-vite-plugin|react-start|start-client-core)' \
package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null
この結果に、GitHub Security Advisory に載っている不正バージョンが含まれていないか確認します。
ただし、これはよく使われそうなものを抜いた確認です。
実際には 42 パッケージが対象なので、@tanstack/solid-*、@tanstack/vue-*、@tanstack/start-* などを使っている場合は Advisory の全リストを見てください。
インストール済みの node_modules が残っている場合は、router_init.js も見ます。
find -L node_modules/@tanstack -name router_init.js -print 2>/dev/null
ヒットしたらアウト寄りです。
また、不正パッケージを安全に確認したい場合は、npm install ではなく npm pack を使います。
npm pack @tanstack/react-router@1.169.5
tar -xzf tanstack-react-router-1.169.5.tgz
grep -A3 optionalDependencies package/package.json
ls -la package/router_init.js
npm pack は tarball を落とすだけなので、install lifecycle script は実行されません。
ただし、npm 側で tarball が削除済みなら取得できません。その場合は手元の lockfile、CI ログ、キャッシュ、node_modules を優先して見ます。
4時台という話
今回の公開窓は日本時間で 2026年5月12日 04:20〜04:26 頃です。
人間が手で npm install を叩く時間としては、かなり少なそうです。
ただ、これは人間に限った話です。
GitHub Actions は寝ません。
たとえばこういう環境は確認したほうがいいです。
schedule:トリガーで早朝に CI を回している- Renovate / Dependabot の PR を自動マージしている
- CI で lockfile が更新されうる状態の
npm installを使っている - lockfile をコミットしていない
@tanstack/react-routerや@tanstack/react-startを使っている
特に CI ランナーは、GitHub token、クラウド認証情報、デプロイ用の秘密情報を持っていることが多いです。
手元の開発PCより CI のほうが危ない、というケースは普通にあります。
GitHub Actions / 自動化まわりの確認
まず見るべき時間帯は、UTC 2026-05-11 19:20〜19:30 です。
日本時間なら 2026年5月12日 04:20〜04:30。
念のため、前後を広めに見てもいいです。
gh run list --repo <owner>/<repo> --limit 100
確認するポイントはこのあたりです。
- 該当時間帯に
npm install/npm ci/pnpm install/yarn installが走っていたか - そのプロジェクトが影響パッケージを使っていたか
- lockfile に不正バージョンが入っていたか
- Renovate / Dependabot が TanStack 関連の PR を作成またはマージしていないか
- CI ランナーに渡していた secret の範囲はどこまでか
npm ci を使っていて、かつ攻撃前の lockfile がコミットされていたなら、基本的には不正バージョンを引きません。
ただし、lockfile 自体に不正バージョンが入っていた場合は別です。npm ci は lockfile を忠実に再現するので、不正バージョンも忠実に入れます。
ここは雑に「npm ci なら安全」と言い切らないほうがいいです。
lockfile と npm ci の話
axios のときにも書きましたが、今回も lockfile の有無がかなり効きます。
# 攻撃を受けやすい運用
npm install
# まだ堅い運用
npm ci
ここで言っている npm install は、lockfile がない、古い、CI で更新される、といった運用の話です。
Sponsored
package-lock.json が安全なバージョンを指していて、package.json と整合しているなら、npm install だから即アウトというわけではありません。
npm ci は lockfile に書かれたバージョンを再現します。
攻撃前の lockfile に安全なバージョンが固定されていれば、レジストリに不正バージョンが出ても勝手には上がりません。
逆に、CI で lockfile が更新されうる状態の npm install を使っている、lockfile をコミットしていない、Renovate の自動マージで新バージョンを即取り込む、という運用は刺さりやすいです。
今回のように lifecycle script が起点になる攻撃には、--ignore-scripts も効きます。
npm ci --ignore-scripts
ただし、ネイティブモジュールなど正当な postinstall / prepare が必要なパッケージもあります。いきなり全環境で入れるとビルドが壊れることもあるので、CI で試してからがいいです。
影響を受けていた場合の対応
不正バージョンをインストールしていた場合、パッケージを入れ替えるだけでは足りません。
install 時点でコードが走っているので、その環境は侵害済みとして扱います。
優先度順にやるならこうです。
- 該当環境をネットワークから切り離す
- lockfile、
node_modules、CI ログ、ランナーの作業ディレクトリを保全する - 長時間プロセスや不審な Node.js プロセスを止める、または環境ごと捨てる
- 別の安全な環境で、対象パッケージを修正版へ上げる、または攻撃前の既知の安全バージョンへ戻す
- その環境からアクセスできた secret をローテーションする
- クラウド監査ログ、GitHub audit log、npm publish history を確認する
- 可能ならマシンや CI ランナーを作り直す
同じ開発機を使い続ける場合は、永続化の痕跡も見たほうがいいです。
Snyk の解析では、.claude/router_runtime.js、.claude/setup.mjs、.vscode/setup.mjs、~/.local/bin/gh-token-monitor.sh、~/.config/systemd/user/gh-token-monitor.service、~/Library/LaunchAgents/com.user.gh-token-monitor.plist などに残る挙動も報告されています。
ここが残ったまま secret をローテーションすると、新しい secret もまた抜かれます。
ローテーション対象は、少なくとも以下です。
- npm トークン
- GitHub token / PAT
- SSH 秘密鍵
- AWS / GCP / Azure の認証情報
- Kubernetes service account token
- Vault token
- GitHub Actions Secrets(該当ジョブに渡していたもの)
.envに入れていた API key
特に GitHub Actions の self-hosted runner で動いていた場合は、雑に片付けないほうがいいです。
ランナーが触れた secret の範囲がそのまま被害範囲になります。
まとめ
今回は「TanStack Query がやられた」という話ではありません。
公式に確認済みでクリーンとされている @tanstack/query* と、実際に影響を受けた Router / Start 系を分けて見る必要があります。
一方で、攻撃の中身はかなり重いです。npm トークンを盗んで公開したのではなく、GitHub Actions の正規リリース経路を踏み台にして、OIDC Trusted Publishing で不正パッケージを出しています。
信頼された経路から出ているから安全、とは言えない。
今回の確認ポイントはシンプルです。
@tanstack/react-routerなど影響パッケージを使っているか- 2026年5月12日 04:20〜04:30 頃に install が走っていないか
- lockfile に不正バージョンや
@tanstack/setupが入っていないか - CI ランナーの secret をどこまでローテーションすべきか
そして今後の対策も、結局は地味なところです。
lockfile をコミットする。CI では npm ci を使う。自動マージは少し待たせる。必要なら --ignore-scripts を検討する。
派手ではないけど、こういう運用がだいぶ効きます。
参考リンク
- Postmortem: TanStack npm supply-chain compromise - TanStack Blog
- Malware in 42 @tanstack/* packages exfiltrates cloud credentials, GitHub tokens, and SSH keys - GitHub Security Advisory
- Several npm latest releases are compromised - TanStack/router Issue #7383
- Hardening TanStack After the npm Compromise - TanStack Blog
- TanStack Npm Packages Compromised Inside The Mini Shai Hulud Supply Chain Attack - Snyk