CI やバッチに claude -p を組み込むとき、permission プロンプトが邪魔で --dangerously-skip-permissions を反射的に付けていませんか。それは「プロンプトを消す」設定であって「守る」設定ではありません。2026-10-19 時点の公式 docs を読み直すと、permission モードが担う役割と、その外側に置くべき隔離の線引きがはっきり分かれます。本稿では、コンテナ内で unattended に Claude Code を回す通し実装を 1 本作りながら、permission モードの限界と、Bash sandbox・deny ルール・コンテナ隔離という三層の組み方を具体的に示します。
permission モードは「プロンプトを消す」機能であって「守る」機能ではない
--dangerously-skip-permissions(bypassPermissions モード)は permission プロンプトと安全チェックを無効化し、protected path への書き込みを含むすべてのツール呼び出しを即時実行します。auto モードはプロンプトを減らしますが、安全性を保証するものではありません。この二つはいずれも「人間の確認回数を減らす」ための機能であり、攻撃者や誤動作からあなたのホストを守る境界ではない、という理解が出発点になります。
bypassPermissions offers no protection against prompt injection or unintended actions
docs は bypassPermissions について、プロンプトインジェクションや意図しない操作に対する保護を提供しないと明記しています。つまり、取り込んだ Issue 本文や依存パッケージの README に悪意ある指示が混ざっていても、bypassPermissions はそれを止めません。プロンプトの数とリスクの大きさは別物だ、ということです。
隔離を足す:Bash sandbox と deny ルールの二層
では何が守るのか。docs は bypassPermissions を、コンテナ・VM・インターネットアクセスのない dev container といった隔離環境でのみ使うべきだとしています。守る主体はモードではなく、その外側の隔離です。
Bash sandbox と auto モードは独立して動作し、組み合わせられます。sandbox の効き目の中心はネットワークです。
Sandboxed commands don't get network access by default
sandbox 化されたコマンドはデフォルトでネットワークアクセスを持ちません。Claude がコマンド自体に必要なホスト名を記載し、classifier がそれをコマンドと一緒にレビューし、承認済みリストがそのコマンド単体にだけホストを開放します。これにより、たとえモデルが余計なコマンドを出しても、外へ出ていく経路がデフォルトで閉じます。
二層目が deny ルールです。deny ルールは bypassPermissions を含むすべてのモードでブロックが有効です。prompt を飛ばすモードでも、deny に書いたものは実行されません。ここが自動化の安全弁になります。
ゼロから作る:コンテナで unattended に回す通し実装
空のディレクトリから、deny ルールと sandbox を効かせたコンテナで claude -p を回すまでを通します。
まずプロジェクトを作ります。
mkdir claude-sandbox-ci && cd claude-sandbox-ci
mkdir -p .claude secrets
echo "SECRET_TOKEN=do-not-read-me" > secrets/token.env
.claude/settings.json を置きます。deny ルールと sandbox 有効化を書きます。
{
"permissions": {
"deny": [
"Bash(echo hello-from-deny-test)",
"Read(./secrets/**)"
]
},
"sandbox": {
"enabled": true
}
}
コマンドラインの /sandbox を使わず、settings ファイルで sandbox.enabled を true にすることで sandbox を有効化できます。unattended 実行では対話操作が無いので、この書き方が前提になります。
Dockerfile を置きます。bypassPermissions は Linux・macOS では非 root ユーザーで実行する必要があるため、専用ユーザーを作ります。
FROM node:22-slim
RUN npm install -g @anthropic-ai/claude-code
# bypassPermissions は root/sudo では起動できないため非 root ユーザーで実行する
RUN useradd --create-home agent
USER agent
WORKDIR /home/agent/project
ビルドします。
docker build -t claude-sandbox-ci .
実行します。fully unattended でコンテナ内実行する場合は --dangerously-skip-permissions を使い、非 root で動かします。
docker run --rm -it \
-e ANTHROPIC_API_KEY \
-v "$PWD":/home/agent/project \
claude-sandbox-ci \
claude -p "次のコマンドを実行して: echo hello-from-deny-test" \
--dangerously-skip-permissions
Deny rules block in every mode, including bypassPermissions
deny ルールは bypassPermissions を含むすべてのモードでブロックが有効なので、--dangerously-skip-permissions を付けていても、deny に書いた echo hello-from-deny-test は拒否されます。これがこの構成の肝です。期待される表示は note 版の動作確認節に逐語で載せます。
docs に無い1節:私ならこう線を引く(設計判断)
ここからは事実の整理ではなく、設計の意見です(事実と区別して書きます)。私なら、permission モードを「境界」として設計に組み込みません。境界はコンテナだけ、permission とモードはその内側の運用補助、と割り切ります。
approve an rm or rmdir command that targets a critical path, even in modes that skip other prompts
docs によれば、Claude Code はどのモードでも、allow ルールや PreToolUse フックの allow 応答が critical path を対象とする rm・rmdir を承認することを許しません。プロンプトを飛ばすモードでも、です。これは「最悪の操作だけは別建てで止める」という設計思想の表れで、裏を返せば、それ以外のリスクは自分で deny とコンテナに落とし込む前提だということです。
意見として、三つの線引きを採ります。第一に、ANTHROPIC_API_KEY 以外の秘密はコンテナに渡さず、渡すものも read を deny で塞ぎます。bypassPermissions はプロンプトインジェクションを止めないので、秘密はそもそもコンテナに入れないのが安全です。第二に、ローカルで試す段では bypass を避け、Manual mode と Bash sandbox の auto-allow モードを組み合わせます。classifier を経由せずプロンプトを減らせて、しかもコマンドはネットワークから隔離されます。第三に、クラウドで同じ settings を使い回さないことです。クラウドセッションは defaultMode: "bypassPermissions" と "dontAsk" を適用しないため、ローカルで通る前提がクラウドで崩れます。
運用上の落とし穴も挙げます。auto モードは classifier が連続 3 回または合計 20 回ブロックすると一時停止し、プロンプト表示に戻ります。unattended 前提の auto は、この停止で止まり得ます。より厳格な CI では、通常プロンプトされる全ツール呼び出しを自動拒否する dontAsk モードを使い、許可するものは allow で事前列挙する方が、挙動が読めます。