Claude Codeの警告「wildcard before the rest」とBash許可ルールの直し方
Bash許可ルールの * がサブコマンドより前にあると出る警告の意味と、Bash(git * main)をBash(git checkout main)やBash(git status *) に書き換える手順です。
has a wildcard before the rest of the commandは、Bashの許可ルールで*をサブコマンドより前に置いたときに出る警告です。Bash(git * main)やBash(git -C * status *)のように、後ろの単語がコマンドの正体を決める位置より手前に*があると対象になります。警告が出てもルールは消えません。マッチの挙動も変わらないので、放置すると広すぎる許可がそのまま残ります。
直し方は2択です。*の位置に入れたい値が決まっているなら、その値を書きます。サブコマンドを許可したいだけなら、*をサブコマンドの後ろへ移します。
警告の全文と、出る条件
警告は次の形で、ルールの出どころが括弧内に入ります。
Permission allow rule (.claude/settings.json): Bash(git -C * status *) has a
wildcard before the rest of the command, so it also matches any options
inserted at that position and approves them without a prompt. For git, options
such as -c and --exec-path can run arbitrary commands. Replace that * with the
exact value you mean, or only use * after the subcommand (for example
Bash(git status *)).対象になるのはBashのallowルールです。errorsのリファレンスは、次の3か所にあるルールを検出元として挙げています。
- 設定ファイル(
.claude/settings.jsonなど) - 管理設定(managed settings)
--allowedToolsまたは--settingsフラグの値
バックグラウンドセッションや--output-format json / stream-jsonでは、機械が読む出力を汚さないため、警告は標準エラーではなくデバッグログに書かれます。--debug付きで起動すると~/.claude/debug/<session-id>.txtで読めます。v2.1.246より前のClaude Codeは、この種のルールを警告なしで受け入れていました。
なぜ*の位置が問題なのか
Bashルールの*は、空白を含む任意の文字列に一致します。先頭から最初の*までは書いたとおりに照合されるため、Bash(git log *)はgit log系だけを通し、Bash(git *)はgitのあらゆるサブコマンドを通します。
git log --oneline mainで言えば、gitがプログラム、logがサブコマンドです。サブコマンドは、そのプログラムが何をするかを決める単語です。*がその手前に来ると、サブコマンドの位置にもオプションの位置にも何でも入れられます。
Bash(git * main)の一致例は、permissionsページのマッチ表で次のように示されています。
| 実際のコマンド | Bash(git * main)に一致するか |
|---|---|
git merge main | Bash(git * main)に一致するか一致する |
git push origin main | Bash(git * main)に一致するか一致する |
git -c core.fsmonitor=<script> diff main | Bash(git * main)に一致するか一致する |
git log | Bash(git * main)に一致するか一致しない(末尾がmainでない) |
書いた本人が想定するのは、おそらくgit checkout mainやgit merge mainあたりでしょう。ところが*はpush originにも-c core.fsmonitor=<script>にも一致します。-cはgitに設定値を渡すオプションで、core.fsmonitorにプログラムを指定するとgitがそれを実行します。警告文が「-cや--exec-pathで任意のコマンドが走りうる」と書くのはこのためです。許可ルールに一致したコマンドは確認なしで走るので、ルールの広さがそのまま承認の広さになります。
書き換えの型
対処は2つです。どちらも、*は後ろ側にだけ置きます。
警告が出るルールと書き換え後
警告が出る書き方
Bash(git * main)
Bash(git -C * status *)
警告が出ない書き方
Bash(git checkout main)
Bash(git status *)
Bash(git * main)のように、後ろの単語(main)で絞りたかったルールは、*の位置に入れたい値を決め打ちします。checkoutとmergeの両方を通したいなら、ルールを2本に分けます。
{
"permissions": {
"allow": [
"Bash(git checkout main)",
"Bash(git merge main)",
"Bash(git status *)",
"Bash(git diff *)"
]
}
}Bash(git -C * status *)のように、ディレクトリ指定の-Cを挟みたかったルールは、Bash(git status *)へ寄せます。許可したいサブコマンドごとに1本ずつ書くのが前提です。git -C <dir> statusの形はこのルールに一致しなくなりますが、これは意図した締め込みです。
ルールを探して直す手順
警告は括弧内に出どころを示します。出どころごとの直し方は次のとおりです。
警告からルールを直すまで
- 1
出どころを読む
警告の括弧内が設定ファイルのパスなら、そのファイルを開きます。
claude-settings-<hash>.jsonのような、ディスクに存在しないパスは--settingsに渡したインラインJSONを指します。 - 2
該当ルールを書き換える
*をサブコマンドの後ろへ移すか、決め打ちの値に置き換えます。 - 3
再起動して警告が消えたか見る
claude --debugで起動し、デバッグログを開いて警告が残っていないか確かめます。
出どころがmanaged policy settingsなら、手元では消せません。管理設定を保守している担当者に警告の全文を渡します。
プロジェクトに許可ルールが多いときは、警告を待たずに洗い出せます。次のコマンドは、Bash(の直後が*で始まるか、*の後ろに文字が続くルールを拾う粗い検索です。
grep -rnE '"Bash\([^")]*\*[^")]+\)"' \
.claude/settings.json .claude/settings.local.json ~/.claude/settings.json \
2>/dev/null末尾だけに*があるルールは、*の後ろが閉じ括弧なので拾われません。ヒットしたものを目で見て、*の後ろの単語が絞り込みのつもりだったかを判断します。Bash(* --version)のようにプログラム名の位置が*のルールもヒットしますが、これが警告の対象かどうかは、errorsのリファレンスに記載がありません。実際に起動して確かめるのが確実です。
*が何の代わりになっているかで直し方が決まる
同じ「*が前にある」ルールでも、書いた人の狙いは違います。*が何を指すつもりだったかを先に決めると、書き換え先が絞れます。
| 元のルール | *で想定していたもの | 書き換え先 |
|---|---|---|
Bash(git * main) | *で想定していたものcheckout / merge などのサブコマンド | 書き換え先サブコマンドごとにBash(git checkout main)のように分ける |
Bash(git -C * status *) | *で想定していたもの作業ディレクトリのパス | 書き換え先Bash(git status *)にして、-C付きの形は許可しない |
Bash(npm * test) | *で想定していたものrunなどの中間語 | 書き換え先Bash(npm run test)のように実際の形を書く |
-Cで別ディレクトリのリポジトリを操作させたいケースは、ルールの側ではなく作業ディレクトリの側で扱います。追加ディレクトリは--add-dir、/add-dir、設定のadditionalDirectoriesで足せます。そこのファイルには、元のディレクトリと同じ権限ルールが適用されます。パスを*にしてgit -Cを通す必要はありません。
なお、lsやcat、diff、gitの読み取り専用の形は、組み込みの読み取り専用コマンドとして確認なしで走ります。git statusやgit diffのためだけに許可ルールを書いていたなら、そもそもルール自体が不要な可能性があります。読み取り専用に数えられる範囲は固定で、設定では変えられません。
引数まで絞りたいなら、ルールよりフックが向く
*の後ろの単語で絞る書き方は、そもそも壊れやすいパターンです。Bash(curl http://github.com/ *)が、オプションの前置やプロトコルの違いで外れるのと同じ理屈です。警告を消すためにサブコマンドごとのルールを何十本も並べるくらいなら、PreToolUseフックでコマンド全文を検査する方法が選べます。
フックの判断は権限ルールを迂回しません。denyとaskのルールは、フックがallowを返しても評価されます。逆に、終了コード2でブロックしたフックは、allowルールがあっても呼び出しを止めます。許可を広げる側はルール、例外を止める側はフック、と役割を分けると、ルール数も警告も減らせます。
直したあとも残るもの
警告の対象でなくなったルールにも、限界はあります。
Bash(git status *)はgit -C . statusを通さない: allowルールは書いたコマンドの形にだけ一致します。通したい形が別にあれば、その形のルールを足します- denyルールは別の形を止めない:
Bash(git push *)をdenyに入れても、git -C . push origin mainやgit -c push.default=current push origin mainは止まりません。denyの効き方はeval等で拒否ルールをすり抜ける問題にまとめています - ラッパーの外側は別のルール:
devbox runやnpxのような環境ランナーは、組み込みの剥がし対象に入っていません。Bash(devbox run *)はrunの後ろの何でも通すので、ここにも同じ広さの問題があります。devbox run rm -rf .も一致するためです。direnv exec、mise exec、docker execも同じ扱いで、ランナーと中身のコマンドを両方書いたBash(devbox run npm test)のように、通したい中身ごとに1本ずつ書きます。なお、素のxargsは剥がされますが、xargs -n1 grep patternのようにフラグが付くとxargsのコマンドとして照合されるので、Bash(grep *)では通りません
複合コマンド(&&や;)がサブコマンドごとにどう評価されるかは、Bash権限ルールと複合コマンドの評価で扱っています。警告が消えたのに確認が出続けるときは、そちらの切り分けが先です。
警告が出ているのに原因のルールが見つからないとき
ルールは複数の場所から集まります。ユーザー設定、プロジェクト設定、ローカル設定、管理設定、フラグが同じ優先順位の仕組みで合成されるので、手元のプロジェクトを探して見つからなくても、~/.claude/settings.jsonや管理設定が出どころのことがあります。括弧内の出どころを最初に読むのは、このためです。
自分で書いた覚えのないルールは、承認ダイアログの「Yes, and don't ask again」から入った可能性があります。このダイアログが保存するのはBash(npm test *)のような、スペース区切りで末尾に*を置く形です。サブコマンドより前に*が来る形は、手で書いたか、他人の設定を貼ったときに混ざります。
PowerShellのルールも、Bashと同じ形式で*を任意の位置に書けます。PowerShell(Get-ChildItem *)はエイリアスのgciやls、dirにも一致し、大文字小文字は区別されません。ただし、この警告がPowerShellルールにも出るかどうかは、errorsのリファレンスに記載がありません。Bash以外で*を前に置いているルールは、警告が出ないことを安全の根拠にせず、Bashと同じ基準で見直すのが無難です。
Claudeに許可ルールを書かせるときの決めごと
Claudeに権限ルールを書かせるときは、書き方を決めておくと警告の元を作りません。CLAUDE.mdに次のような一文を置く方法があります。
## 権限ルールの書き方
- Bashの許可ルールは、`*`をサブコマンドの後ろにだけ置く(例: `Bash(git status *)`)
- 後ろの引数で絞りたいときは、`*`を使わず1コマンド1ルールで書くこれは指示にすぎず、境界にはなりません。ルールを追加させたら、設定ファイルを開いて*の位置を自分の目で確かめるのが確実です。ルールの評価順はdeny、ask、allowの順で、最初に一致したものが結果を決め、ルールの具体性では順序は変わりません。広すぎるallowを直す前に、同じコマンドに一致するdenyやaskが先にないかも見ておくと、警告の原因と確認が出る原因を分けられます。管理設定で配布するルール集は、チームの全員に同じ警告を出すことになるため、配布前にclaude --debugで一度起動しておくと手戻りが減ります。許可ルールを管理設定だけに限定する運用については、allowManagedPermissionRulesOnlyの記事が参考になります。
よくある質問
Bash(git *)のように末尾だけの*も警告されますか
*がサブコマンドの後ろにある書き方は、警告文が勧める形です。ただしBash(git *)はgitの全サブコマンドを通すため、広さの面では別の見直し対象になります。
:*の書き方でも同じですか
Bash(ls:*)はBash(ls *)と同じ意味の末尾ワイルドカードです。:*は末尾でのみ認識され、Bash(git:* push)の:は文字そのものとして扱われます。