一行要約

ルールは deny → ask → allow の順に評価され、最初に一致したものが結果を決める具体性は順序を変えない)。したがって広い deny ルールに狭い allow の例外を持たせることはできない。

要点

3階層の権限システム

ツール種別承認が要るか「今後聞かない」の効果
読み取り専用ファイル読み取り、Grep不要(作業ディレクトリと追加ディレクトリ内)
Bash コマンドシェル実行必要(組み込みの読み取り専用コマンドを除く)リポジトリとコマンドごとに永続
ファイル変更Edit / Write必要セッション終了まで

評価順序(最重要)

Rules are evaluated in order: deny, then ask, then allow. The first match in that order determines the outcome, and rule specificity doesn’t change the order. ルールは deny → ask → allow の順で評価される。この順序で最初に一致したものが結果を決め、ルールの具体性は順序を変えない

A broad deny rule like Bash(aws *) blocks every matching call, including calls that also match a narrower allow rule like Bash(aws s3 ls), so a deny rule can’t carry allowlist exceptions. Bash(aws *) のような広い deny ルールは、一致するすべての呼び出しをブロックする。Bash(aws s3 ls) のようなより狭い allow ルールにも一致する呼び出しを含めてである。つまりdeny ルールに許可の例外を持たせることはできない

deny ルールは2種類で挙動が違う:

  • 裸のツール名Bash)— ツールを Claude の context から完全に取り除く。Claude はその存在を知らない
  • スコープ付きBash(rm *))— ツールは使えるまま、一致する呼び出しをブロックする

Permission rules are enforced by Claude Code, not by the model. Instructions in your prompt or CLAUDE.md shape what Claude tries to do, but they don’t change what Claude Code allows. permission ルールを強制するのは Claude Code であって、モデルではない。プロンプトや CLAUDE.md の指示は Claude が何をしようとするかを形づくるが、Claude Code が何を許すかは変えない。

ルール構文

形式は Tool または Tool(specifier)

入力パラメータでのマッチ(deny / ask 専用):

  • Agent(model:opus) / Agent(isolation:worktree) / Bash(run_in_background:true)
  • allow ルールでは使えない(1つのパラメータ値だけでは呼び出し全体の安全性を保証しないため)
  • パラメータ名はツール入力の直下フィールドでなければならない。オブジェクトや配列の中は不可
  • **1ルールにつき1パラメータ。**2つを条件にしたいなら2ルール書く
  • モデルが省略したパラメータは決してマッチしないAgent(model:*)model 未設定の呼び出しにマッチしない
  • 正規化前のリテラル入力と比較されるAgent(model:opus) は別名 opus にはマッチするが完全なモデル ID にはマッチしない

ツールの主要コンテンツフィールドはこの方法でマッチできない — Bash/PowerShell の command、Read/Edit/Write の file_path、Grep/Glob の path、WebFetch の urlBash(command:rm *)複合コマンドで迂回できてしまうので Claude Code は無視して起動時に警告を出す。

ツール名のワイルドカード: deny / ask はツール名の位置でも glob を受け付ける("*" は全ツール、"mcp__*" は全 MCP ツール)。allow のツール名 glob は mcp__<server>__ の literal な接頭辞の後でのみ許される — サーバ部分に glob は使えない。"*""mcp__*" のような anchor のない allow glob は警告付きでスキップされ、何も自動承認しない

The label shown for a tool in the transcript can differ from its canonical name. For example, the tool labeled Stop Task has the canonical name TaskStop. Permission rules and hook matchers match the canonical name only. トランスクリプト上でツールに表示されるラベルは、正式名称と異なることがある。たとえば Stop Task と表示されるツールの正式名称は TaskStop である。permission ルールと hook のマッチャーは正式名称にのみ一致する。

Bash ルールの落とし穴

単一の * は空白を含む任意の文字列にマッチするので、1つのワイルドカードが複数の引数にまたがる。

末尾の * の前に空白があると単語境界を強制する:

  • Bash(ls *)ls -la にマッチするが lsof にはマッチしない
  • Bash(ls*)(空白なし)は lsof にもマッチする

