npm install なしで payload が実行される — Keyv / ChainDrop 攻撃の仕組みと検査手順(前編)

sho

この記事は全2回の前編です。
前編(この記事): 攻撃の2経路と、リポジトリを開く前の検査手順
後編: トークン失効の順序を間違えない — Keyv / ChainDrop 侵害時の初動と予防(後編)

Keyv / ChainDrop 攻撃は、2026年8月4日に keyv ほか11パッケージの侵害から始まった npm サプライチェーン攻撃です(IoC に Shai-Hulud: Here We Go Again を含みます)。過去の npm 侵害と違うのは、リポジトリを VS Code で開くか、その中で Claude Code のセッションを開始した時点で payload が実行される点です。Aikido はこう記述しています。

“the payload executes automatically the next time any developer opens the repository in VS Code or starts a Claude Code session inside it, with no npm install required”
(次に開発者がそのリポジトリを VS Code で開くか、その中で Claude Code セッションを開始した時点で payload が自動的に実行される。npm install は不要である)

「攻撃期間中に npm install を叩いていない」は安全の証明にならない。最初に取る行動は clone して IDE で開くことではなく、チェックアウトせずに設定ファイルだけを読むことです。

攻撃は2経路で走る — パッケージ経路とソース経路

パッケージ経路:preinstall から Bun 1.3.13 を落として実行する

侵害版の name@version をインストールすると preinstall が走り、正規の Bun ランタイム(bun-v1.3.13、公式 oven-sh/bun のリリース URL から取得)で payload を実行して、実行後にランタイムを削除します。配布元が正規なのでバイナリのレピュテーションでは検知されません。痕跡は User-Agent Bun/1.3.13npm-cache[.]com:443/router への通信です。

初期侵害の11パッケージは以下(Wiz 公式 IoC CSV と StepSecurity が一致。後者は 2026-08-04 09:09〜10:28 UTC のタイムスタンプ付き)。入口であって全体像ではないので、二次拡散分も調べます。

keyv 6.0.0
flat-cache 6.1.24
file-entry-cache 11.1.6
cacheable-request 13.0.20
cacheable 2.5.1
@cacheable/memory 2.2.1
cache-manager 7.2.10
@cacheable/node-cache 3.1.2
@cacheable/utils 2.5.1
@cacheable/net 2.1.1
ecto 5.0.1

ソース経路:リポジトリを開いた時点で走る

StepSecurity によれば、同一コミットで3つのファイルが仕込まれました。

  • .claude/settings.jsonSessionStart hook
  • .vscode/tasks.json"runOptions": {"runOn": "folderOpen"}
  • .claude/math_init.js — 第2段階のペイロード

攻撃者はコミットの著者を claudeclaude@users.noreply.github[.]com)に偽装しました。コミット一覧を流し読みする運用では、AI エージェントの自動生成コミットとして通過します。

Elastic Security Labs は、窃取した GitHub App トークンでアクセス可能な各リポジトリの最大50ブランチへ5ファイルをコミットする挙動を報告しています。main.claude/ が無いことの確認は、main を検査した結果にすぎません。

provenance が証明していること、していないこと

正規の GitHub Actions workflow(OIDC)が悪意あるソースをビルドし、valid な provenance 付きで npm に公開されました。provenance の証明範囲は「誰が・どの workflow で・どの source をビルドしたか」までで、source に悪意がないことは範囲の外です。検証が通ったことを安全の根拠にする設計は、この攻撃を通します。

規模の数字を影響判定に使わない

2026-08-04〜05 時点で Aikido / SafeDep / Wiz / StepSecurity の4社が 444〜446 パッケージ・1,381〜2,317 バージョンと報告し、2026-08-07 時点でも更新されていません。一部メディアの 860+ / 1,280+ は一次ソースで再現確認できず、TheHackerNews も広い集計は完全な公開リストから再現できなかったと注記しています。総数で影響判定を行うと参照した記事で結論が変わるので、判断材料は自分の lockfile の name@version と IoC、実行ログに絞ります。

.claude/settings.json.vscode/tasks.json という攻撃面

AI コーディングエージェントを使う環境で、見落とされている攻撃面がここです。

Claude Code の設定スコープと trust の効き方

Claude Code の設定ファイルはスコープが3段あります。

ファイルスコープgit で共有されるか
~/.claude/settings.json全プロジェクト共通不可
.claude/settings.jsonプロジェクト単位可(リポジトリにコミットできる)
.claude/settings.local.jsonプロジェクト単位原則不可

攻撃経路は2行目です。リポジトリにコミットできるので clone すれば手元に来ます。SessionStart の matcher は startup / resume / clear / compact / fork の5種類で、開始と再開の両方が発火条件です。過去に clone したリポジトリでセッションを再開する動作も resume で条件を満たします。

trust ダイアログとの関係は、公式ドキュメントで範囲が限定されています。

“Claude Code applies them only after you accept the workspace trust dialog for that workspace. Until then, Claude Code reads the rules but doesn’t apply them.”
(そのワークスペースの workspace trust ダイアログを承認した後にのみ適用される。それまでは読み込まれるが適用されない)

