Claude Codeのrm安全プロンプトを環境変数で無効化する方法
critical-path削除の時間制限・コマンド置換由来のrm・bash -cの中身・PowerShellのcmd削除という4つの安全チェックを、環境変数で局面別に止める方法です。
Claude Codeのrm安全プロンプトとは
Claude Codeは、ファイルシステムのルートやホームディレクトリなど「critical path(重要パス)」を対象にしたrm・rmdirを、許可ルールやPreToolUseフックの"allow"があっても自動承認しません。モデルの誤りに備えるサーキットブレーカーとして、常に確認を挟む設計です。一致する拒否ルールがあれば、確認を待たずにそのコマンドを止めます。
claudeでrm -rfが止まる、または確認が出て先へ進めないときは、症状から原因の変数を引けます。
| 起きていること | 原因のチェック | 外す変数 |
|---|---|---|
| 確認が2分で時間切れになり拒否される | 原因のチェックcritical-path削除の時間制限 | 外す変数DANGEROUS_RM_TIMEOUT |
rm -rf "$(pwd)"のような置換だけの削除で確認が出る | 原因のチェックコマンド置換の出力だけが対象 | 外す変数SUBSTITUTION_RM_PROMPT |
bash -c 'rm -rf ~'の中身が止められる | 原因のチェック-cスクリプトの検査 | 外す変数INLINE_SHELL_RM_PROMPT |
PowerShellのcmd /c rdが確認なしで拒否される | 原因のチェックcmdビルトインのシステムパス拒否 | 外す変数POWERSHELL_CMD_RM_DENY |
変数名の頭にはCLAUDE_CODE_DISABLE_が付きます。rm -fのように再帰しない削除は、対象がcritical pathでなければこの確認の対象外です。
この確認とチェックは、別々の局面で働きます。環境変数も局面ごとに分かれていて、いま困っている場面に合うものだけを選べます。
止められる4つのチェック
DANGEROUS_RM_TIMEOUT
critical-path削除の確認にある2分のカウントダウンを外します。v2.1.281以降です。
SUBSTITUTION_RM_PROMPT
削除対象がコマンド置換の出力だけの再帰的な
rmへの確認を外します。v2.1.281以降です。INLINE_SHELL_RM_PROMPT
bash -c '…'のようにシェルへ渡したスクリプトの中身を、critical-path削除として読むのをやめます。v2.1.288以降です。POWERSHELL_CMD_RM_DENY
PowerShellツール経由の
cmdビルトイン(rd/rmdir/del/erase)がシステムパスを狙ったときの拒否を外します。v2.1.283以降です。
いずれも変数名の頭にCLAUDE_CODE_DISABLE_が付き、値は1です。「確認を省略する」スイッチではありません。別の扱いに切り替えるか、その1チェックだけを止めます。共通の落とし穴として、設定できる場所にも制約があります。
critical pathとして扱われる対象
Claude Codeはrm・rmdirの削除対象が次のいずれかに当てはまるとき、critical-path削除として扱います。
- ファイルシステムのルート
- ルート直下のトップレベルディレクトリ(
/usr、/etc、/dataなど) - ホームディレクトリ
- Windowsのドライブルートとその直下(
C:\、C:\Windowsなど) - 作業ディレクトリとその親ディレクトリ
- 追加の作業ディレクトリの親ディレクトリ(そのディレクトリ自体への
rm -rf <dir>は対象外で、<dir>/*のようにグロブで直下を狙う場合のみ)
実際に引っかかりやすいのは、この一覧そのものより、変数やコマンド置換で「最終的にどこを指すか実行前に分からない」形のほうです。次の形も、critical-path削除として扱われます。
| 形 | 例 | 扱われる理由 |
|---|---|---|
| 変数の直下にグロブや末尾スラッシュ | 例rm -rf "$DIR"/* | 扱われる理由変数が空だとルート削除になる |
| 値のない位置パラメータの直下 | 例rm -rf "$1"/* | 扱われる理由同じくルートからの削除に展開される |
| 変数の後にトップレベル名が続く | 例rm -rf "$TMPDIR/mnt" | 扱われる理由変数が空だと/mntを消す |
$(pwd)などを代入した変数 | 例D=$(pwd); rm -rf "$D" | 扱われる理由作業ディレクトリやリポジトリルートを指す可能性がある |
コマンド置換の出力だけが対象(再帰的なrm) | 例rm -rf "$(pwd)" | 扱われる理由実行前に対象を検証できない |
| critical pathの後ろにコマンド置換 | 例rm -rf ~/$(cmd) | 扱われる理由置換が空なら残るパス(ここではホーム)を検査する |
| バックスラッシュだけの対象 | 例rm -rf "\\" | 扱われる理由WindowsのGit Bashでは現在のドライブのルートと読まれる |
/*や/*/で終わる一部の対象 | 例rm -rf logs/*/* | 扱われる理由どのディレクトリに届くか実行前に分からない |
サブシェル、コマンド置換、バッククォート、プロセス置換の中に隠したrmも同じ判定を受けます。(rm -rf ~)やecho "$(rm -rf ~)"は素通りしません。
モードごとのcritical-path削除への既定の反応
環境変数がどこに効くかは、パーミッションモードごとの既定の反応を先に押さえると見通せます。
| モード | critical-path削除への既定の反応 |
|---|---|
default / acceptEdits | critical-path削除への既定の反応承認を求める(時間制限は付かない) |
plan | critical-path削除への既定の反応承認を求める。planの間にクラシファイアがコマンドを審査する設定で、bypassPermissionsが使えない場合はautoと同じ扱い |
auto | critical-path削除への既定の反応ターミナルでは時間制限付きで承認を求め、ターミナルを出せない場所では拒否 |
dontAsk | critical-path削除への既定の反応拒否 |
bypassPermissions | critical-path削除への既定の反応ターミナルでは時間制限付きで承認を求める |
2分のカウントダウンが付くのはautoとbypassPermissionsの2モードだけです。後述のCLAUDE_CODE_DISABLE_DANGEROUS_RM_TIMEOUTがこの2モードにしか関係しないのは、そのためです。
公式のモード表は最初の行をdefaultと書いています。一方、v2.1.289のclaude --helpは--permission-modeの選択肢をacceptEdits・auto・bypassPermissions・manual・dontAsk・planの6つで表示し、defaultという語は並びません。manualはdefaultの別名で、どちらの値も指定できます(v2.1.200以降)。
明示的なaskルールに一致したコマンドは、autoモードでも時間制限なしで確認を求めます。承認待ちの画面にはPermissionRequestフックで応答することもできます。
2分のカウントダウンを外す(DANGEROUS_RM_TIMEOUT)
autoモードとbypassPermissionsモードでは、critical-path削除の確認プロンプトに2分のカウントダウンが付きます。放置されたセッションが止まり続けないよう、時間切れでは拒否に倒れます。
カウントダウンの流れ
- 1
確認プロンプトが出る
critical-path削除に対し、2分のカウントダウン付きで承認を求めます。
- 2
時間切れなら拒否する
応答がなければコマンドを拒否し、Claudeには代わりの進め方が伝わります。キーを押すとカウントダウンだけが止まり、プロンプトは回答を待ち続けます。
- 3
3回続けて時間切れになると、確認も出なくなる
そのセッションでは以降の確認を出さず、critical-path削除を即座に拒否します。新しいメッセージを送ると、カウントはゼロから始まります。
CLAUDE_CODE_DISABLE_DANGEROUS_RM_TIMEOUT=1を設定すると、この時間制限だけが外れます。無効化後の挙動はモードによって分かれます。
無効化したあとの挙動
auto
critical-path削除はプロンプトを経ずにクラシファイア(分類器)へ送られます。
bypassPermissions
確認プロンプトは残り、時間制限なしであなたの回答を待ちます。
defaultモードとacceptEditsモードにはもともと時間制限が無いため、この変数は影響しません。
ここで外れるのは「時間切れ」だけです。ターミナルを出せない場所のauto(非対話の-p、Agent SDKセッション、VS Code拡張のチャットパネル、デスクトップアプリ)では、そもそも確認を出さずに拒否します。拒否時のClaudeへの指示は「消したかった対象を報告し、削除はユーザーに委ねる」です。
コマンド置換の出力だけが対象のrmを許す(SUBSTITUTION_RM_PROMPT)
rm -rf "$(pwd)"のように、削除対象がまるごとコマンド置換の出力になっている再帰的なrmは、実行前に対象パスを検証できません。そのため確認を求め、Claudeへは「まず置換を単独で実行し、出力された具体的なパスを使って削除し直す」よう指示します。
CLAUDE_CODE_DISABLE_SUBSTITUTION_RM_PROMPT=1はこの1チェックだけを止めます。ルートディレクトリ、ホームディレクトリ、作業ディレクトリの親など、他のcritical-pathチェックは動いたままです。
この確認は、単なる更新前の注意書きとして加わったものではありません。v2.1.281のChangelogには、rm -rf "$(pwd)"がautoモードと--dangerously-skip-permissionsで確認なしに走っていたのを直し、Bashの許可ルールがあっても確認する形にした、という記述があります。変数を設定すると、この修正前に近い状態へ戻ります。
bash -cの中身を読ませない(INLINE_SHELL_RM_PROMPT)
bash -c 'rm -rf ~'のように、sh・bash・zshなどへ-cで渡したスクリプトの中の削除も、Claude Codeはcritical-path削除として検査します。ダブルクォートで囲んだスクリプトは、呼び出し側のシェルが変数を先に展開するため、find . -name '*.tmp' -exec sh -c "rm -rf \"$1\"/*" _ {} \;のような形は、マッチごとにルートからの削除に展開されると判定されます。シングルクォートで$1に実際の値を結び付けるsh -c 'rm -rf "$1"/*' _ {}は、検査の対象になりません。
CLAUDE_CODE_DISABLE_INLINE_SHELL_RM_PROMPT=1は、~のようなcritical pathが-cスクリプトの中に直接書かれているときの検査を止めます。スクリプトの中の変数や位置パラメータが対象になっているチェックと、他のcritical-pathチェックは動き続けます。v2.1.288以降の変数なので、claude --versionが2.1.287以前を返す環境では効きません。
PowerShell経由のcmd削除の拒否を外す(POWERSHELL_CMD_RM_DENY)
PowerShellツールを有効にしている環境では、Claude Codeはcmdビルトインのrd / rmdir / del / eraseがシステムパスを対象にしたとき、既定ではモードを問わず確認なしで拒否します。システムパスとは、ファイルシステムのルートとその直下、ドライブルートとその直下、ホームディレクトリです。cmd /c rd /s /q C:\Usersのような呼び出しが対象になります。
判定は文字列の見た目より厳密です。リテラルの文字列に続くPowerShell変数は空として扱うため、cmd /c rd /s /q "C:\$name"はC:\の削除と見なされて拒否されます。末尾のワイルドカードは、空にするフォルダとして数えます。cmd /c del /q C:\*は拒否され、プロジェクト内のcmd /c del /q dist\*は拒否されません。
CLAUDE_CODE_DISABLE_POWERSHELL_CMD_RM_DENY=1が止めるのは、このcmdのチェックだけです。同じPowerShellツールのRemove-Itemは別の検査を受けていて、この変数を設定しても変わりません。
Remove-Itemの対象 | 扱い |
|---|---|
| システムパス(ルート、ドライブルート、ホームとそれぞれの直下) | 扱いモードを問わず、確認なしで拒否 |
裸の*、/*や\*で終わる対象($dir/*を含む) | 扱いモードを問わず、クラシファイアより前に拒否 |
作業ディレクトリかその親を-Recurse付きで対象にする | 扱い通常の承認が必要なコマンドと同じ扱い。bypassPermissionsではこのチェック自体が働かない |
最後の行は、モードによって確認・クラシファイア送り(auto)・拒否(dontAsk)に分かれます。上の2行がこの変数でも緩まないのは、この変数がcmdのシステムパス判定に対応する部分だけを対象にしているためです。
4つに共通する落とし穴 — settingsのenvブロックからは効かない
4つの変数はいずれも、claudeを起動するシェルなど「起動環境」で設定する必要があります。settingsファイルのenvブロックに書いても、Claude Codeはそのコピーを無視します。
# 効く: 起動前にシェルでexport
export CLAUDE_CODE_DISABLE_DANGEROUS_RM_TIMEOUT=1
claude効かない例(settings.jsonのenvブロック):
{
"env": {
"CLAUDE_CODE_DISABLE_DANGEROUS_RM_TIMEOUT": "1"
}
}設定したつもりで効かないときは、まずsettingsファイルではなくシェルのexportやCI環境変数として渡しているかを疑ってください。CLAUDE_CODE_DISABLE_POWERSHELL_CMD_RM_DENYも同じ制約です。
CI・コンテナのように毎回新しいプロセスからclaudeを起動する環境なら、起動スクリプトやDockerfileのENV、CIのjob設定でエクスポートすれば反映されます。
無効化せずに通すコマンドの書き方
プロンプトが出る原因が書き方にあるなら、変数を設定するより、コマンドを直すほうが影響は小さく済みます。公式の書き換え方は、対象の形ごとに違います。
# 変数が空だとルート直下の削除になるため、critical pathとして止まる
rm -rf "$DIR"/*
# 各展開に :? のガードを付けると、空・未設定ならシェルがエラーで止まり、この検査を通る
rm -rf "${DIR:?}"/*$HOMEのように、普通は値が入っている変数のグロブ: リテラルのパスに書き換えます。$(pwd)などから代入した変数: これもリテラルのパスにします。値が空ではないので、"${D:?}"のガードでは検査が解けません。- コマンド置換の出力だけが対象: 置換を単独で先に実行し、出力されたリテラルのパスを削除します。プロンプトがClaudeに求める手順と同じです。
ガードを付けた形は、bypassPermissionsモードでは、他のcritical-pathチェックに引っかからない限りプロンプトなしで走ります。「止められる確認を外す」のではなく「そもそも確認が要らない書き方にする」という選び方です。
無効化しても残るチェック
4つの環境変数はいずれも1つのチェックだけを無効化し、critical-pathの判定ロジック全体を止めるものではありません。次はどの変数を設定しても残ります。外付けドライブ向けの確認を止める変数はマウントドライブのrm確認を止める手順にあります。
- 許可ルールや
PreToolUseフックの"allow"がcritical-path削除を承認することはない(サーキットブレーカー自体は無効化できません) - 一致する拒否ルールがある削除は、拒否されたままです
- PowerShellの
Remove-Itemによるシステムパスとワイルドカードへの削除は、モードを問わず拒否されます
判定ロジック全体は止まりませんが、設定した変数に対応する削除は、確認を経ずに進むことがあります。たとえばSUBSTITUTION_RM_PROMPTを設定すると、rm -rf "$(pwd)"はautoモードでも確認なしで走ります。
使い分けの目安
| 状況 | 向き | 理由 |
|---|---|---|
| 対話的なターミナルで作業ログを見ながら使う | 向き設定は不要 | 理由時間制限内に人が応答できるため、そもそも詰まりにくい |
完全非対話のCI/CDジョブで-p実行する | 向き慎重に検討 | 理由autoではターミナル確認自体が出せず即座に拒否される。critical-path対象のパスをそもそも作らない設計の方が安全 |
| 隔離済みの使い捨てコンテナでバッチ処理を回す | 向き検討の余地あり | 理由誤削除の影響範囲がコンテナ内に閉じるなら、時間制限やコマンド置換チェックの緩和が現実的な選択肢になる |
| 本番ホストや共有マシンで直接作業する | 向き影響が大きい | 理由critical-pathチェックは人為ミスに対するサーキットブレーカーであり、外すほど防波堤が薄くなる |
設定した変数は、用途が終わったら起動環境から外せば、チェックが元の状態に戻ります。
まとめ
プロンプトが出る局面を4つのうちから切り分け、必要な1つだけを起動環境に設定します。その前に、${DIR:?}のガードやリテラルパスで通せないかも見直せます。運用に組み込む前に、破壊的操作を確認させるプロンプト設計やauto modeの安全確認の強化内容も合わせて読んでおくと、意図しない削除を避けやすくなります。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。