複合コマンド:

Claude Code is aware of shell operators, so a rule like Bash(safe-cmd *) won’t give it permission to run safe-cmd && other-cmd. The recognized command separators are &&, ||, ;, |, |&, &, and newlines. A rule must match each subcommand independently. Claude Code はシェルの演算子を認識するため、Bash(safe-cmd *) というルールでは safe-cmd && other-cmd の実行は許可されない。認識される区切りは &&||;||&&、改行である。ルールは各サブコマンドに個別に一致しなければならない。

複合コマンドを「今後聞かない」で承認すると、サブコマンドごとに別々のルールが保存される(最大5つ)。

ラッパーの剥がし方(ここは知らないと事故る):

剥がされる剥がされない
timeout, time, nice, nohup, stdbuf, シェル組み込みの command / builtin, zsh の noglobcommand -v(照会形式)、zsh の nocorrect
フラグなしの xargsxargs -n1 ...(フラグ付き)
既知の安全な環境変数の先頭代入NODE_ENV=test npm test)。ただし allow ルールはそれ以外の変数代入を跨がない。deny / ask は任意の先頭代入を跨いでマッチする

This wrapper list is built in and is not configurable. Development environment runners such as direnv exec, devbox run, mise exec, npx, and docker exec are not in the list. Because these tools execute their arguments as a command, a rule like Bash(devbox run *) matches whatever comes after run, including devbox run rm -rf .. このラッパーの一覧は組み込みであり、設定で変更できない。direnv execdevbox runmise execnpxdocker exec といった開発環境のランナーは一覧に含まれない。これらは引数をコマンドとして実行するため、Bash(devbox run *) のようなルールは run の後に来るものすべてに一致してしまう。devbox run rm -rf . も含めてである

watch / setsid / ionice / flock のような exec ラッパーは常に確認を求め、接頭辞ルールで自動承認できない。 find-exec / -delete も同様。

組み込みの読み取り専用コマンド(設定不可): ls, cat, echo, pwd, head, tail, grep, find, wc, which, diff, stat, du, cd、および git の読み取り専用形式。

それでも確認を求めるケース:

  • 書き込み可能フラグを持つコマンドでの unquoted globfind, sort, sed, git)— glob が -delete のようなフラグに展開されうるため
  • 別デーモンを指す docker-H, --context, Podman の --url / --connection
  • file-m / -f(フラグの値に書かれたパスを開くため)
  • Windows のネットワーク(UNC)パスWindows の認証情報が相手ホストに送られうるため
  • 解析できないコマンド、および 10,000文字を超えるコマンド
  • cd + git(新しいディレクトリの hook が走りうる)、cd + 出力リダイレクト(リダイレクト先の解決先が確定できない場合。/dev/null だけなら確認なし)

