Claude Code permissions—allow/denyの落とし穴と評価順序

Claude Code permissions—allow/denyの落とし穴と評価順序 | mohablog

結論から言うと、Claude Code の permissionsdeny → ask → allow の順で評価され、最初にマッチしたルールが勝ちます。この優先順位を頭に入れておかないと、「Bash(npm run *) を allow に入れたのに毎回確認される」とか、逆に「Bash(devbox run *) を許可したつもりが devbox run rm -rf . まで通ってしまう」といった事故に直結します。

v2.1.119(執筆時点での最新)で実際に ~/.claude/settings.json.claude/settings.json を触りながら、permissions 設計のハマりどころを整理しました。サジェストでもよく出る「allow all」「deny 効かない」あたりの疑問にも、公式ドキュメントの Configure permissions の記述に沿って答えていきます。

目次

許可ルールは「広く・狭く」の2層で組むのが効率的

permissions の運用で一番効くのは、ルールの粒度を分けることです。とくに Bash 系は、許可を狭めすぎるとプロンプトの嵐になり、緩めすぎると事故るので、二層に分けて考えると整理しやすくなります。

狭い deny を上に置き、広い allow を下に置く

運用しやすいパターンは、危険な操作を deny で先に塞ぎ、その上で日常的な操作を広く allow する形です。評価順が deny → ask → allow なので、たとえ allow に Bash(全許可)と書いても、deny にマッチした時点で停止します。

{
  "permissions": {
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git push --force *)",
      "Bash(curl *)",
      "Read(./.env)",
      "Read(./.env.*)",
      "Edit(/.git/**)"
    ],
    "ask": [
      "Bash(git push *)",
      "Bash(npm publish *)"
    ],
    "allow": [
      "Bash(npm run *)",
      "Bash(git status)",
      "Bash(git diff *)",
      "Bash(git log *)"
    ]
  }
}
/permissions コマンドで現在のルール一覧と適用元の settings.json を確認できる

この構成だと「破壊的なものは絶対に通さない」「公開系は人間が確認する」「日常作業は素通り」の3層が成立します。

「allow all」は本当に必要か一度立ち止まる

サジェストに「claude code permissions allow all」が並ぶくらい、全許可の需要は高いです。やり方は defaultModebypassPermissions にするか、起動時に --dangerously-skip-permissions を渡す方法があります。ただ公式ドキュメントの警告にもある通り、隔離環境(コンテナ・VM)以外では推奨されません。

個人的には、普段は defaultMode: "acceptEdits" にして編集だけ自動承認、Bash と WebFetch は明示 allow、というバランスが扱いやすいです。acceptEdits は編集と mkdir/touch/mv/cp など基本的なファイル操作も自動承認してくれるので、コーディング中の確認回数がかなり減ります。

settings.json は4つのスコープで評価される

permissions の挙動を理解するうえで避けて通れないのが、settings.json の階層構造です。同じキーが複数のファイルに書かれていると、どれが優先されるか分からなくなりがちです。

優先順位の高い順に並べると

公式ドキュメントの Settings precedence セクションに明記されている順序がこれです。

