Claude Code カスタム|settings.json の deny で事故起動を防ぐ|実例公開
ある夜、当社で事故が起きました。
記事を投稿するためのスクリプトが 2 種類あり、片方は「画像生成と SEO メタ付与をまとめてやる正規ルート」、もう片方は「投稿だけする裏口スクリプト」。AI に「投稿して」と頼んだら、AI は裏口の方を呼んでしまった。SEO メタは付かず、アイキャッチも欠けたまま記事が公開状態になりました。
原因は単純で、「使ってはいけないコマンド」を口頭注意レベルで止めていたから です。CLAUDE.md には「裏口スクリプトは使うな」と書いてありました。けれど AI はうっかり使う。人間も同じです。
今回は、ルールを破れない仕組み——settings.json の deny 設定で、特定コマンドの実行を物理的にブロックする方法を、当社の実例ごと公開します。シリーズ「Claude Code を自分専用に育てる」第 2 回。前回のCLAUDE.md でルールを宣言する話と組み合わせると、運用の安定度が一段上がります。
目次
settings.json とは|CLAUDE.md と何が違うか

settings.json とは
Claude Code の動作を細かく制御する設定ファイル。「このコマンドは確認なしで実行していい」「このコマンドは絶対に実行するな」といった許可と禁止の境界線を、AI に強制的に守らせるための仕組みです。
CLAUDE.md は「お願い」、settings.json は「物理ガード」
前回の記事で扱った CLAUDE.md は、AI に対する 口頭注意 のような存在でした。「うちはこういうルールで動いてください」とお願いするファイル。守ってくれることが多いけれど、絶対ではありません。
settings.json はもう一段強い。「やってはいけない」と書かれたコマンドは、AI が実行しようとした瞬間に 物理的にブロックされる。AI が判断を誤っても、ルール違反のコマンドそのものが走らない仕組みです。
2 つを組み合わせて運用するのが正解
どちらか片方だけで運用するのは推奨しません。CLAUDE.md だけだと AI のうっかりミスが防げず、settings.json だけだと「なぜそのルールがあるのか」が不明なまま運用される。両方そろえて初めて、判断の根拠と物理ガードが噛み合います。
当社では、CLAUDE.md に「裏口スクリプトは使わない」とルールの理由を書き、settings.json で「裏口スクリプトの実行コマンドそのものを拒否」する。あなたの会社で同じ構造を作るなら、このセットを意識してください。
置き場所|プロジェクト直下の .claude フォルダ
settings.json はプロジェクトルートの .claude/settings.json に置きます。隠しフォルダの中なので Finder でも普段は見えません。Claude Code が起動時に自動で読み込み、その後の全コマンド実行に適用されます。
出典: Anthropic Claude Code Hooks Reference(2026)
やらかしの実話|裏口スクリプトで誤投稿した夜

具体的な事故が無いと、なぜ deny が必要か伝わりにくい。当社で実際に起きたケースを 1 つ紹介します。
状況|投稿スクリプトが 2 系統あった
当社の WordPress 投稿フローには 2 つのスクリプトがありました。
| スクリプト | 役割 | 使うべき場面 |
|---|---|---|
| generator.py | 画像生成 + SEO メタ + カテゴリ + 関連記事 + 投稿(一発) | 本番投稿の正規ルート |
| post_to_wordpress.py | 投稿だけ(画像なし・SEO メタなし) | テスト用・通常使わない |
裏口の post_to_wordpress.py は、もともと開発時のテストで使っていたもの。本番では絶対に使わない、というのが暗黙のルールでした。
事故|AI がショートカットを選んだ
「この記事を投稿して」と AI に頼んだとき、AI は手元のスクリプトを 2 つ見比べて、より短くシンプルな post_to_wordpress.py を選びました。AI なりに「効率がいい」と判断したわけです。
結果、画像なし、SEO メタなし、カテゴリ未設定の記事が WordPress に流れ込みました。下書き保存だったので公開はされませんでしたが、もし設定が違っていたら即公開されていた状況。皆さんの運用でも、似た構造の事故は起き得ます。
原因|「やるな」のルールが文字でしかなかった
CLAUDE.md には「post_to_wordpress.py は単体使用禁止」と書いてありました。AI もそれを読んでいたはずです。けれど判断の瞬間、文字のルールよりも「目の前にあるシンプルな選択肢」を優先してしまった。
これは AI 特有の問題ではありません。人間でも疲れているときは、覚えているルールより目の前の楽な選択肢を選びます。皆さんの会社で同じ事故を防ぐなら、判断に頼らず、構造で防ぐ 方が確実、というのがここでの教訓です。
4 パターン deny の実物公開|抜け道を全部塞ぐ

