Claude Code を bypassPermissions で自走させたいけど、勝手にファイルを消されたり、git push されたり、データを外部送信されたら困る……ということ、ありませんか?
結論からお伝えすると、cage + hooks + settings.json の3層構造で「開いているフォルダの中だけ自由に操作できる」環境を作れます。 セットアップスクリプト1発で導入できるようにClaude codeと一緒に作ったので、GitHub で公開しました。
この記事では、なぜこの設定が必要なのか、どんな仕組みで守っているのか、セットアップ方法まで、まるっとお伝えします。
なお、この記事はこちらを参考にさせていただきました。感謝です。
bypassPermissions の「自走」は便利、でもドキドキする
Claude Code には bypassPermissions というモードがあります。これを有効にすると、いちいち「このコマンド実行していいですか?」と聞かれなくなるので、作業がめちゃくちゃ快適になります。
で、快適なんですが……ドキドキするんですよね。
だって、Claude が自由にコマンドを叩けるということは、こんなことも起こりうるわけです。
- プロジェクトと関係ないディレクトリのファイルを消す
- 勝手に
git pushしてしまう curlでデータを外部に送信してしまう- 環境変数から API キーを読み取って……あっ、考えたくない
「Claude Code 本体にも sandbox 機能あるやん」と思うかもしれません。ところが、bypassPermissions の状態だと Claude が自力で sandbox を突破するケースが報告されています。なんでやねん。
じゃ、どうすれば、いいのか?
Claude の外側から囲む、3層のガードレール
Claude 本体の機能に頼るのではなく、外側のツールで防御するというアプローチです。3つのレイヤーで守ります。
| レイヤー | ツール | 役割 |
|---|---|---|
| 1 | cage | OS レベルでファイルアクセスを制限 |
| 2 | hooks | コマンド実行前に中身を検証 |
| 3 | settings.json | deny リストで二重防御 |
順番に見ていきましょう。
レイヤー1: cage でフォルダの外に出さない
cage は、macOS 上でプロセスのファイルアクセスを制限できるサンドボックスツールです。
cage claude で起動すると、Claude Code が触れるディレクトリをこちらで指定できます。
# ~/.config/cage/presets.yml
presets:
base:
allow:
- "." # カレントディレクトリのみ!
- "/private/tmp" # macOS tmpディレクトリ
- "/var/folders" # macOS mktemp
- "$HOME/Library/Caches" # macOS アプリキャッシュ
- "$HOME/.npm" # npm キャッシュ
- "$HOME/.local/share" # CLIツール用データ
claude-code:
allow:
- "$CLAUDE_CONFIG_DIR" # ~/.claude(設定・履歴)
ポイントは .(カレントディレクトリ)だけが自由という点です。 それ以外は読み込みすらできません。
cage は OS レベルで制限をかけるので、Claude が「sandbox を突破してやろう」と思っても突破できません。ほっ。
レイヤー2: hooks でコマンドを検証する
cage でファイルアクセスは守れますが、ネットワーク送信や危険なコマンドは別途止める必要があります。
Claude Code の PreToolUse フック機能を使って、Bash コマンドと WebFetch の実行前に中身をチェックする validate-bash.sh を作りました。
止めるもの一覧
けっこうな量を止めています。5つのカテゴリに分けて紹介します。
間接実行の防止(パターン回避対策)
Claude は賢いので、直接的なコマンドを禁止すると迂回しようとすることがあります。なので先回りして塞ぎます。
eval— 文字列をコマンドとして実行(全チェックを回避できてしまう)| bash/| sh— パイプ経由のシェル実行base64 -d— エンコードされたコマンドの復号$VAR push— 変数経由でコマンドを組み立てるsource— 外部スクリプトの読み込み実行
ネットワーク送信の防止
データの外部送信を防ぎます。
curlの POST / PUT / PATCH / DELETEcurlの GET でも$(...)を含む場合(URL にデータを埋め込む手口)wget --post-data/--post-file- Python / Node.js の送信系 API(
requests.post、urlopen、fetch等) nc/netcat
スクリプトファイル経由の送信も検出
ここでハマりました。 インラインのコマンドだけ止めても、Claude が Python スクリプトを書いてから python3 script.py で実行すると、スクリプト内のネットワーク送信コードは検出できません。
というわけで、python3 ファイル名.py や node ファイル名.js の実行を検出したら、そのファイルの中身をスキャンして、requests、urllib、http.client、socket 等のネットワーク関連コードが含まれていないかチェックするようにしました。
# Python スクリプトファイルの実行を検出してスキャン
if echo "$command" | grep -qE '\bpython3?\s+[^-]'; then
script_file=$(echo "$command" | grep -oE '\bpython3?\s+(\S+)' | head -1 | grep -oE '\S+$')
if [[ -n "$script_file" && -f "$script_file" ]]; then
matched=$(grep -nE "$NETWORK_PATTERNS_PY" "$script_file" 2>/dev/null | head -5)
if [[ -n "$matched" ]]; then
deny "Python スクリプト '${script_file}' にネットワーク送信コードが検出されました: ${matched}"
fi
fi
fi
該当行が具体的に表示されるので、何が引っかかったか一目でわかります。
WebFetch ツールの制御
Claude の WebFetch ツール(Bash を経由しない)も、URL にデータを埋め込んで外部送信される可能性があります。クエリパラメータが200文字を超える場合や、単一パラメータの値が100文字を超える場合は拒否します。
通常のドキュメント閲覧はそのまま通るので、開発作業には影響ありません。
環境変数の読み取り防止
env/printenv— 全環境変数の一覧表示export -p— 全 export 変数の一覧set(引数なし)— シェル変数の一覧
API キーやトークンが環境変数に入っていることは多いので、一括表示は止めています。
危険コマンドの防止
git push— 意図しないプッシュを防止git add -A/git add .— ファイル名を個別に指定させるsed/awk— Claude の Edit ツールを使わせるrm -rf /...— カレントディレクトリ外の削除防止
ブロック時はユーザーに確認してもらう
すべての拒否メッセージに「実行が必要な場合は、コマンド全文をユーザーに提示し、手動実行を依頼してください。」という指示が自動で付きます。
なので、業務上 curl -X POST が必要なときの流れはこうなります。
- Claude が POST コマンドを実行しようとする
- hooks が拒否
- Claude がコマンド全文をユーザーに提示する
- ユーザーが中身を確認して、自分で実行する
Claude が勝手にデータを送信することはなく、かつ業務は止まらない、という感じです。ふぅ。
レイヤー3: settings.json で二重防御
hooks でカバーしていますが、settings.json の deny リストでも重要なものは止めています。ベルトとサスペンダー方式ですね。
{
"permissions": {
"defaultMode": "bypassPermissions",
"deny": [
"Bash(git push:*)",
"Bash(git add -A:*)",
"Bash(git add --all:*)"
]
}
}
セットアップは1コマンド
ここまでの設定、手作業で全部やるのはしんどいですよね。setup.sh で自動化しました。
git clone https://github.com/mono96/claude-code-guard.git
cd claude-code-guard
chmod +x setup.sh
./setup.sh
setup.sh がやってくれること:
- cage が未インストールなら
brew installで自動インストール ~/.config/cage/presets.ymlを配置~/.claude/hooks/validate-bash.shを配置して実行権限を付与~/.claude/settings.jsonを既存設定とマージ(env や enabledPlugins を保持)- 全ステップの結果を色付きで表示
既存の設定ファイルがある場合は自動でバックアップしてからマージするので、安心です。
起動方法
cage claude
これだけです。パラメータもそのまま渡せます。
cage claude --continue
cage claude -p "テストを実行して"
毎回 cage を付けるのが面倒なら、.zshrc にこう書いておけば claude コマンドだけで cage 経由になります。
function claude() {
cage claude "$@"
}
残っている限界(正直に書きます)
完璧ではないので、把握しておいてほしい点が2つあります。
1. 個別の環境変数は読める
env は止めていますが、echo $SECRET_KEY のように変数名を直接指定されると止められません。完全に塞ぐと echo $PATH のような正常な操作まで壊れるので、ここはトレードオフとして許容しています。
2. WebSearch は未対策
検索クエリに機密情報を埋め込む可能性はゼロではありませんが、実用上のリスクは低いと判断しています。
カスタマイズ
MCP ツールを追加したとき
そのツールがデータを保存するディレクトリを cage の設定に追加してください。
presets:
claude-code:
allow:
- "$CLAUDE_CONFIG_DIR"
- "$HOME/.serena" # 例: serena を使う場合
拒否コマンドを追加したいとき
validate-bash.sh にパターンを追加するだけです。
# 例: docker を禁止
if echo "$command" | grep -qE '\bdocker\b'; then
deny "docker は禁止です。理由を書く。"
fi
まとめ
Claude Code を bypassPermissions で自走させるなら、Claude の外側からガードレールを張るのが安全です。
- cage で OS レベルのファイルアクセス制限 → カレントディレクトリの中だけ自由
- hooks でコマンド・ネットワーク・環境変数を検証 → 危険な操作は理由付きで拒否
- settings.json で deny リスト → 二重防御
拒否されたコマンドも、ユーザーが確認して手動実行すれば業務は止まりません。
セットアップスクリプト1発で導入できるので、お試しください。




![[ 御礼 ]みなさんに支えてもらって37歳を迎えました。幸せな気持ちです。](https://mono96.jp/wp-content/uploads/2013/05/IMG_5648-1-150x150.jpg)