優先順位場所用途
1Managed settings組織IT管理者の強制設定(macOS: /Library/Application Support/ClaudeCode/managed-settings.json
2CLI 引数--allowedTools, --disallowedTools
3.claude/settings.local.jsonプロジェクト個人用(gitignore 推奨)
4.claude/settings.jsonプロジェクト共有(git にコミット)
5~/.claude/settings.jsonユーザー全体

ポイントは、配列系のキー(allow/ask/deny/additionalDirectories)はマージされて重複排除されることです。「ユーザー設定で deny に入れたものは、プロジェクト設定では外せない」という挙動になります。逆に defaultMode のような単一値は、優先順位の高いスコープが上書きします。

settings.local.json を使い倒す

チーム開発で重宝するのが settings.local.json です。これは自動的に .gitignore に追加される個人用ファイルで、共有設定を汚さずに自分だけの allow ルールを足せます。

tree .claude
.claude
├── settings.json          # チームで共有(コミット対象)
└── settings.local.json    # 個人ローカル(gitignore対象)

たとえばチームの settings.json には最低限の allow しか書かず、個人の settings.local.jsonBash(docker compose *)Bash(make *) を足す、といった運用がやりやすいです。

Bashコマンドのプレフィックスマッチで踏む3つの罠

permissions の罠の8割は Bash ルールに集中します。一見動きそうなパターンが効かないケースを、実際に試して引っかかったものから3つ紹介します。

罠1: スペースの有無で意味が変わる

Bash(ls *)Bash(ls*) は別物です。前者はスペース+ワイルドカードで「単語境界あり」を意味し、後者はスペースなしで「ls から始まるあらゆるコマンド」になります。

{
  "permissions": {
    "allow": [
      "Bash(ls *)"
    ]
  }
}

このルールは ls -la にはマッチしますが、lsof -i :3000 にはマッチしません。逆に Bash(ls*) と書くと両方マッチしてしまいます。lsof を許可するつもりがなかった場合、後者は事故のもとになります。

公式の補助記法として、Bash(ls:*) という末尾コロンスター形式もあります。これは Bash(ls *) と等価で、IDE的な補完表示で使われることがあります。ただし末尾以外で :* を使うとリテラル扱いになるので注意です。

罠2: 複合コマンドはサブコマンドごとに評価される

これは知らないと相当ハマります。&&, ||, ;, |, |&, &, 改行を Claude Code は認識していて、複合コマンドは各サブコマンドが独立に許可されている必要があります。

npm run lint && npm run test
[npm run lint] と [npm run test] が両方とも allow にマッチしないと、確認プロンプトが出る

つまり Bash(npm run lint) だけ allow に入れていると、npm run lint && npm run test は止まります。allow をパターン化(Bash(npm run *))するか、両方を個別に追加する必要があります。

罠3: プロセスラッパーは除去されるが、実行ラッパーは別扱い

これは触ってみないと気づかない仕様です。timeout, time, nice, nohup, stdbuf, それと無フラグの xargs は内部的に剥がされてからルールマッチされます。なので Bash(npm test *)timeout 30 npm test もカバーします。

一方で devbox run, mise exec, npx, docker exec といった実行ラッパーは剥がされません。これは引数を子コマンドとして実行する性質上、内部コマンドが任意になり危険だからです。

{
  "permissions": {
    "allow": [
      "Bash(devbox run *)"
    ]
  }
}
この設定だと、 devbox run rm -rf . も許可されてしまう(公式ドキュメントの Process wrappers セクションに明記)

対策は、内部コマンドを含めた完全形でルールを書くことです。Bash(devbox run npm test) のように具体化して、内部コマンドごとに別ルールを追加します。

Read / Edit のパスパターンは gitignore 仕様

ファイル系ルールはBashとは別の仕様で動きます。公式ドキュメントの Read and Edit セクションに記載されている通り、gitignore 仕様のグロブパターンが使われます。

パス指定の4つの形式

表で整理するとこうなります。

パターン意味
//pathファイルシステムのルートからの絶対パスRead(//Users/alice/secrets/**)
~/pathホームディレクトリからの相対Read(~/.zshrc)
/pathプロジェクトルートからの相対Edit(/src/**/*.ts)
path または ./pathカレントディレクトリからの相対Read(*.env)

初見で間違えやすいのが、/Users/alice/file は絶対パスではないという点です。Claude Codeのpermissionsでは / 単独はプロジェクトルート起点になります。絶対パスで指定したいときは //Users/alice/file とスラッシュ二つにする必要があります。

*」と「**」の違いを意識する

gitignore 準拠なので、シングルアスタリスクは1階層のみ、ダブルアスタリスクは再帰的にマッチします。

{
  "permissions": {
    "deny": [
      "Read(./secrets/*)",
      "Read(./secrets/**)"
    ]
  }
}
./secrets/api.key       → どちらにもマッチ
./secrets/aws/key.pem   → ./secrets/** のみマッチ(* は階層をまたがない)

ディレクトリ全体を再帰的に守りたいときは必ず ** を使うのが鉄則です。

defaultMode と additionalDirectories の使いどころ

permissions オブジェクトには allow/ask/deny 以外にも、運用に効く設定が2つあります。

defaultMode の6種類を使い分ける

v2.1系で利用できる defaultMode の値は6つです。それぞれのユースケースを表にまとめました。

モード挙動使い所
default各ツール初回使用時に確認初心者・新規プロジェクト
acceptEdits編集と基本的なファイル操作を自動承認日常的なコーディング作業
plan分析のみで変更不可コードレビュー・調査
autoAIが安全性を自動判定(research preview)慣れてきた中級者以上
dontAsk事前許可されたツール以外は自動拒否CI/CD・厳格運用
bypassPermissionsほぼすべてスキップ(一部除く)コンテナ・VM内のみ

dontAsk は意外と使い勝手がよくて、CI のように人間が応答できない環境で「許可外は問答無用で拒否」という動きになります。bypassPermissions は便利ですが、.git, .claude, .vscode, .idea, .husky への書き込みだけは確認が残るという点を覚えておくと、想定外の挙動に戸惑わずに済みます。

additionalDirectories でモノレポを横断する

Claude Code はデフォルトで起動ディレクトリ配下しか触れませんが、additionalDirectories で追加できます。

{
  "permissions": {
    "additionalDirectories": [
      "../shared-utils/",
      "../docs/"
    ]
  }
}

注意点として、追加ディレクトリは「ファイル読み書きの許可範囲」を広げるだけで、設定の発見元にはならないことです。例外的に .claude/skills/(live reload あり)と、enabledPlugins/extraKnownMarketplaces だけは読み込まれます。サブエージェントやスラッシュコマンドは追加ディレクトリから読み込まれないので、共有したい場合はユーザー設定(~/.claude/)か Plugin にまとめる必要があります。

deny だけで防げないケースは PreToolUse hook で補強する

permissions だけでは塞げない領域があります。これは公式ドキュメントの警告ボックスにも明記されている話で、知っておかないとセキュリティ上の穴が残ります。

Read/Edit deny は Bash サブプロセスには効かない

たとえば Read(./.env) を deny しても、cat .env を Bash で実行すると素通りします。これは Read ルールが Claude Code の組み込みファイルツールにのみ適用されるためで、Bash で起動したサブプロセスは対象外だからです。

cat .env
DATABASE_URL=postgres://...
API_KEY=sk-xxxxxxxxxx

これを防ぐには、Bash の catless もパスごと deny する、または OS レベルの sandbox を有効化するという2択になります。サンドボックスは Bash コマンドとその子プロセスにファイルシステム制限をかけられる仕組みで、permissions と組み合わせると防御層が二重になります。

PreToolUse hook で動的にブロックする

もう一つの補強策が PreToolUse hook です。ツール呼び出し前にシェルスクリプトを挟めるので、permissions の静的ルールでは表現しきれない判定ができます。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/check-bash.sh"
          }
        ]
      }
    ]
  }
}

フック側のスクリプトで exit code 2 を返すと、permissions ルールに先んじて呼び出しを停止できます。allow ルールに Bash を入れた上で、特定のコマンドだけ hook で弾くという運用が可能です。フック実装の具体例は Claude Codeのセキュリティ対策—Auto modeとHooksで固める3層防御 の記事で扱っています。あわせて読むと多層防御のイメージが掴みやすいと思います。

ちなみに、deny ルール自体が一部のバージョンで Read/Write について期待通り効かないという既知の Issue(GitHub の anthropics/claude-code Issue #6699 / #6631)が報告されていました。最新版では修正されつつありますが、機密情報の保護には permissions と PreToolUse hook、できればサンドボックスまで組み合わせる方が安全です。

permissions が期待通り動かないときに最初に見る場所

設定したのに反映されない、というときの調査手順を整理しておきます。

/permissions コマンドで現状を確認

Claude Code 起動中に /permissions を実行すると、現在有効なルール一覧と、それぞれの適用元 settings.json のパスが表示されます。同じルールが複数ファイルに重複していたり、思っていたファイルから読まれていなかったりすると、ここで一発で分かります。

JSON Schema を有効にして構文ミスを潰す

地味に効くのが、settings.json に $schema を入れることです。VSCode などのエディタでキー名のtypoをすぐに検出できます。

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": []
  }
}

たとえば defualtMode(typo)と書いていても気づかないことが多いので、Schema 検証は地味だけど効果が大きいです。

まとめ

Claude Code の permissions 設計で押さえておきたい要点を整理します。

  • 評価順は deny → ask → allow。最初にマッチしたルールが勝つ
  • 許可ルールは「狭い deny + 広い allow」の二層で組むと事故を減らせる
  • Bash のプレフィックスマッチはスペースの有無で意味が変わり、複合コマンドはサブコマンドごとに評価される
  • devbox run, npx, docker exec といった実行ラッパーは剥がされないので、内部コマンドまで含めて具体化する
  • Read/Edit のパス指定は gitignore 仕様。/ はプロジェクトルート、// がファイルシステム絶対パス
  • defaultMode は acceptEdits を基本に、CI では dontAsk を使うと運用しやすい
  • Read/Edit deny は Bash サブプロセスに効かないため、PreToolUse hook かサンドボックスで補強する
  • 困ったら /permissions で適用元を確認し、$schema でtypoを早期発見する

関連トピックとして、フック設計とサンドボックスを組み合わせた多層防御は、Claude Codeのセキュリティ対策—Auto modeとHooksで固める3層防御 も参考になります。permissionsだけでは届かない領域を補完する位置づけで読むとつながりが見えやすいです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次