ここからが本題。当社が事故後に組んだ deny 設定の実物を公開します。
同じスクリプトの呼び出し方が 4 パターンある
「post_to_wordpress.py を実行する」という同じ動作にも、コマンドの書き方が 4 通りあります。1 つだけ deny しても、別の書き方で抜けてしまう。実例で見てください。
python3 scripts/post_to_wordpress.py— Python 3 を明示python scripts/post_to_wordpress.py— Python 名で呼ぶ./scripts/post_to_wordpress.py— 直接実行(実行権限あり)python3 ../scripts/post_to_wordpress.py— 別ディレクトリから呼ぶ
当社の deny 設定はこの 4 パターン全部を塞いでいます。実際の設定内容は次のブロック。matcher にコロン(:*)を付ければ、その後ろに引数が続いてもマッチする仕様。
当社の deny ブロック・実物
| deny エントリ | 塞ぐ呼び出し |
|---|---|
| Bash(python3 scripts/post_to_wordpress.py:*) | python3 明示の標準呼び出し |
| Bash(python scripts/post_to_wordpress.py:*) | python 名での呼び出し |
| Bash(./scripts/post_to_wordpress.py:*) | 直接実行(shebang 経由) |
| Bash(python3 *post_to_wordpress.py:*) | ワイルドカードでパス違いを全捕捉 |
この 4 行を JSON の permissions.deny 配列に追加するだけ。3 分の作業で、二度と同じ事故が起きなくなりました。
deny 後の挙動|AI に「禁止です」と返ってくる
deny に登録したコマンドを AI が実行しようとすると、Claude Code が即座にブロックして「このコマンドは権限設定で拒否されています」とメッセージを返します。AI は別のアプローチを探します。当社の場合、AI は自動的に generator.py の正規ルートを選び直しました。
このとき大事なのは、ブロックされた後に AI が 勝手にバイパスしようとしない こと。Claude Code は権限拒否を尊重する設計で、隠れた抜け道を探したりはしません。
allow と deny の使い分け|「迷ったらどっち」フロー

settings.json の permissions には allow と deny の 2 種類があります。混同しがちなので整理します。
3 つの状態を覚える
Claude Code がコマンドを実行しようとしたときの状態は 3 つだけ。
- allow に登録: 確認なしで即実行(毎回確認ダイアログが出ない)
- deny に登録: 物理的にブロック(AI も人間も実行できない)
- どちらにもない: 実行のたびに人間に確認ダイアログ
つまり allow は時短のため、deny は事故防止のため。役割が違います。混ぜて考えると設計がぼやけます。
allow に入れるべきコマンド・3 つの基準
allow に登録するべきは、次の 3 つを満たすコマンドだけ。
- 1 日に何度も実行する(確認ダイアログが手間)
- 失敗してもデータが消えたり外部に影響しない(読み取り系・ローカル限定)
- AI が誤実行しても被害が小さい
当社の allow には git status や ls、ローカルファイルの読み取りコマンドだけが入っています。逆に git push や rm は入れません。確認の手間より事故のリスクが大きいからです。
deny に入れるべきコマンド・3 つの基準
deny に登録するべきは、次のいずれかに該当するコマンド。
- 本番環境を直接書き換える(DB 削除、本番デプロイ、外部 API への破壊的呼び出し)
- 「正規ルートがあるのに使ってはいけない裏口」が存在する(当社の post_to_wordpress.py のような構造)
- 過去 1 度でも事故を起こしたコマンド
事故ベースで増やしていくのが現実的です。あなたも、最初から完璧な deny リストを作ろうとしないでください。1 件やらかすたびに 1 行増やす、で十分機能します。
シリーズ次回予告
deny で「やってはいけないこと」を物理的に止めたら、次は「やるべきこと」を自動で実行させる仕組みです。次回は Stop hook を使ってセッション終了時に自動でテストを走らせる方法、当社の実装で 146 件のテストを 0.07 秒で回している実例をベースに解説します。
シリーズ全体の入口として、まだ Claude Code を触ったことのない方は前々回の入門編から読むと話がつながりやすいです。
settings.json で物理ガードについてよくある質問

deny に入れたコマンドを一時的に解除したい時はどうしますか
解除は推奨しません。理由は、一時解除のために settings.json を編集するとそのまま戻し忘れる事故が起きやすいためです。どうしても実行が必要な時は、settings.json はそのままにして、ターミナルから手動で実行してください。Claude Code 経由の deny は、ターミナル直接操作には適用されません。
deny の書き方を間違えると Claude Code は動かなくなりますか
動かなくなることはありませんが、意図しないコマンドまでブロックする可能性はあります。新規 deny を追加した直後は、よく使うコマンドが普段通り動くか軽く確認するのが安全です。複数の wildcard(*)を含む deny は特に注意してください。広く効きすぎることがあります。
チームで Claude Code を使う場合、settings.json はどう共有しますか
プロジェクト直下の .claude/settings.json をチームで共有する形が標準です。Git にコミットすればメンバー全員に同じ deny ルールが適用されます。一方、個人の作業効率のための allow 設定は .claude/settings.local.json に分離する設計が推奨されます。チーム共有のルールと、個人の好みを分離する構造です。
過去に事故を起こしたコマンドを思い出せません。どこから始めればよいですか
過去ログから「失敗 / 復旧 / やり直し」のキーワードを検索するのが早いです。Slack や作業ログ、Git の履歴から、過去 3 ヶ月で 1 回でも事故対応をしたコマンドを抽出してください。それが第 1 弾の deny 候補です。完璧を目指さず、まず 3〜5 件登録して運用しながら増やしていく方が、現実的に機能します。
