Read denyルールがあるとcd複合コマンドで承認が求められる — Claude Code
Read()のdenyルールが1つでもあると、cdを含む一部の複合コマンドはauto modeで承認待ちになります。v2.1.257〜261の経緯と、承認を外す回避策です。
permissions.denyにRead()のルールを1つでも置いていると、cdを含む複合コマンドが絶対パス指定でも承認待ちになることがあります。auto modeで再現し、v2.1.259ではbypassPermissionsでも止まる報告が出ていました。報告者の測定では、止まるかどうかはReadのdenyルールの有無で決まります。そのパターンが読むファイルに当たるかどうかは関係しません。
Edit・Writeが止まる「Read deny rule」エラーの対処とは別のエラー経路です。cdとgitの組み合わせで出る仕様上の確認やWindows Git Bashの回帰は、Claude Codeでcdに許可プロンプトが出る原因と対処法で扱っています。ここではRead()のdenyルールが引き金になる確認を見ます。
何が起きるか
報告はWindows(Git Bash)に加えて、macOSとLinux(Arch、WSL2)からも上がっています。特定OSに閉じた不具合ではありません。
引っかかるのは「cdのあと相対パスで何かを読む」形の複合コマンドです。次の2行は同じ処理なのに、結果が変わります。
cd /d/dev/myrepo && sed -n 1,2p README.mdcd /d/dev/myrepo && sed -n 1,2p README.md; echo donecdの対象は同じ絶対パスで、読むファイルも同じです。1行目は素通りし、2行目は;が付いた瞬間に確認プロンプトへ切り替わります。表示される文言は次の形です。
reads a file by a relative path after a cd in a compound command; which file that is
cannot be resolved statically while a Read() deny rule is configured, so this needs approval.v2.1.259では、grepを含む複合コマンドで対象パスの取り違えも報告されています。次のコマンドの検索パスは.です。
cd /d/dev/myrepo && echo "=== X ==="; grep -rn "X" --include=*.cs .それでもダイアログは--include=*.csを検索先として名指しします。この文言はゲートAのもので、v2.1.260で消えています(後述)。
どんな形が引っかかるか
複合コマンドはサブコマンドごとに判定されるのが基本です(Claude CodeのBash権限ルールは複合コマンドをどう評価するか)。Read()のdenyルールがある状態では、cdを含む形だけが別枠で評価されます。報告者が測った組み合わせは次のとおりです。
| 複合コマンドの形 | 結果 | 測定された版 |
|---|---|---|
cd <絶対パス> && sed -n 1,2p file | 結果通る | 測定された版v2.1.259・v2.1.261 |
cd <絶対パス> && sed -n 1,2p file; echo done | 結果確認が必要 | 測定された版v2.1.259・v2.1.261 |
cd <絶対パス>; sed -n 1,2p file | 結果確認が必要 | 測定された版v2.1.259・v2.1.261 |
cd <相対パス> && grep pat file | 結果確認が必要 | 測定された版v2.1.261 |
cd <絶対パス> && cat a && cat b(読み取りが複数連続) | 結果確認が必要 | 測定された版v2.1.259 |
cd <相対パス>; grep pat file(Read()のdenyルールが0本) | 結果通る | 測定された版v2.1.261 |
単独のsed -n 1,2p file(cdなし) | 結果通る | 測定された版v2.1.259 |
境界線は3つです。cdの対象が相対パス、;が混ざる、読み取りが複数連なる(v2.1.259の報告のみ)。絶対パスの&&一本で読み取りが1つなら通ります。最下段から2行目が示すとおり、Read()のdenyルールを外せば同じ形が通ります。
サブエージェントが実行したBash呼び出しでも同じプロンプトが出て、親セッション側の手動承認として表示されます。general-purposeとPlanの両方で報告されています。並列のサブエージェントでファイルを読むたびに親へ承認が積み上がり、自律実行が止まります。サブエージェントを使わない単一セッションでも、原因は同じです。
v2.1.257からv2.1.261までの経緯
最初の報告は9月2日に立ちました。以下の日付はnpmの公開時刻のUTC日付で、日本時間では1日後になります。公式のchangelogと報告の時系列を並べると、導入から部分的な撤回までが数日です。
ガードが入ってから残るまで
- 9月1日v2.1.257
changelogに「Bash
Read()/Edit()のdenyルールが< fileリダイレクトとリーダー系コマンドに効いていなかった問題を修正」と載りました。権限ドキュメントも、入力リダイレクトの対象チェックはv2.1.257以降と書いています。報告者の一人は、v2.1.257が自動で入った朝からプロンプトが出始めたと書いています。 - 9月2日v2.1.259
Read()のdenyルールをBashの引数(オプション値・git diff/git grepのファイルオペランド・cd DIR && cat FILE)にも適用する変更が入りました。cd複合コマンドの確認プロンプトが、ここで大量に表へ出ました。 - 9月3日v2.1.260
v2.1.259の変更をリバートしました。
Read(./**/build/**)のようなルールがnpm run buildをあらゆるモードで拒否することと、cd … && grepがauto modeでも確認待ちになることが理由です。 - 9月4日v2.1.261
リバート後の測定で、
cd複合コマンドの確認の一部が残っていると報告されました。v2.1.289までのchangelogに、この分岐の解消を告げる項目はありません。
リバートで消えたのは、2つあった確認のうち片方だけです。
bypassPermissionsで止まった報告の文言はゲートAのものだった
v2.1.259は2種類の確認分岐を、同じ「Read()のdenyルールが存在する」という前提の下に足していました。報告者は文言で見分けています。
2つのゲートの違い
ゲートA
文言は「would search a directory that cannot be determined here, and a Read() deny rule is configured; only you can approve running it anyway」。bypassImmuneとして登録され、allowルールでもフックのallowでも解除できないと測られました。
ゲートB
内部識別子はcd-compound-read。文言は前段の「resolved statically」の一文です。通常のaskと同じ扱いで、広いBashのallowルールで解除できたと測られています。
--dangerously-skip-permissionsやbypassPermissionsで止まったという報告のうち、文言が添えられているものは「only you can approve」で終わるゲートAでした。ゲートBについては、最初の報告者がヘッドレスの-p --dangerously-skip-permissionsでv2.1.258・v2.1.259とも全形が通ったと書いています。つまり、bypassPermissionsで止まるのは主にゲートAで、v2.1.260以降は解消済みと読めます。auto modeの分類器がaskとallowをどう振り分けるかは、Claude Codeのauto mode分類器は何を止めているかにあります。
回避策
v2.1.289までのchangelogに、ゲートBそのものを直した項目はありません。個別の環境で報告された手を、効き方と代償で並べます。
承認を外す5つの手
Read()のdenyルールを外す
最も確実です。代償は、
.envやsecrets/を守る仕組み自体を失うことです。ルールの一覧・削除は/permissionsコマンドで権限ルールとauto mode拒否を管理するから行えます。Bashを広くallowに入れる
ゲートBは通常の
askなので、Bashのallowルールで解除できます。代償は、askルールもdenyルールも書いていないBashコマンドまで確認なしで通ることです。askルールはallowより先に評価されるため、書いてあるaskルールは効いたままです。PreToolUseフックで形を拒否する
cd+相対パス読みの形をフックが拒否し、理由に「絶対パスを使ってください」と返すと、Claudeは書き直して再実行します。ユーザーへの確認は出ません。ヘッドレスなら--permission-prompts none
v2.1.259以降で使えるフラグです。確認になるはずの操作が自動で拒否され、ホストの応答を待たずに、Claudeには再試行しないよう伝わります。
絶対パス1本の&&にする
頻度は下がります。ただし複合コマンドの組み立てはClaude側が決めるため、CLAUDE.mdの指示だけでは形を保証できません。
フックのallowは、一致するdenyルールを上書きしません。公式のフックドキュメントも、allowを返してもdenyとaskのルールは評価され続けると書いています。そのためdenyルール自体は残せます。
--permission-promptsの説明は、v2.1.289のclaude --helpでそのまま読めます。
claude --help | grep -A5 -e '--permission-prompts' --permission-prompts <target> Who answers permission prompts with
--print: "host" (the SDK host or
--permission-prompt-tool) or "none"
(nobody: anything that would prompt is
denied automatically; the permission
mode still decides everything else)公式のヘッドレスのドキュメントによると、PermissionRequestフックが許可したものを除き、確認になる操作は拒否されます。Claudeには承認者がいないと伝わり、再試行もしません。ゲートBのaskも、この設定では拒否になります。CIでcd複合コマンドが拒否されたときは、コマンドを絶対パス1本の形にするか、Bashのallowを足す手があります。
Bashのallowを入れても、実際にRead()のdenyルールへ一致するファイルの読み取りは通りません。deny判定はallowやaskより常に先に評価され、.envそのものを読む操作は拒否されたままです。allowで外れるのは、パスが一致するかどうか分からないまま確認を求めてくるゲートBのaskだけです。
公式の読み取り専用コマンド一覧との距離
権限ドキュメントは、cdを含む読み取り専用の複合コマンドが確認待ちになる例を、cdのあとに別ディレクトリでgitを実行する場合と、cdのあとのリダイレクト先が解決できない場合の2つに絞っています。gitは移動先のディレクトリにあるフックを実行しうること、リダイレクトは解決先のディレクトリを判定できないことが、権限ドキュメントの書く理由です。なおcdの対象が現在の作業ディレクトリに解決されるなら、そのcdは何もしない操作として扱われ、プロンプトの原因になりません。
今回の確認は、ルールが実際に一致するかどうかとは無関係に出ます。23本のRead()denyルールを登録していても、どれ一つ触れたファイルに一致しない環境で同じ確認が出た報告があります。
よくある質問
Edit()のdenyルールだけを置いていれば発生しませんか
報告された範囲では、Read()のルールが0本なら同じ形が通っています。この分岐が見ているのはReadという名前、またはRead(で始まるルールの存在だけだと報告者は読んでいます。ただしヘッドレスのautoでRead・Editルールを消しても出た報告もあり、managed settingsなど別の層のルールが残っていた可能性があります。なおReadのdenyルールは、同じパスへのEditとWriteも止めます。
.claudeignoreでファイルを隠せば、Readのdenyルールは不要ですか
不要になりません。公式の権限ドキュメントは、.claudeignoreは効果がなく、中身はReadのdenyルールへ移すよう書いています。ファイルを守りたいなら、この記事の確認と付き合うことになります。
まとめ
Read()のdenyルールが1本でもあると、;・相対パスのcd・読み取りの連結のいずれかで確認待ちになります。止まる形がゲートBで、Bashの広いallowかPreToolUseフックで外せます。bypassPermissionsでも止まっていたのはv2.1.259のゲートAで、v2.1.260で消えています。evalで包んだコマンドがdenyルールをすり抜ける別のゲートはClaude Codeの拒否ルールをeval等ですり抜けられる問題と対策で扱っています。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。