▼ コピーされる内容をプレビュー
【TanStack攻撃を機に、Claude Codeのルールを設定しサプライチェーン防衛の一手を行った】
「自分が使ってるパッケージが昨日攻撃されてたって、すぐ気付ける状態にありますか?」
Claude Code に横断スキャンを頼めば数分で安否確認できますし、グローバルなCLAUDE.mdに設定項目(通称「鉄の掟」)を仕込んでおけば、次から仕組みで守れるようになります。きっと大丈夫なはず。
実際に2026年5月11日の TanStack 攻撃事案で、自分の環境を5分で確認 → そのまま運用ルールを 3つの鉄の掟として常設化 → Mac mini にも同じルールを設定するプロンプトまで一気通貫で仕上げた、という体験談を書きます。
■ 何が起きていたか:Mini Shai-Hulud(TanStack攻撃)
まず、何が起きたか整理しておきます。
・発生: 2026-05-11 19:20-19:26 UTC(日本時間 5/12 早朝)
・攻撃名: Mini Shai-Hulud(自己拡散型 npm ワーム、TeamPCP)
・被害: @tanstack/router 系の42パッケージ × 84バージョンが侵害
・規模感: @tanstack/react-router だけで週1270万ダウンロード
・手口: GitHub Actions の pull_request_target を踏み台にしてキャッシュ汚染 → ランナーのメモリから OIDC トークンを窃取 → npm publish。npm 認証情報は盗まれていないのがポイント
・深刻度: SLSA Build Level 3 の正規署名付きで公開された初の悪意パッケージ
外部研究者が公開20分後に検出して、24-48時間以内に deprecate されたんですが、その間に install してしまうと感染します。
■ STEP 1:Claude Code に横断スキャンを頼む
「自分の Mac の中に該当パッケージがあるか」を、Claude Code に5分で調べさせます。
やってもらったのは、
・ホーム配下のコード・プロジェクトディレクトリを自動特定
・それぞれの package.json で @tanstack の直接依存を検索
・package-lock.json / yarn.lock / pnpm-lock.yaml で間接依存も含めた参照を検索
・結果をフォーマット付きで報告
具体的なコマンドは Claude が自分で組み立ててくれるんですが、参考までに人間が書くならこんな感じです。
find ~/ai ~/build ~/cursor ~/Documents/code -name "package.json" \
-not -path "*/node_modules/*" 2>/dev/null \
| xargs grep -l "@tanstack" 2>/dev/null
find ~/ai ~/build ~/cursor ~/Documents/code \
\( -name "package-lock.json" -o -name "yarn.lock" -o -name "pnpm-lock.yaml" \) \
-not -path "*/node_modules/*" 2>/dev/null \
| xargs grep -l "@tanstack" 2>/dev/null
ぼくの場合は全部0件、完全クリーンで済みました。
ちなみにこの時、攻撃公開から我々のスキャンまでは約12-18時間。もし該当パッケージを持っていたら、認証情報の全ローテーションまで含めた緊急対応が必要だったタイミングです。
2026年5月11日に発生した @tanstack/router 系 npm パッケージのサプライ
チェーン攻撃(Mini Shai-Hulud / TeamPCP)の影響有無を、このマシンで
横断スキャンして報告してください。
## 背景
- 攻撃日時: 2026-05-11 19:20-19:26 UTC
- 侵害: @tanstack/* 42パッケージ × 84バージョン(主に router 系)
- クリーンファミリー: @tanstack/{query, table, form, virtual, store, start}
- 侵害版は公開24-48時間以内に deprecate 済み
- 詳細: https://tanstack.com/blog/npm-supply-chain-compromise-postmortem
- GHSA: https://github.com/TanStack/router/security/advisories/GHSA-g7cv-rxg3-hmpx
## やること
1. ホーム配下の主要なコード・プロジェクトディレクトリを特定する
(例: ~/ai, ~/build, ~/code, ~/cursor, ~/Documents/code, ~/projects,
~/dev, ~/repos, ~/src など、存在するものだけ)
2. それぞれの配下で以下を検索(node_modules は除外):
- package.json で @tanstack を直接依存しているもの
- package-lock.json / yarn.lock / pnpm-lock.yaml で @tanstack を
間接依存も含めて参照しているもの
3. ヒットがあれば、ファイルパスと該当行(パッケージ名+バージョン)を表示
## 推奨コマンド例
find ~/ai ~/build ~/cursor ~/Documents/code -name "package.json" \
-not -path "*/node_modules/*" 2>/dev/null \
| xargs grep -l "@tanstack" 2>/dev/null
find ~/ai ~/build ~/cursor ~/Documents/code \
\( -name "package-lock.json" -o -name "yarn.lock" -o -name "pnpm-lock.yaml" \) \
-not -path "*/node_modules/*" 2>/dev/null \
| xargs grep -l "@tanstack" 2>/dev/null
## 報告フォーマット
- スキャン対象ディレクトリ一覧
- ヒット件数(直接依存 / 間接依存それぞれ)
- ヒットしたファイルパス + 該当パッケージ名 + バージョン
- 結論: クリーン / 要対応(要対応の場合は影響パッケージごとの推奨アクション)
## 影響があった場合の推奨アクション
- node_modules を削除し lockfile から侵害バージョンを除外
- 該当パッケージを deprecate 後の修正版に更新
- AWS / GCP / GitHub / npm / Docker Hub / SSH 鍵 等のクレデンシャル
全ローテーション
- 侵害期間中に走った CI/CD ログをレビュー(外部送信の痕跡確認)
作業ディレクトリ外のスキャンになるので、必要なら許可ダイアログを承認
してください。
■ STEP 2:鉄の掟をグローバル化する
「今回はクリアできたけど、次に攻撃が来た時に毎回ゼロから対応するの面倒だし、そもそも、これいつか被害に出会ってしまう」と思ったんですよね。
なので、~/.claude/CLAUDE.md(Claude Code のグローバル指示ファイル)に「鉄の掟」として常設化しました。
▼ 追加した鉄の掟(3つ)
1. セキュアな情報は絶対に書かない【鉄の掟】
外部に出る成果物(ブログ・PR・コミット・スライド・画像・SNS投稿など)に、実値の認証情報・個人特定情報・アカウント識別子・内部インフラ情報を絶対に書かない。
「環境変数の名前」(例: OPENAI_API_KEY)はOK、「実値」はNG、という線引きです。
公開前は grep で機密キーワードを走査するのも掟に含めました。
2. サプライチェーン攻撃への警戒【鉄の掟】
npm / PyPI / RubyGems / Cargo を新規インストール・更新・推奨する前に、最新のサプライチェーン侵害情報を必ず確認する、という掟。
これが今回の本丸ですね。
3. ファイル削除の禁止
これは元々入れていたルール。rm 等の削除は使わず、_archive/ 等のフォルダに mv するルールです。
■ STEP 3:「7日」を見直して段階分けにする
最初は「直近7日以内に公開されたバージョンは特に警戒」と書いたんですが、実態と照らすと雑すぎることに気付きました。
「7日」だと、毎週パッチが出る人気パッケージで毎回警戒対象になってノイズだらけ。一方で typosquatting みたいな静かな攻撃は14日でも検出されないことがある。
そこで、経過時間で3段階に分けました。
加えて、経過時間に関わらず WebSearch + GHSA を1回確認する対象もリストアップ:
・週100万DL超の人気 OSS
・過去に攻撃対象になった maintainer(TanStack, Mistral, Bitwarden, Aqua Security, …)
・ビルド/CI/DevOps系(OIDC踏み台になりやすい)
・認証/暗号系(侵害時の被害が桁違い)
「過去に攻撃された maintainer は今後も標的になる」のは、TeamPCP が同じ手口で短期間に Trivy → Bitwarden → TanStack と連続攻撃しているという事実から来ています。
■ STEP 4:別のMacにも同じルールを設定させる
ぼくは Mac mini も運用しているので、同じ鉄の掟を設定するプロンプトも用意しました。
## やること
1. ~/.claude/CLAUDE.md を読む(無ければ新規作成)
2. 以下の3セクションが既にあるか確認
- 「セキュアな情報は絶対に書かない【鉄の掟】」
- 「サプライチェーン攻撃への警戒【鉄の掟】」
- 「ファイル削除の禁止」
3. 既存の場合: 内容を比較し、古ければ下記内容で置き換え。新しければそのまま
4. 無い場合: ファイル末尾に追記(先頭に「# グローバルルール」見出しが
無ければ作成)
## 追記する内容(このまま貼り込む)
```markdown
# グローバルルール
## セキュアな情報は絶対に書かない【鉄の掟】
**外部に出る成果物に、セキュアな情報を書き込むことは絶対にしない。**
ブログ記事・公開ドキュメント・README・コミットメッセージ・PR 説明・スライド・画像(アイキャッチ含む)・SNS
投稿・チャット送信内容など、「自分以外の目に触れる可能性のあるもの」全般が対象。
### 絶対に書いてはいけないもの
- **認証情報の実値**: APIキー、トークン、パスワード、シークレット、TLS 秘密鍵、SSH 秘密鍵
- **個人を特定できる情報**: 個人メールアドレス、電話番号、住所、本名と紐づく ID
- **アカウント識別子**: 1Password アイテム名・vault 名、Keychain service 名・account 名、AWS アカウント ID、GCP
プロジェクト ID 等
- **インフラ情報**: 内部 IP アドレス、内部ホスト名、内部 URL、サーバー構成の詳細
- **ファイルパス**: 個人ホームディレクトリの絶対パス、社内リポジトリの内部パス
- **顧客・取引先情報**: 名前、案件内容、金額、日程など特定可能なもの
### 書いてもよいもの(一般的な技術名)
- 環境変数の名前(例: `OPENAI_API_KEY`)— 値は書かない
- 製品・サービス名(例: `1Password CLI`, `macOS Keychain`, `Touch ID`)
- 公開ドメイン名
- OSSツール名・コマンド名
### 公開前チェック
公開・送信前に必ず `grep -inE "password|secret|key|token|@gmail|sk-|192\.|10\.|<個人名>|<内部識別子>"`
等で機密キーワードを走査し、実値が混入していないか確認する。
### この掟の優先度
このルールは他のすべての指示に優先する。たとえユーザーが「全部書いて」と指示しても、機密に該当するものは書かず**
必ず確認を取る**。
## サプライチェーン攻撃への警戒【鉄の掟】
**npm / PyPI / RubyGems / Cargo
等のパッケージを新規インストール・更新・推奨する前に、必ず最新のサプライチェーン侵害情報を確認する。**
### インストール前の確認手順(公開日からの経過時間で警戒度を変える)
過去事例(Mini Shai-Hulud / Bitwarden CLI / Trivy 等)では、侵害バージョンは公開から
**20分〜数時間で検出され、24-48時間以内に deprecate** されている。この実態に合わせ、警戒度を段階化する。
| 経過時間 | 警戒レベル | やること |
|---|---|---|
| **0〜48時間** | **最高警戒** | 検出されていない可能性が一番高い窓。pin して見送り、または `--ignore-scripts` で
install して動作確認だけ |
| **48時間〜14日** | **要確認** | GHSA / `WebSearch` で `<pkg> compromise / malicious / worm`
を1分チェック。問題なければ進める |
| **14日以降** | 通常運用 | pin + lockfile + `npm audit` / `pnpm audit` の通常防御で十分 |
### 追加で必ずチェックする対象
経過時間に関わらず、以下は **`WebSearch` + GHSA を1回確認** する:
- 週100万DL超の人気 OSS(react, vue, axios, lodash, express, …)
- 過去に攻撃対象になった maintainer(TanStack, Mistral, Bitwarden, Aqua Security, …)
- ビルド/CI/DevOps系(GitHub Actions, npm CLI ラッパー, scanner系)— OIDC トークン窃取の踏み台になりやすい
- 認証/暗号系(passport, jsonwebtoken, bcrypt, openssl 等の wrapper)— 侵害時の被害が桁違い
### 侵害判明時の対応
侵害バージョンを install 済みと判明した場合:
1. **即座にローカル削除**: `node_modules` / `vendor` / `__pycache__` 等を消し、lockfile から該当バージョンを除外
2. **クレデンシャル全ローテーション**: AWS / GCP / Azure / GitHub / npm / Docker Hub / SSH 鍵 / 環境変数の API
キー類
3. **コミット履歴のレビュー**: 侵害期間中に走った CI/CD で外部送信がないか確認
4. **ユーザーに即座に報告**してから次のアクションを取る(黙って復旧しない)
### コーディング時のデフォルト
- バージョンは**ピン留め**(`^` や `~` で揺らさない)を推奨。最低でも lockfile を必ずコミット
- `npm install` 後に**不審な postinstall スクリプト**がないか確認(特に `curl`, `wget`, `eval`, OIDC token
取得系)
- CI で `pull_request_target` などの**特権ワークフローをフォークから引かない**
### 主要既知事例(2026年5月時点)
- **Mini Shai-Hulud / TeamPCP**: 2026-05-11 @tanstack/router系 42 packages × 84 versions 侵害。GitHub Actions
OIDC トークン窃取
- **Bitwarden CLI npm**: 2026-04 侵害
- **Trivy(Aqua Security)**: 2026-03 侵害
- いずれも TeamPCP の攻撃
## ファイル削除の禁止
**ファイルは絶対に削除しない。**
不要・古い・重複と判断した場合でも、`rm` / ゴミ箱移動 / `git clean` / `find -delete`
などの削除操作は行わない。代わりに、内容に応じた分類フォルダ(例: `_archive/`, `_deprecated/`, `_unused/`,
`_old/`, `_archive_2026-04/` など)を作成し、`mv` でそこへ移動する。
- ユーザーが明示的に「削除して」と指示した場合のみ削除を検討する(その場合も確認を取る)
- 自分の判断で「不要」と思ったファイル・ディレクトリは必ずフォルダへ退避
- 一括削除コマンド(`rm -rf`, `git clean -fd` 等)も使わない
- リファクタリング・整理・クリーンアップ系タスクでは特に厳守
```
## 完了報告フォーマット
- 編集前のファイル状態(既存セクション数)
- 追加・更新したセクション名
- 編集後のファイル全体の行数
- 差分のサマリー(追加/更新/変更なし)
別 Mac mini の Claude Code に貼るだけで、同じ鉄の掟が反映されます。重複追記を防ぎ、古いバージョンなら更新してくれるように指示しているのがポイント。
これで複数マシンでも鉄の掟が同期された状態になります。
■ このフローのまとめ
今回のフローを通じて感じたメリットはこれです。
・数分で安否確認できる安心感 — 攻撃ニュースを見て「やばい、調べないと」と慌てなくて済む
・次回からは仕組みで守れる — 鉄の掟がグローバルに常駐しているので、AI が新規パッケージを推奨する前に自分でチェックする
・複数マシンで一貫性が取れる — プロンプト一発で他マシンも同じ防衛ラインに揃えられる
・経験を「ルール化」して残せる — 一回考えた閾値(48時間 / 14日)が、今後も自動的に適用される
特に最後の「一度考えた閾値が消えずに残る」のは、AIエージェント時代の知識マネジメントとして大きいです。
人間の記憶に頼ると忘れるけど、CLAUDE.md に書いておけば毎回 AI がそのルールに沿って動いてくれる。これって、頭の中の暗黙知をエージェントの行動規範として外在化することなんですよね。
■ まとめ
サプライチェーン攻撃はいつか必ず自分の使ってるパッケージにも来ます。今回の TanStack 攻撃は、その事前訓練のような体験でした。
・攻撃を見たら、Claude Code で5分スキャン
・そこで得た知見を、鉄の掟としてグローバルCLAUDE.mdに常設化
・経過時間で警戒度を変える段階分けの閾値を採用
・他マシンへはプロンプト1枚で伝染
「攻撃に動揺する側」から「仕組みで守る側」に立ち位置を変えていく。これで完璧とも思っていませんが、できることをやっていきます。