Bash permission patterns that try to constrain command arguments are fragile. Bash(curl http://github.com/ *) won’t match curl -X GET http://..., https://, redirects, URL=... && curl $URL, or extra spaces. コマンドの引数を制約しようとする Bash の permission パターンは脆い。Bash(curl http://github.com/ *) は、curl -X GET http://...https://、リダイレクト、URL=... && curl $URL、余分な空白のいずれにも一致しない。

URL を絞りたいなら: curl / wget を deny し、WebFetch の WebFetch(domain:...) を使う / PreToolUse hook で検証する / CLAUDE.md は補助にとどめる。WebFetch だけではネットワークアクセスを防げない — Bash が許可されていれば curl で任意の URL に届く。

Read / Edit ルール

  • Edit ルールはファイルを編集する全組み込みツールに適用される
  • Read の deny ルールは同じパスの Edit(新規作成を含む)もブロックする。 ただし Write と NotebookEdit はカバーされないので、どのツールも変更してはいけないパスには Edit の deny ルールも足す
  • ファイル権限は Edit(path)Read(path) ルールだけで判定される。 Write(...) / NotebookEdit(...) / Glob(...) にパスルールを書いても受理はされるが決して参照されず、起動時に警告が出る

パターンの anchor 4種(gitignore 構文):

パターン意味
//pathファイルシステムルートからの絶対パス
~/pathホームディレクトリから
/path設定ファイルの出所からの相対
path / ./pathカレントディレクトリからの相対

A pattern like /Users/alice/file isn’t an absolute path. The single leading slash anchors at the settings source, not the filesystem root. Use //Users/alice/file for absolute paths. /Users/alice/file というパターンは絶対パスではない。先頭のスラッシュ1つは、ファイルシステムのルートではなく設定ファイルの位置を基準にする。絶対パスには //Users/alice/file を使え

/path の解決先は設定の出所で変わる:

定義場所/path の解決先
.claude/settings.json<project root>/path
.claude/settings.local.json<original cwd>/path
~/.claude/settings.json~/.claude/path
--settings <file><file のディレクトリ>/path
CLI フラグ / /permissions / セッションルール<original cwd>/path

A deny rule such as Read(/secrets/**) in user settings blocks ~/.claude/secrets/**, not a secrets directory in your project. user 設定にある Read(/secrets/**) のような deny ルールがブロックするのは ~/.claude/secrets/** であって、プロジェクト内の secrets ディレクトリではない

Read and Edit deny rules apply to Claude’s built-in file tools and to file commands Claude Code recognizes in Bash, such as cat, head, tail, and sed. They don’t apply to arbitrary subprocesses that read or write files indirectly, like a Python or Node script. For OS-level enforcement, enable the sandbox. Read と Edit の deny ルールは、Claude の組み込みファイルツールと、Claude Code が Bash 内で認識するファイル操作コマンドcatheadtailsed など)に適用される。**Python や Node のスクリプトのように、間接的にファイルを読み書きする任意のサブプロセスには適用されない。**OS レベルで強制したいなら sandbox を有効にせよ。

WebFetch ルール

  • WebFetch(domain:example.com) — そのホスト
  • WebFetch(domain:*.example.com)任意の深さのサブドメイン。ただし example.com 自身にはマッチしない
  • 先頭の *. と単独の * 以外の位置では、ワイルドカードは2つのドットの間だけにマッチするexample.*example.org にマッチするが example.evil.com にはマッチしない(攻撃者が登録できるドメインに末尾ワイルドカードがマッチしないようにするため)

Agent / Cd ルール

  • Agent(Explore) / Agent(Plan) / Agent(my-custom-agent)
  • Cd はモデルが呼べるツールではない/cd を自分で実行したときにだけ適用される。Cd allow ルールを1つでも足すと /cd が allowlist モードに切り替わる
  • Cd のパスマッチはディレクトリパス全体に anchor される(gitignore 形式ではない)。*ちょうど1セグメント** はセグメントを跨ぐ。末尾の /** はその root 自体にもマッチする

hook との関係

Hook decisions don’t bypass permission rules. Claude Code evaluates deny and ask rules regardless of what a PreToolUse hook returns. **hook の判断は permission ルールを迂回しない。**PreToolUse hook が何を返そうと、Claude Code は deny と ask のルールを評価する。

ただし exit code 2 のブロックは別:

A hook that exits with code 2 stops the tool call before permission rules are evaluated, so the block applies even when an allow rule would otherwise let the call proceed. 終了コード 2 で終わる hook は、permission ルールが評価される前にツール呼び出しを止める。そのため、allow ルールなら通していたはずの呼び出しでもブロックが効く

「Bash を全許可しつつ一部だけ止める」を実現するには、"Bash" を allow に入れて、PreToolUse hook で特定のコマンドを拒否する。

作業ディレクトリと追加ディレクトリ

--add-dir / /add-dir からロードされる設定permissions.additionalDirectories 設定からは何もロードされない):

設定--add-dir からロードされるか
.claude/skills/ の skillはい(live reload 付き)
.claude/agents/ の subagentはい
.claude/settings.jsonenabledPluginsextraKnownMarketplaces キーのみ
CLAUDE.md / .claude/rules/ / CLAUDE.local.mdCLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 のときのみ

/cd/add-dir と違ってセッションを移動させる — 新しいディレクトリの CLAUDE.md がロードされ、--resume もそこから見つかる。

設定の優先順位

Permission rules follow the same settings precedence as all other settings, with managed settings highest: no other level, including command line arguments, can override a managed permission rule. permission ルールは他の設定と同じ優先順位に従い、managed 設定が最上位に来る。コマンドライン引数を含め、他のどの階層も managed の permission ルールを上書きできない

If a tool is denied at any level, no other level can allow it. The same holds across scopes: a user-level deny blocks a project-level allow. **いずれかの階層でツールが拒否されていれば、他のどの階層もそれを許可できない。**これはスコープをまたいでも成り立つ。user 階層の deny は project 階層の allow をブロックする。

project の allow ルールと workspace trust

permissions.allow rules and permissions.additionalDirectories entries in a project’s .claude/settings.json grant capability, so Claude Code applies them only after you accept the workspace trust dialog. deny and ask rules aren’t affected, since they only restrict. プロジェクトの .claude/settings.json にある permissions.allow ルールと permissions.additionalDirectories の項目は権限を与えるものなので、Claude Code はワークスペースの信頼ダイアログを承認した後にのみこれらを適用する。denyask のルールは制限するだけなので影響を受けない。

trust はワークスペース単位で保存され、git リポジトリのルート(リポジトリ外なら起動ディレクトリ)をキーにする。親ディレクトリを信頼しても、入れ子のプロジェクトの allow ルールは適用されない。

managed 専用の設定(抜粋)

user / project に書いても効かないもの:

設定効果
allowManagedPermissionRulesOnlyuser / project が allow / ask / deny を定義できなくする
allowManagedHooksOnlymanaged / SDK / managed で強制有効化した plugin の hook 以外をブロック
allowManagedMcpServersOnlymanaged の allowedMcpServers のみ有効(deniedMcpServers は全ソースからマージ)
strictPluginOnlyCustomizationskill / agent / hook / MCP を plugin か managed からのみに限定。true で全4面、配列で個別指定
disableSideloadFlags--plugin-dir / --plugin-url / --agents / --mcp-config を起動時に拒否
strictKnownMarketplaces / blockedMarketplacesplugin marketplace の制限
forceRemoteSettingsRefreshremote managed settings の取得に失敗したら起動を止める(fail-closed)

そのまま使える具体例

npm と git commit を許可し、git push を禁じる:

{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(git commit *)",
      "Bash(git * main)",
      "Bash(* --version)",
      "Bash(* --help *)"
    ],
    "deny": [
      "Bash(git push *)"
    ]
  }
}

全 MCP ツールを禁じる:

{
  "permissions": {
    "deny": ["mcp__*"]
  }
}

Explore エージェントを無効化する:

{
  "permissions": {
    "deny": ["Agent(Explore)"]
  }
}

「Bash は全許可、一部だけ hook で止める」構成(この文書で最も実用的なパターン):

{
  "permissions": {
    "allow": ["Bash"]
  },
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous.sh" }
        ]
      }
    ]
  }
}

user settings から全プロジェクトに効くパスルールを書く(/ ではなく //~/):

{
  "permissions": {
    "deny": [
      "Read(//Users/alice/secrets/**)",
      "Read(~/.ssh/**)"
    ]
  }
}

環境ランナーを許可するときは内側のコマンドまで書く:

❌ Bash(devbox run *)          → devbox run rm -rf . まで通る
✅ Bash(devbox run npm test)   → 許可したい内側のコマンドごとに1ルール

ワイルドカードの単語境界:

Bash(ls *)   → ls -la にマッチ、lsof にはマッチしない
Bash(ls*)    → lsof にもマッチしてしまう
Bash(ls:*)   → Bash(ls *) と同じ(:* は末尾でのみ有効)

原典で言及されている関連文書

  • permission-modes — モードとルールの関係
  • hooks-guide — exit code 2 のブロックが allow ルールより優先されること
  • claude-code-sandboxing — OS レベルの強制(任意のサブプロセスまでカバーする唯一の手段)
  • mcp — コネクタツールの askrequiresUserInteraction
  • sub-agentsAgent(...) ルール
  • large-codebasesRead deny ルールによる読み取り削減

未取得の派生リンク