GoogleのAIエージェント開発キット Agent Development Kit (ADK) for Python(PyPI名 `google-adk`)に、CVSS v4.0 で 10.0(CRITICAL) のコードインジェクション CVE-2026-79696 があります。アドバイザリの記述では対象は 2.0.0 から 2.6.0、修正版は 2.7.0(2026年8月13日公開)です。
何が起きるかというと、`adk web` を動かしていて、かつ pytest が入っている環境に対し、攻撃者が細工したテストセッションの再生(test session replay)を送ることで、認証なしに任意のコードを実行されるというものです。アドバイザリはこの条件として Python(OSS)、Cloud Run、GKE の各環境を挙げています。
2026年9月9日時点の最新版は 2.8.0(8月26日公開)です。開発機でも本番でも、`google-adk` を入れているならまずバージョンを確認してください。
今すぐ行う対策
- ✓入っているバージョンを確認する。 `pip show google-adk` の Version 行、または `pip list | findstr google-adk`(Linux/macOSは `grep`)で分かります
- ✓2.7.0以降へ更新する。 `pip install -U google-adk`。2026年9月9日時点の最新は 2.8.0 です
- ✓2.6.1・2.6.2・2.6.3 を使っている場合も更新する。 アドバイザリの範囲表記は「2.0.0〜2.6.0」ですが、この3版にも修正が入っていないことをソースで確認しました(後述)
- ✓`adk web` をインターネットに公開しない。 これは開発用のWeb UIです。Cloud RunやGKEで動かしているなら、公開範囲と認証の設定を今すぐ確認してください
- ✓本番イメージから pytest を外す。 アドバイザリは「pytestが入っている環境」を条件に挙げています。テスト用の依存を本番コンテナに含めない、という一般的な作法がそのまま緩和策になります
- ✓信頼できないYAMLのエージェント設定を読み込まない。 後述のとおり、修正後も第三者パッケージ名の指定までは塞がれていません
このスコアの出所を明示します。 上の 10.0 は、このCVEの採番機関(Google)が付けた CVSS v4.0 の値です。ベクタは `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H`。GitHubのセキュリティアドバイザリ GHSA-q9cv-5mjc-7cjc も同じ v4.0 で 10.0 CRITICAL としています。
NVDにはまだNIST自身(Primary)の評価が載っていません。2026年9月9日時点のステータスは `Received` で、v3.1のスコアは付いていません。後日、別の数字が出る可能性があります。弱点の種類は CWE-184(不許可入力リストの不備) です。
CISAのKEV(悪用が確認された脆弱性カタログ)には、2026年9月9日時点で登録されていません。
何が起きていたのか:拒否リストに漏れがあった
ADKは、エージェントの構成をYAMLで書けます。その中で「コード参照」として、ツールやコールバックに使うPythonの関数・クラスを名前で指定できます。ここに、危険な標準ライブラリを名前で拒否するdenylist(拒否リスト)が置かれていました。
修正コミットの説明が、問題をそのまま書いています。拒否リストは危険なモジュールを1つずつ名前で挙げていたため、挙げ漏れたものはそのまま到達可能でした。具体的には `profile` は入っていたが `cProfile` は入っておらず、`pdb` はあったが `bdb`・`trace`・`timeit`・`pydoc` は無い、という状態でした。
そして開発元はこう書いています。「それらのいくつかは渡された文字列をそのまま実行し、コンストラクタの引数を必要としない。だからツールやコールバックとして名前を挙げるだけで、既存の緩和策を両方すり抜けて任意コードが動いた」。
つまり「危険なものを列挙して塞ぐ」という設計そのものが破れていたわけです。CWE-184(不許可入力リストの不備)という分類は、まさにこの形を指しています。
修正は「標準ライブラリを丸ごと遮断」
2.7.0 に入った修正は、拒否リストを足すのではなく、判定の向きを変えるものでした。
修正コミット a16f6da(2026年8月7日)のメッセージより
Block the standard library outright via `sys.stdlib_module_names`.
(sys.stdlib_module_names を使って標準ライブラリを丸ごと遮断する)
Configs only ever name the agent's own package, `google.adk`, or a
third-party integration, so nothing legitimate breaks and the list
stops needing a revisit every Python release.
(設定が名指しするのはエージェント自身のパッケージ、google.adk、
第三者連携のいずれかだけなので、正当な用途は壊れず、
Pythonのリリースごとにリストを見直す必要も無くなる)`sys.stdlib_module_names` はPythonが自分で持っている標準ライブラリ名の集合です。個別に列挙する代わりにこれを使えば、Pythonが新しいモジュールを追加しても拒否側が自動で追随します。明示的な拒否リストは、標準ライブラリとして報告されなくなったが今もインポートできるもの(`distutils` や CPython の `test` パッケージ)のために残されています。
★影響範囲の注意:2.6.1〜2.6.3も未修正です
ここは当サイトで実際にソースを確認した結果を書きます。
アドバイザリの影響範囲は「2.0.0 through 2.6.0」ですが、PyPIには 2.6.1(7月31日)・2.6.2(8月4日)・2.6.3(8月7日) が存在します。この3つは範囲の書き方から外れて見えます。そこで 2.6.0・2.6.1・2.6.2・2.6.3・2.7.0・2.8.0 の6タグから `src/google/adk/agents/config_agent_utils.py` を取得し、中身を突き合わせました。
- ▸v2.6.0・v2.6.1・v2.6.2・v2.6.3:4つとも `sys.stdlib_module_names` を使っていない。しかも4ファイルは9,988バイトでSHA-256が完全に一致(`9c39ecfc30ceb4d0…`)します。つまりこの4版のファイルは同一物で、いずれも未修正です
- ▸v2.7.0:`sys.stdlib_module_names` を使っている(11,296バイト・修正済み)
- ▸v2.8.0:同上(12,298バイト・修正済み)
つまり 2.6系はどの版を使っていても修正が入っていません。「2.6.1に上げたから範囲外」と判断しないでください。2.7.0以降へ上げるのが唯一の対処です。
なお、修正コミットの日付は2026年8月7日で、2.6.3 の公開(8月7日)と同じ日です。取り込まれたのは次のマイナーリリースである 2.7.0(8月13日)でした。
開発元自身が「塞ぎ切ってはいない」と書いている
修正コミットのメッセージは、最後にこう締めくくられています。「拒否リストは第三者パッケージまではカバーできない。ローダーは名前で解決するため、これは攻撃面を狭めるものであって、閉じるものではない」。
これは重要な但し書きです。2.7.0以降でも、YAMLのエージェント設定に第三者パッケージの名前を書かれれば、それは読み込まれます。したがって更新は必須ですが、それだけで終わりにせず、「誰が書いたYAMLをエージェントに読ませているのか」を運用として押さえてください。ユーザーから受け取った設定をそのままロードする作りになっていないか、が分かれ目です。
この件から持ち帰れること
拒否リストは、書いた時点の知識で止まります。今回は `profile` を書いた人が `cProfile` を忘れ、`pdb` を書いた人が `bdb` を忘れました。Pythonが次のバージョンでモジュールを1つ増やせば、そのたびにリストは古くなります。「危険なものを数え上げる」設計は、数え漏れが1つでもあれば破れるという点で、そもそも分の悪い賭けです。
修正後の形は逆で、「許してよいものだけを通す」に寄せています。エージェント設定が名指しする先は自分のパッケージか `google.adk` か連携先だけ、と決めてしまえば、標準ライブラリ全体を機械的に落として問題ありません。入力の検査は、拒否ではなく許可で書けないかをまず考える。これが今回の教訓です。
もうひとつ。`adk web` は開発用のWeb UIです。AIエージェントの開発キットは、ローカルで試すためのUIやデバッグ機能を持っていることが多く、そこには「開発者しか触らない」という前提が置かれています。その前提はCloud RunやGKEへ載せた瞬間に消えます。開発用のUIとテスト用の依存(pytest)を本番に持ち込まない、という当たり前の線引きが、今回はそのまま緩和策になっていました。