この記述の対象はプロジェクト .claude/settings.jsonpermissions.allowadditionalDirectories です。deny / ask は制限を課すだけなので影響を受けません。hook の実行が同じゲートに乗るとは書かれていないので、trust ダイアログを承認していないことを hook 実行の防御と見なす設計は避けるべきです。信頼の記録は git リポジトリのルート単位で保存されます。

claude -p では trust 検証が無効になる

公式ドキュメントには2箇所、明示的な記述があります。

“In non-interactive mode with -p, no dialog appears and the rules stay ignored.”
-p による非対話モードではダイアログは表示されず、ルールは無視されたままになる)

“Trust verification is disabled when running non-interactively with the -p flag”
-p フラグによる非対話実行時、trust 検証は無効になる)

未検証のリポジトリを claude -p の無人実行に含めた時点で、人間が判断を挟む機会は一度も来ません。対象は検査済みリポジトリに限定し、外部リポジトリを自動処理するパイプラインには後述の静的検査を前段に置きます。

devcontainer は未知のリポジトリの検分箱ではない

「怪しいリポジトリは devcontainer の中で開けばいい」という発想は、公式に否定されています。

“When executed with –dangerously-skip-permissions, dev containers do not prevent a malicious project from exfiltrating anything accessible inside the container, including the Claude Code credentials stored in ~/.claude. Only use dev containers when developing with trusted repositories”
--dangerously-skip-permissions 付きで実行した場合、dev container は悪意あるプロジェクトがコンテナ内のアクセス可能なもの——~/.claude に保存された Claude Code の資格情報を含む——を外部送信することを防げない。dev container は信頼できるリポジトリでの開発にのみ使用すること)

隔離されているのはホストのファイルシステムの一部だけで、盗まれると困るものはコンテナの内側にあります。

VS Code 側には確実な防御ラインが1本ある

.vscode/tasks.json"runOptions": {"runOn": "folderOpen"} はフォルダを開いた時に実行されます。task.allowAutomaticTasks の既定値 "off" では初回に許可/拒否のプロンプトが出ますが、"on" なら信頼済みワークスペースで無確認に実行されます。設定値より強い保証がここにあります。

“Automatic tasks never run in an untrusted workspace, regardless of this setting.”
(この設定に関わらず、自動タスクが未信頼のワークスペースで実行されることはない)

Workspace Trust を「信頼しない」(Restricted Mode)に保つ限り、task.allowAutomaticTasks の値と無関係に folderOpen タスクは走りません。Restricted Mode では tasks 以外に AI agents、terminal、debugging、拡張も無効化されます。ステータスバーのバッジは、安全に閲覧できている状態のサインとして読めます。

claude --version を今すぐ確認する

Claude Code 自体にも、この攻撃面に対応する修正済みの脆弱性が2件あります。

CVE概要CVSS影響バージョン修正版
CVE-2025-59536 (GHSA-4fgq-fpq9-mr3g)startup trust ダイアログを承認する前にプロジェクト内のコードが実行され得た(CWE-94)8.7 High< 1.0.1111.0.111
CVE-2026-21852 (GHSA-jh7p-qr78-84p7)設定ファイルが ANTHROPIC_BASE_URL を攻撃者のエンドポイントに向けていると、trust プロンプト表示前に API リクエストが飛び、Anthropic API キー等が外部送信され得た(CWE-522)5.3 Medium< 2.0.652.0.65

claude --version を実行し、少なくとも 2.0.65 以上を維持します。この2件は「trust を承認する前にリポジトリ制御のコードが動く」という、今回のキャンペーンが使った構造に対応しています。

このキャンペーン限りの話ではない

Datadog Security Labs は、コーディングエージェントでリポジトリを信頼する行為を「そのセットアップスクリプトを実行するのと同じものとして扱え」と表現しています。挙げられている事例は今回のキャンペーンとは別です。

  • MAL-2026-3648: 3件の悪意ある npm パッケージが Claude Code の SessionStart hook をインストールし、感染プロジェクトの再オープン時に実行されます
  • Codex のプロジェクトスコープ MCP 設定が、プロジェクトを開いた際に攻撃者制御のプロセスを起動します

数か月前に clone したリポジトリを久しぶりに開き直す操作も、同じ経路の発火条件を満たします。

今日できる検査 — チェックアウトせずに中身を見る

以下は anthropics/claude-code で動作確認したコマンドです(git 2.50.1)。GitHub REST API の Contents endpoint は公開リポジトリなら認証不要です。

手順A:clone せずに API で中身だけ見る

curl -s "https://api.github.com/repos/OWNER/REPO/contents/.claude"
curl -s "https://api.github.com/repos/OWNER/REPO/contents/.vscode"
curl -s "https://api.github.com/repos/OWNER/REPO/contents/.devcontainer"

返ってきた JSON の download_url から個別ファイルの raw を取得できます。ローカルに何も置かないので、この段階で発火する経路はありません。

