TL;DR
- Cursor の CVE-2026-22708 は、コマンド allowlist を有効にした Auto-Run Mode でも、シェルの組み込みコマンド(
exportなど)が allowlist を通らずに実行できてしまう脆弱性です(GitHub Security Advisory GHSA-82wg-qcm4-fp2w)。 - 攻撃の肝は「許可したコマンドそのものを差し替える」ことではなく、環境変数を汚染して、許可済みコマンドの実行結果を横取りする ことです。
- 筆者は Cursor 本体ではなく、その土台であるシェルの挙動を手元の bash で再現しました。
gitだけを許可した状態でも、PATHを書き換えるだけで偽のgitが動き、GIT_EXTERNAL_DIFFを書き換えるだけで正規のgit diffから任意コマンドが実行されました。 - 対策は Cursor 2.3 への更新が第一ですが、「allowlist に入れたコマンドは安全」という前提そのものが崩れている点が本質です。
対象読者は、AI コーディングエージェント(Cursor / Claude Code / Codex など)を自動実行モードで動かしている、あるいは CI・開発環境に組み込もうとしている開発者です。
はじめに
コマンド allowlist は、AI エージェントに「この一覧のコマンドだけは確認なしで実行してよい」と許可する仕組みです。git status や ls のような無害なコマンドを並べておけば、いちいち承認を押さずに作業が進みます。
ここで一つ疑問が浮かびます。allowlist に git を入れたとして、本当に「安全な git」だけが動くのでしょうか。実行されるバイナリがすり替わっていたら、あるいは git の挙動を変える環境変数が仕込まれていたら、allowlist は何を守っているのでしょうか。
Cursor の CVE-2026-22708 は、まさにこの隙を突いたものでした。本記事では、脆弱性の構造を確認したうえで、その中核にあるシェルの挙動を手元で再現します。Cursor 固有の内部実装には踏み込めないため、そこは公式アドバイザリの記述に依拠しますが、「許可済みコマンドが乗っ取られる」というメカニズム自体は、どの環境でも成立する普遍的な問題 であることを示します。
CVE-2026-22708 の概要(公式アドバイザリより)
まず二次情報として、公式アドバイザリが述べている事実を整理します。
- 影響範囲: Cursor 2.2 以前。修正版は 2.3(GHSA-82wg-qcm4-fp2w)。
- 前提条件: エージェントが Auto-Run Mode(既定ではない)で動作し、かつ Allowlist モードが有効であること。
-
仕組み: 公式アドバイザリは「trusted command に影響する環境変数を設定・変更・削除してシェル環境を汚染する」と説明しています。シェルの組み込みコマンドが allowlist チェックを丸ごと素通りするため、
PATHのような環境変数を書き換えて、許可済みコマンドの実行方法を乗っ取れます。 - 想定被害: prompt injection と連鎖すると、許可した覚えのないコマンドが動く、すなわち任意コード実行につながり得ます。
修正内容としてアドバイザリが挙げているのは「エッジケースまわりのコマンドパースの改善」です。つまり、組み込みコマンドや環境変数の扱いをパーサーが取りこぼしていた、という素直な話です。
ここまでが公式の記述です。以下は筆者が手元の bash で実際に確かめた内容に切り替わります。
手元で再現する:allowlist は「バイナリ名」しか見ていない
再現に使ったのは、Cursor ではなく素の bash です。allowlist の本質は「起動しようとしているコマンド名が一覧にあるか」を見る点にあり、その名前で実際にどのバイナリが起動されるかは、シェルの PATH 解決に委ねられています。この分離が攻撃面になります。
ベクター1:PATH を汚染して偽の git を起動させる
git だけを許可した状況を想定し、攻撃者が仕込む偽 git を用意して、export PATH=... で先頭に差し込みました。
# 攻撃者が仕込む偽 git(任意コードを実行する)
mkdir -p "$WORK/evil"
cat > "$WORK/evil/git" <<'EOF'
#!/usr/bin/env bash
echo "[!!! 任意コード実行 !!!] 偽 git が動作: whoami=$(whoami)" >&2
exit 0
EOF
chmod +x "$WORK/evil/git"
# シェル組み込みの export で PATH を汚染(allowlist を経由しない)
export PATH="$WORK/evil:$PATH"
# allowlist 済みの「安全な」git を呼ぶ
git status
実行結果は次のとおりでした。
=== ステップ2: allowlist 済みの「安全な」git を呼ぶ ===
[!!! 任意コード実行 !!!] 偽 git が動作: whoami=root
=== 結論: allowlist は git を許可しただけなのに、実行されたのは偽 git ===
git status を呼んだつもりが、動いたのは攻撃者の偽 git でした。allowlist は「git という名前」を許可しただけで、その名前がどのファイルに解決されるかは一切見ていません。PATH を先頭一つ書き換えるだけで、承認ゼロのまま任意コードが走ります。
筆者が改めて怖いと感じたのは、PATH の書き換えが export というごくありふれた組み込みコマンド一つで済む点です。ファイルを一つも作らずに alias git='...' を仕込む変種も同じ理屈で成立します。「新しいコマンドの実行」に見えないので、コマンド単位の allowlist では原理的に検知できません。
ベクター2:バイナリはそのまま、環境変数だけで乗っ取る
ベクター1 は「偽バイナリを PATH で優先させる」手口でした。より発覚しにくいのは、正規の git バイナリをそのまま使いながら、git が参照する環境変数だけを汚染する 手口です。git には外部プログラムを呼び出すための環境変数がいくつもあり、その一つ GIT_EXTERNAL_DIFF を使いました。
# payload.sh は任意コードを実行するスクリプト
export GIT_EXTERNAL_DIFF="$WORK/payload.sh"
# allowlist 済みの git diff を非対話で呼ぶ
git -C "$WORK" diff
結果は次のとおりです。
--- allowlist 済みの git diff を非対話で呼ぶ(パイプ済み) ---
[!!! payload !!!] GIT_EXTERNAL_DIFF 経由で任意コード実行: user=root
差分を表示するだけの git diff から、攻撃者のスクリプトが実行されました。ここで動いている git は正真正銘の /usr/bin/git です。バイナリの完全性を検証しても、PATH を監視しても、このベクターは素通りします。書き換わったのは環境変数一つだけだからです。
なお、同じく外部プロセスを呼ぶ GIT_PAGER も試しましたが、こちらは出力がパイプ(非対話)だと git がページャ自体を無効化するため、今回の非対話実行では発火しませんでした。すべての環境変数が同じ条件で使えるわけではない、という点は正直に補足しておきます。攻撃者にとっては、対話・非対話それぞれで確実に発火するベクターを選べばよいだけの話です。
なぜ「コマンド単位の許可」では守れないのか
2 つのベクターに共通するのは、allowlist が検査するレイヤーと、実際にコードが実行されるレイヤーがずれている ことです。
allowlist は「コマンド名」という表層を見ています。しかし実際に何が起動するかは、その下にあるシェルの名前解決(PATH)と、コマンドが読む環境変数(GIT_* など)が決めます。組み込みコマンドがこのチェックを素通りする限り、攻撃者は「許可された名前」を保ったまま、中身だけを差し替えられます。
CVE-2026-22708 が Cursor 固有の実装バグに見えて、実はもっと根が深いのはこのためです。Cursor 2.3 の修正は「パーサーが取りこぼしていた組み込みコマンドを承認対象に含める」という方向で、これは正しい対処です。ただし、環境変数を汚染する経路は export だけではありません。防御側は「特定のコマンドを塞ぐ」発想から、「信頼できない入力を読んだ後は環境そのものを信用しない」発想へ切り替える必要があります。
対策:バージョン更新の先にあるもの
第一の対策は明快です。Cursor を 2.3 以降に更新する こと。これで組み込みコマンドが承認対象に入り、export PATH=... のような一手は塞がれます。
そのうえで、allowlist に頼る運用者が持っておくべき視点を挙げます。
- Auto-Run Mode を常用しない。公式アドバイザリも前提条件に挙げているとおり、既定ではないこのモードを漫然と有効にしないことが、そもそもの露出を減らします。
-
信頼できない入力を読むエージェントは環境を隔離する。prompt injection の起点は外部から読み込んだテキストです。ネットワークやファイルから untrusted な内容を読ませるセッションは、コマンド実行を許さない、あるいは使い捨て環境で走らせるのが安全です。この方向は各エージェントが機能として持ち始めており、たとえば Claude Code は v2.1.248 で、組み込みのコマンド実行ツールと
WebFetchを外してファイルツールを作業ディレクトリに限定する--restrictedフラグを追加しています(Claude Code changelog)。 - allowlist を「安全の証明」と読み替えない。allowlist が保証するのは「一覧にある名前が呼ばれた」ことだけで、「安全なコードが動いた」ことではありません。本記事の 2 つの再現は、その差を具体的に示しています。
まとめ
CVE-2026-22708 は、Cursor 2.2 以前の Auto-Run Mode + Allowlist という条件下で、シェルの組み込みコマンドが allowlist を素通りする脆弱性でした。筆者が手元の bash で再現したところ、export で PATH を汚染すれば偽の git が承認なしで動き、GIT_EXTERNAL_DIFF を書き換えれば正規の git diff から任意コマンドが実行されました。どちらも「新しいコマンドの実行」には見えないため、コマンド単位の allowlist では検知できません。
修正版 2.3 への更新は必須ですが、それ以上に「許可したコマンド名」と「実際に動くコード」は別物である、という前提を運用に組み込むことが重要です。allowlist は入口の名札を確認しているだけで、中で何が起きるかまでは見ていないのです。
参考リンク
- Cursor Security Advisory GHSA-82wg-qcm4-fp2w(CVE-2026-22708)
- Claude Code changelog(
--restrictedフラグ)