sandbox.excludedCommandsが効かない原因 — Claude Codeの既知バグ3つと切り分け
Claude Codeでsandbox.excludedCommandsを設定してもコマンドが外れない原因は、多くが設定や呼び出しの形です。残る3つの既知バグと切り分け手順を示します。
sandbox.excludedCommandsとは何か
Claude Codeでsandbox.excludedCommandsが効かないとき、原因の多くはバグではなく、パターンの書き方か呼び出しの形です。リダイレクトを付けた、sudoで始めた、除外していないコマンドと&&でつないだ、といった形はサンドボックスに留まります。
sandbox.excludedCommandsは、Bashサンドボックスの制約下では動かないコマンドを名指しで除外し、サンドボックスの外で実行させる設定キーです。docker *のようなパターンを指定すると、そのコマンドは通常の権限フローだけを通り、ファイルシステムやネットワークの隔離を受けません。
ところが2026年9月に入り、この設定が意図どおりに働かない3つの既知バグがGitHub Issueに報告されました。除外設定そのものが無視されるケース、git -Cのようなサブコマンド前のフラグが付くと除外が外れるケース、単一コマンドへのファイルリダイレクトが除外を無効化するケースです。バグ2はv2.1.277で入った修正の副作用として、バグ3はv2.1.278で観測されています。バグ1は原因となった版が分かっていません。
3件ともIssueはオープンのままで、バグ3は公式ドキュメントでは仕様として書かれています。先に切り分けの手順を示し、そのあとで3つのバグを順に見ていきます。除外の対象がPythonの並列処理なら、ProcessPoolExecutorが失敗する件も参照してください。
まず設定ミスを疑う: 効かない原因の切り分け
次の順に見ていくと、バグを疑う前に設定側の原因を除けます。
excludedCommandsが効かないときの切り分け
- 1
パターンの末尾にワイルドカードを付けたか
エントリは
Bash(...)の許可ルールと同じ構文で、ワイルドカードの無いパターンは完全一致です。dockerは引数なしのdockerにだけ一致し、docker *なら引数の有無を問わず一致します。 - 2
呼び出し全体が一致する形か
1回の呼び出し(Claudeが送るコマンドライン全体)に含まれるコマンドは、すべてが一致しなければ除外されません。
npm ci && docker compose buildは、npm ciを別のエントリで覆わないかぎりサンドボックスに留まります。pbcopyへのパイプも同様で、pbcopyを除外リストに足しても、その呼び出しは外れません。 - 3
サンドボックスに留まる形になっていないか
cd・sudo・コマンド置換・ファイルへのリダイレクトなどを含むと、一致していても除外されません。一覧は後述の「公式ドキュメントはどこまでこの挙動を認めているか」にあります。 - 4
書かれたテキストで一致しているか
照合の対象は呼び出しの文面です。
dockerを内部で呼ぶスクリプトやmakeのターゲットは一致せず、/usr/local/bin/dockerのように絶対パスで呼んでもdocker *には一致しません。 - 5
エントリをどこに保存したか
管理設定か
--settingsでallowUnsandboxedCommands: falseにすると、サンドボックスは管理者必須の扱いになります。この状態では、リポジトリの.claude/settings.jsonと.claude/settings.local.jsonにあるエントリが無視されます。~/.claude/settings.json・管理設定・--settingsのエントリは有効です。
5項目のどれにも当てはまらないとき、バグの可能性が出てきます。確かめるには、Manualモードで変更を伴うコマンド(docker compose up -dなど)をClaudeに実行させます。除外が効いていれば、権限プロンプトの題が「Bash command (unsandboxed)」になります。
設定キーの基本的な使い方はsandbox.excludedCommandsでdockerなど非対応コマンドを対象外にするにあります。
3つのバグはどの版で出たか
excludedCommandsの一致判定をめぐる版の流れ
- v2.1.277「全部のコマンドが一致」へ厳格化
複合コマンドの一部だけが一致しても全体が除外される抜け道を塞ぐ修正です。バグ2はこの直後に報告されました。
- v2.1.278バグ2が再現し、バグ3が観測される
git -Cの過剰一致は残ったままで、ファイルへのリダイレクトでも除外が外れることが確認されました。 - v2.1.281一部の過剰一致が修正される
git rev-parse --git-dir、シェルの組み込みコマンドに似た名前のプログラム、[WIP]や#の行を含むコミットメッセージで一致しない不具合が直りました。 - v2.1.282管理者必須のサンドボックスでリポジトリのエントリが無視される
allowUnsandboxedCommands: falseなどを管理設定で指定すると、プロジェクトとローカルの設定のエントリが無視されるようになりました。 - v2.1.285管理者必須のサンドボックスの扱いが変わる
プロジェクト設定は、管理者必須のサンドボックスを広げたり切ったりできなくなりました。管理設定の拒否リストを支えるプロキシの差し替え、厳格な許可リストの拡張、管理設定の読み取り拒否の開け直しも同じく止まります。
v2.1.303までのchangelogには、git -Cなどのフラグの件と、リダイレクトの件の修正が載っていません。
バグ1: 除外リストがそのまま無視される(Issue #95813)
Issue #95813は、excludedCommandsに列挙したコマンドが、それでもサンドボックス内で実行され失敗する事例です。報告者はmacOS 24.6.0で、/Library/Application Support/ClaudeCode/managed-settings.jsonに次のブロックを置いていました(Issueの設定から抜粋)。
{
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"autoAllowBashIfSandboxed": true,
"excludedCommands": ["docker *", "gh *", "ps *", "pgrep *", "sudo *"],
"allowUnsandboxedCommands": false
}
}この状態でps -p 1を実行するとoperation not permitted: ps、pgrep -x nodeはsysmond service not found、gh pr viewはTLS証明書検証エラーで失敗します。同じps -p 1をdangerouslyDisableSandbox: trueで実行すると1 /sbin/launchdが正しく返ります。コマンド自体は動くので、除外リストが適用されていないことになります。
ghのエラーは、TLSやプロキシの問題に見えて原因が別でした。同じシェルからcurl https://api.github.com/を実行すると200が返り、実際の証明書チェーンも検証できています。報告者は、Go製のバイナリがシステムの信頼サービスに届かないという、pgrepと同じ原因だと見ています。
報告者はallowUnsandboxedCommands: falseが原因かもしれないと疑いました。そのキーを外して再起動しても3つのコマンドの失敗は変わらず、キーが戻したのは、モデルがdangerouslyDisableSandboxを要求できる状態だけです。パターン構文も原因ではありません。ドキュメントでは、docker *は引数の有無を問わず一致するとされています。
コメント欄では、Linux/Debian・v2.1.280でも同じ症状が再現したという報告が付いています。dockerグループのソケットを使うためにdocker*を並べても無視され、bwrapが補助グループをすべて落とすためdocker.sockへの接続が権限エラーになる、という具体例です。このIssueにはduplicateラベルが付いています。
バグ2: git -Cのようなサブコマンド前のフラグで「全部一致」判定が壊れる(Issue #95455)
Issue #95455は、v2.1.277で入った修正が副作用を持つケースです。v2.1.277のchangelogには次の記載があります。
Fixed a sandbox.excludedCommands glob exempting an entire compound Bash command from the sandbox when only one part matched; every part must now match
これは、複合コマンドの一部だけが除外パターンに一致していても全体をサンドボックス外に出してしまうバイパスを塞ぐ、正当な修正です。ところが、パイプもチェインもリダイレクトも持たない単一コマンド、つまり「部分」が1つしかないコマンドまで、サブコマンドの前に値を取るフラグが付くだけで一致しなくなりました。
excludedCommands: ["git *"]を設定した状態での実測が、issueの表にまとまっています。
| コマンド | v2.1.276 | v2.1.277 |
|---|---|---|
git status --porcelain | v2.1.276除外 | v2.1.277除外 |
git --no-pager status --porcelain | v2.1.276除外 | v2.1.277除外 |
git -C . status --porcelain | v2.1.276除外 | v2.1.277サンドボックス化 |
git -c core.pager=cat status --porcelain | v2.1.276除外 | v2.1.277サンドボックス化 |
git --git-dir=<path>/.git --work-tree=<path> status --porcelain | v2.1.276除外 | v2.1.277サンドボックス化 |
git --exec-path=/usr/lib/git-core status --porcelain | v2.1.276除外 | v2.1.277サンドボックス化 |
値を取らない真偽値フラグ(--no-pager、--literal-pathspecs)は影響を受けず、値を取るフラグ(-C、-c、--git-dir、--exec-path)を挟むと除外が外れます。ただし、後のコメントで別の報告者がawsとgitを調べ、トリガーは「値を取るフラグ」ではなく特定のフラグ群だと指摘しています。Issueの表は入口の一例と見るのが安全です。
aws --profileやgit -C、-c、--git-dirは、位置を問わず=付きの形でも外れます。awsでは、完全なコマンドと一致するエントリを足しても直りませんでした。別の報告者は、git clone <url> <path>をgit clone *で、git fetchをgit fetch*で除外していても、パイプも連結も事前のフラグも無いのにサンドボックス化されたと書いています。v2.1.280、管理設定、remote-control起動の環境で、新しいセッションの最初のコマンドでも起きたそうです。
影響は2段階あります。git statusはサンドボックスに入ったまま終了コード0で「動いているように見える」出力を返します。サンドボックスがマスクしたホームディレクトリの.bashrcや.gitconfigが未追跡ファイルとして表示され、通常のワークツリーの雑音と誤認されます。報告者は、原因に気づかないまま、ステージングの判断を含む後続の操作を進めたと書いています。
認証が要る操作は別の形で失敗します。git -C <path> fetchは認証情報の設定を読めず、fatal: could not read Username for 'https://<host>'で止まります。エラーが資格情報やネットワークの問題に見えるため、原因の取り違えを招きやすい失敗です。
なお報告者は当初、この現象が非決定的(同じコマンドが失敗・失敗・成功と揺れる)だと書きましたが、Issue本文で撤回しています。失敗した呼び出しには2>&1 | head -20が付いており、成功した呼び出しには付いていなかっただけで、v2.1.277のルールどおり複合コマンドとして扱われた結果でした。git -C単体の過剰一致そのものはv2.1.278でも再現しており、決定的な挙動です。
コミットメッセージの中身でも外れる
同じIssueのコメントには、単一コマンドの引用符の中身で除外が外れる報告もあります。git commit --dry-run -m '…'の本文に何を含めるかで結果が変わり、失敗するとfatal: unable to access '~/.gitconfig': Operation not permittedと出ます。資格情報の問題に見える点は、git -Cの場合と同じです。
| 引用符の中身 | v2.1.281より前 | v2.1.285までの再測定 |
|---|---|---|
#で始まる本文の行、[Word] | v2.1.281より前失敗 | v2.1.285までの再測定通る(v2.1.281で修正) |
| バッククォート | v2.1.281より前失敗 | v2.1.285までの再測定失敗のまま |
$(…) | v2.1.281より前失敗 | v2.1.285までの再測定失敗のまま |
コミットメッセージやMRの説明文は、コード書式のファイル名にバッククォートを使うのが普通です。v2.1.285でもこの形は直っておらず、git commitを除外に頼っている自動化では最後の障害として残ります。
バグ3: 単一コマンドへのリダイレクトが除外を無効化する(Issue #95532)
Issue #95532は、v2.1.278で観測された別の過剰一致です。excludedCommands: ["gh *"]のもとで、gh api user --jq .loginは除外されて動きますが、同じコマンドの出力をファイルへリダイレクトすると、サンドボックス内に戻ってしまいます。
| コマンド | サンドボックス外で動くか |
|---|---|
gh api user --jq .login | サンドボックス外で動くか動く |
gh api user --jq .login && gh api user --jq .name | サンドボックス外で動くか動く |
gh api user --jq .login 2>&1 | サンドボックス外で動くか動く |
gh api user --jq .login > "$TMPDIR/out.txt" | サンドボックス外で動くか動かない |
gh api user --jq .login > /dev/null | サンドボックス外で動くか動かない |
gh api user --jq .login 2>/dev/null | サンドボックス外で動くか動かない |
ファイルディスクリプタの複製(2>&1)は許容される一方、/dev/nullを含むファイルへのリダイレクトは何であれ除外を外します。$(gh …)やgh … | headがサンドボックスに入るのは、報告者もv2.1.277の意図した挙動だと見ており、このIssueの対象外です。
macOSでは、ghのようなGoベースのCLIがSeatbelt配下でTLS検証に失敗するx509: OSStatus -26276が知られており、excludedCommandsが回避策になっています。リダイレクトを使っただけで元のTLSエラーへ逆戻りするため、ghの出力をファイルへ書き出して後続コマンドで読む、という「パイプが使えないなら代わりにリダイレクトする」自然なワークアラウンドが機能しません。
報告者はv2.1.277の厳格化そのものの取り消しを求めてはいません。要望は2つで、リダイレクトを「一致しない部分」に数えないこと、意図した挙動なら設定リファレンスとTLSのトラブルシューティング項目に書くことです。
公式ドキュメントはどこまでこの挙動を認めているか
sandbox.excludedCommandsの設定リファレンスには、除外が効かないままサンドボックスに留まる呼び出しの形が列挙されています。
sudo、eval、xargsで始まるコマンドcd、pushd、popdをどこかに含むコマンド- コマンド置換・サブシェル・
ifやforなどの制御構文 - ファイルディスクリプタの複製(
2>&1)以外のリダイレクト - コマンド名が変数由来のもの
- 絶対パス・
~・..を含むパスを引数にしたgit clone・git init・git worktree add・git worktree move・git bundle create
この一覧は例示で、これで全部ではありません。バグ3(単一コマンドへのリダイレクト)は、この一覧にそのまま載っています。サンドボックスの解説ページにも、ファイルへのリダイレクトはcdやコマンド置換$(...)と並んで呼び出し全体をサンドボックスに留める、とあります。一方で、TLS失敗のトラブルシューティング項目には「gh *をexcludedCommandsに足す」とだけあり、この制約への言及がありません。
3つのバグと公式の記述の関係
バグ1(除外リストが全く効かない)
失敗する条件がリファレンスの一覧に載っていないため、実装側の不具合と見るのが妥当です。
バグ2(git -Cなどのフラグ)
例示の一覧に載っていません。値を取るフラグを挟むと一致しなくなる挙動は、設定リファレンスからは読み取れない副作用です。
バグ3(リダイレクト)
仕様として明文化されているため、直す対象は設定ではなく呼び出しの形です。
影響の出方は用途によって変わる
excludedCommandsをどう使っているかによって、3つのバグが刺さる度合いは変わります。
| 使い方 | 影響 |
|---|---|
docker *のように単純なプレフィックスだけを除外している | 影響バグ1が起きれば全滅する。バグ2・3は関係が薄い |
git *を除外し、-Cや--git-dirで別ディレクトリを操作するスクリプトがある | 影響バグ2が直撃する。git statusが誤った出力を返す点が特に危険 |
gh *など、出力をファイルに保存してから加工するツールを除外している | 影響バグ3が直撃する。--jqのようなオプションで整形しきれない出力は詰まる |
| CI的な自動化で、除外コマンドの成否をログにリダイレクトして残している | 影響バグ3によりリダイレクトを足した瞬間にサンドボックス内へ戻り、ghならログにはTLSエラーが記録される |
git commit -mの本文にバッククォートや$(…)を含める | 影響引用符の中身だけで除外が外れ、~/.gitconfigへのアクセス拒否が出る |
現状のワークアラウンドとその限界
除外が効いているかは、aws configure get regionのような読み取りで見分けられます。除外されていればリージョンが表示され、サンドボックス内では設定ファイルが見えず出力されません。awsを除外している場合は、"env": {"AWS_PROFILE": "<name>"}でプロファイルを指定すれば--profileを避けられます。
バグ2については、issueの報告者がcd <dir> && git ...ではなく、ディレクトリ移動を別のBash呼び出しに分けてcd <dir>の後に素のgitを実行する方法を挙げています。(cd <dir> && git ...)はそれ自体が複合コマンドになりv2.1.277のルールで正しくサンドボックス化されるため、回避策になりません。git -C *のエントリを足しても変わらないことも、別のコメントで報告されています。
より根本的な対処として、git -Cを別名のラッパー(gitc)にして、その名前をexcludedCommandsに登録する方法があります。ラッパーの中身はexec git -C "$@"です。PreToolUse(Bash)フックでgit -C DIRをgitc DIRに書き換える手もあります。updatedInputを返し、permissionDecisionは省きます。この方法には落とし穴があります。permissions.allowにBash(gitc:*)を足さないと、autoモードの分類器が新しいラッパーを[Self-Modification]として止め、サンドボックスの失敗が権限プロンプトに置き換わるだけになります。フック方式にも落とし穴が3つあります。既存のdenyフックがあると書き換えられません。shlexで再クォートすると意味が変わります。-C以外のフラグは対象外です。
バグ3については、ファイルディスクリプタの複製(2>&1)だけは除外を保ったまま使えるため、--jqのような整形オプションで標準出力に必要な形を出し切り、リダイレクトを避けるのが現実的な回避策です。ファイルへの永続化がどうしても必要な場合の根本的な回避策は、Issue #95532では挙がっていません。
バグ1は設定側でどうにもならない不具合です。コメント欄では、bubblewrapとseccompをコマンド実行の前段で検知して素通しするラッパースクリプトが有志によって公開されています。ただしこれはClaude Code本体の挙動を書き換えるサードパーティの回避策で、公式のサポート対象ではありません。allowUnsandboxedCommandsがtrueの環境ならdangerouslyDisableSandboxでの個別実行は動きますが、falseではClaude Codeがこのパラメータを無視します。動く場合も、そのコマンドだけサンドボックスの保護を丸ごと外すことになります。excludedCommandsが本来担うはずだった「特定コマンドだけを狭く除外する」目的からは外れます。
まとめ
設定側の原因を除いたあとに残る症状のうち、リダイレクトは仕様なので呼び出しの形を変えるしかありません。除外リストが無視される#95813と、git -Cなどのフラグで外れる#95455は、Issueの更新を待つ状況です。
サンドボックスの隔離が失敗する別の原因もあります。apply-seccompのsetgroups書き込みがLinuxで失敗する件はapply-seccompのsetgroups書き込みがLinuxで失敗する原因と対処に、Bash確認をスキップするautoAllowBashIfSandboxedの条件はautoAllowBashIfSandboxedでBash確認をスキップする条件と無効化にあります。抜け道を管理設定で塞ぐallowUnsandboxedCommandsとの違いはallowUnsandboxedCommandsは抜け道を管理設定で塞ぐ設定を参照してください。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。