手順B:--no-checkout でワーキングツリーに展開せず確認

git clone --no-checkout --depth 1 https://github.com/OWNER/REPO.git repo-inspect
cd repo-inspect
git ls-tree -r HEAD --name-only | grep -E '^\.(claude|vscode|devcontainer)/'
git show HEAD:.claude/settings.json
git grep -n -i "SessionStart\|runOn\|folderOpen\|preinstall\|postinstall" HEAD -- '*.json' '*.js' '*.mjs'

--no-checkout によりオブジェクトは取得しますが、ワーキングツリーには展開されません。git showfatal: path ... does not exist が出るのは対象ファイルが存在しないだけで、正常な動作です。

手順C:全ブランチを横断する

git ls-remote --heads https://github.com/OWNER/REPO.git
git ls-tree -r origin/BRANCH --name-only | grep -E '^\.(claude|vscode|devcontainer)/'
git show origin/BRANCH:.claude/settings.json

ChainDrop は最大50ブランチに撒いていたので、HEAD だけでは足りません。

相関させる IoC

単独の指標では確定しません。path・サイズ・SHA-256・隣接する package.json を相関させて判断します。

ファイルサイズSHA-256
setup.mjs(initial)29,918 bytes54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668
setup.mjs(second wave)11,017 bytesfd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb
Math_Symbol.js / math_init.js727,680 bytes9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc

ネットワークとその他の指標は npm-cache[.]com:443/router、User-Agent Bun/1.3.13、Ethereum contract 0xE1f239…93103、文字列 Shai-Hulud: Here We Go Again

なお Wiz の公開ブログにこの3ファイルの SHA-256 は載っていません。Wiz が掲載している SHA256 はマシンフィンガープリント用途の別物で、ファイルハッシュと混同して照合すると空振りします。

まとめ

今日のうちに手を動かせるものを4つ挙げます。

  1. claude --version を実行して 2.0.65 以上を確認します。下回っていれば更新します
  2. VS Code の task.allowAutomaticTasks"off" のまま維持し、未検証のリポジトリでは Restricted Mode を解除しません
  3. 外部から取り込むリポジトリは git clone --no-checkout --depth 1git ls-tree.claude / .vscode / .devcontainer を先に読み、git ls-remote --heads でブランチを横断します
  4. claude -p を無人実行しているパイプラインの対象を洗い出し、検査済みのものだけに絞ります

失効の順序・判定の書き方・予防策の選定は、後編で扱います。1点だけ先に書きます。感染を疑う端末からトークンを失効させない。 失効時の 40x 応答を引き金にする watcher が仕込まれているためです。

FAQ

攻撃期間中に npm install していなければ安全ですか

いいえ。侵害されたリポジトリを VS Code で開くか、その中で Claude Code のセッションを開始した時点で、.vscode/tasks.json の folderOpen タスクや .claude/settings.jsonSessionStart hook 経由で payload が実行されます。Aikido はこの経路を “with no npm install required”(npm install は不要)と記述しています。install 履歴の有無は判定材料の一つにすぎず、設定ファイルとエンドポイントの痕跡を別に確認します。

未知のリポジトリを devcontainer の中で開けば安全に検分できますか

いいえ。Claude Code の公式ドキュメントは、--dangerously-skip-permissions 付きで実行した場合、dev container は悪意あるプロジェクトが ~/.claude の資格情報を含むコンテナ内のデータを外部送信することを防げないと記述し、利用を信頼できるリポジトリでの開発に限定するよう案内しています。未知のリポジトリは、チェックアウトせずに git ls-tree や GitHub REST API で中身だけ読みます。

claude -p を CI で使っています。何を確認すればいいですか

-p による非対話実行では trust ダイアログが表示されず、trust 検証が無効になると公式ドキュメントに記述されています。未検証のリポジトリを対象に含めると、人間が判断を挟む機会がないまま処理が進みます。対象を検査済みのものに限定し、claude --version が 2.0.65 以上(CVE-2026-21852 の修正版)であることを確認します。

参考情報

  • Aikido(https://www.aikido.dev/blog/keyv-and-friends-compromised-in-npm-supply-chain-attack)
  • SafeDep(https://safedep.io/keyv-npm-supply-chain-compromise/)
  • Wiz(https://www.wiz.io/blog/keyv-and-cacheable-npm-supply-chain-attack)
  • Wiz IoC CSV(https://github.com/wiz-sec-public/wiz-research-iocs/blob/main/reports/keyv-packages.csv)
  • StepSecurity(https://www.stepsecurity.io/blog/chaindrop-npm-worm)
  • Elastic Security Labs(https://www.elastic.co/security-labs/shai-hulud-chaindrop-npm-supply-chain)
  • Snyk(https://snyk.io/blog/inside-keyv-npm-compromise-preinstall-malware-trusted-provenance-ide-hooks/)
  • Datadog Security Labs(https://securitylabs.datadoghq.com/articles/coding-agent-project-trust-code-execution-before-first-prompt/)
カテゴリー: 業界最新情報