# ツールと権限：許可規則・権限モード・サンドボックス

> Claude Code がツール呼び出しを実行する前に通す関門を、公開ドキュメントの規則（deny → ask → allow の評価順、複合コマンドの分割、権限モード、フック、OS のサンドボックス）から模型として定式化し、判定が規則の順序や細かさに依らないこと、deny の単調性、サンドボックスが独立の層であることを証明する。
> https://rikai.mugen-giken.com/computer-science/claude-code-internals/tools-and-permissions

## 0. この記事の要点

Claude Code は、AI が「このコマンドを実行したい」と言ってきたとき、それをそのまま実行せず、実行してよいかを先に確かめます。たとえばファイルを消すコマンドや外部へ何かを送るコマンドは、利用者が事前に決めたルールに照らして、通す・確認する・止めるのどれかに振り分けられます。この記事は、その振り分けのルールがどんな順序で効くのか、ルールを増やしたり並べ替えたりすると何が変わり何が変わらないのかを、集合の言葉で確かめます。読み終えると、自分で書いた許可・禁止のルールが実際にどう判定されるかと、ルールだけでは防げないものが何かを説明できるようになります。

- [前章](/computer-science/claude-code-internals/agent-loop)のループでは、モデルが出力したツール呼び出しは、実行の前に**関門**（実行してよいかを確かめる検査所）を通ります。モデルが何を出力しても、実行されるのは関門を通ったものだけです。公式ドキュメントは「権限規則を強制するのは Claude Code であって、モデルではない」と明記しています。
- 権限規則は allow・ask・deny の 3 種類で、**deny → ask → allow の順**に調べ、最初にマッチした種類で決まります。この記事ではこれを集合の言葉で定義し、判定が規則を書いた順序・規則のある設定ファイルの階層・規則の細かさに依らないことを証明します。
- deny 規則を足しても許可される呼び出しは増えず、allow 規則をいくら足しても deny にマッチする呼び出しは許可されません（単調性。規則を足したときの結果が、安全側と逆には動かない性質）。「より狭い規則が優先する」方式ではこの性質が成り立たないことを、反例で示します。
- シェルの複合コマンドは区切りで分けて判定されるので、`safe` で始まることを許す規則は、`safe && other` を許しません。
- 権限モードは「確認なしで実行できる呼び出しの集合」の基準を決めます。deny 規則はどのモードでも適用されます。
- サンドボックス（プログラムが触れるファイルや通信先を OS が囲い込む仕組み）は、シェルコマンドに対して OS が強制する別の層です。権限の判定が誤って許可しても、サンドボックス下のシェルコマンドの書き込みは境界の中に限られます。接続については、許可ドメインの外のホストの扱いが既定では権限モードに依るので、同じことが言えるのは、許可ドメインを厳格に適用する設定のときです（8 節）。また、ファイル用のツール・MCP サーバ（外部の道具を Claude Code につなぐ別のプログラム）・フック（ツール実行の前後に動く、利用者が登録した処理）はサンドボックスの外で動きます。
- 規則は**コマンドの文字列**に対する構文的な照合です。「このコマンドが危険な効果を持つか」という意味的な性質は一般に決定不能（どんな手順でも、すべての場合に正しく判定することはできない）で、同じ効果を持つ別の書き方は規則を外れます。

<Aside type="tip" title="この記事で使う事前知識">

- **ツール呼び出しと関門**: モデルが出したツールの使用要求を、実行の前に検査する段のこと。→ [エージェントループ](/computer-science/claude-code-internals/agent-loop)
- **ハーネス**: モデルの外側でループを回し、ツールを実際に実行するプログラム。→ [ハーネス](/computer-science/claude-code-internals/agent-loop#def-harness)
- **システムコール**: 実行中のプログラムが、ファイルの書き込みや通信を OS に頼むための入り口。→ [システムコール](/computer-science/cs-basics/operating-systems#def-syscall)
- **プロセス**: 実行中のプログラムを OS が管理する単位。→ [プロセス](/computer-science/cs-basics/operating-systems#def-process)
- **単調性**: 入力（ここでは規則）を増やしたとき、結果が一方向にしか動かない性質。
- **決定不能**: どんな手順（プログラム）でも、すべての入力について正しい答えを出せない問題のこと。

</Aside>

この記事の事実の記述は、2026-10-10 に確認した公式ドキュメントに基づきます。規則の書式・モードの名前・既定値は版で変わるので、各節の出典で最新の記述を確かめてください。命題は、公式ドキュメントが述べる仕様を模型として定式化したものの上で証明します。模型は、判定に介入するプラグイン（mod）を導入していない構成を表します（3.1 節の仮定と <Ref to="rem-permission-model-assumptions" />）。

## 1. 動機

前章の模型では、関門を写像 $g : \mathrm{Name} \times \mathrm{In} \to \{\mathrm{run}, \mathrm{refuse}\}$ として置き、その中身には触れませんでした（<Ref to="computer-science/claude-code-internals/agent-loop#def-agent-loop-model" />）。そして、関門が何であっても、拒否された呼び出しは環境を変えず、ループは壊れないことを示しました（<Ref to="computer-science/claude-code-internals/agent-loop#prop-denied-call-continues" />）。この章は $g$ の中身を扱います。

関門が要る理由は、モデルの出力を信頼できる根拠が無いからです。モデルは学習で得られた関数で、その出力がいつも利用者の意図どおりである保証はありません。読み込んだファイルやウェブページに、モデルを誘導する文章が紛れていることもあります（[安全性の章](/computer-science/claude-code-internals/safety-and-trust-boundaries)で扱います）。そこで、「何を実行してよいか」の判断のうち、機械的に書ける部分をモデルの外へ出します。プロンプトや指示ファイルに「このコマンドは実行しないこと」と書くのは、モデルへのお願いです。権限規則は、[ハーネス](/computer-science/claude-code-internals/agent-loop#def-harness)のコードが実行の直前に確かめる条件で、契約の言葉でいえばツールの実行の事前条件（<Ref to="computer-science/software-design-testing/contracts-and-invariants#def-contract" />）です。

関門の設計には、いくつも選び方があります。規則がぶつかったらどちらを優先するか。`a && b` のような複合コマンドをどう照合するか。規則で決まらない呼び出しをどうするか。規則をすり抜けたコマンドの被害をどう限るか。公式ドキュメントが述べる答えを順に定式化し、それぞれの選択から何が保証され、何が保証されないかを確かめます。

## 2. ツールとツール呼び出し

<Definition id="def-tool-call" title="ツール呼び出し">
**ツール呼び出し**は、ツール名 $n \in \mathrm{Name}$ と入力 $x \in \mathrm{In}$ の組 $q = (n, x)$ である。ツール呼び出しの全体を $Q = \mathrm{Name} \times \mathrm{In}$ と書く。
</Definition>

前章の tool_use ブロック $\mathsf{use}(u, n, x)$ から id を落としたものです。関門は id を見ません。

公式ドキュメントは、組み込みツールを次のように分類しています（2026-10 時点）。ツールの一覧は版で増減するので、ここでは分類と代表的なものだけを挙げます。全体は出典のツールの一覧を見てください。

| 分類 | できること | 代表的なツール |
|---|---|---|
| ファイル操作 | ファイルを読む・編集する・作る | `Read`・`Edit`・`Write` |
| 検索 | パターンでファイルを探す、内容を検索する | `Glob`・`Grep` |
| 実行 | シェルコマンドを実行する | `Bash` |
| ウェブ | ウェブを検索する、ページを取得する | `WebSearch`・`WebFetch` |
| コード解析 | 定義へのジャンプ、型エラーの報告など（専用のプラグインが必要） | `LSP` |
| 調整 | サブエージェントの起動、利用者への質問など | `Agent`・`AskUserQuestion` |

同じ一覧には、ツールごとに「権限が必要か」の列があります。この列は、すべての行為を利用者が確かめる Manual モード（設定値は `default`）で、作業ディレクトリの中のパスについて確認が出るかどうかを示します。`Bash`・`Edit`・`Write`・`WebFetch`・`WebSearch` は「必要」、`Read`・`Glob`・`Grep` は「不要」です。ただし `Bash` には、確認なしで実行される組み込みの読み取り専用コマンドの集合（`ls`・`cat`・`grep` など）があり、`Read` などのファイルを読むツールも、作業ディレクトリと追加したディレクトリの外のパスでは確認が出ます。

「今後確認しない」を選んだときに承認が続く範囲も、ツールの種類で違います。Bash コマンドの承認はリポジトリとコマンドごとに永続し、ファイルの変更の承認はセッションの終わりまでです。

出典（確認日 2026-10-10）: [Tools reference](https://code.claude.com/docs/en/tools-reference)、[How Claude Code works](https://code.claude.com/docs/en/how-claude-code-works)、[Configure permissions](https://code.claude.com/docs/en/permissions)（"Permission system" の節）。

## 3. 権限規則と判定

### 3.1. 規則

公式ドキュメントによれば、権限規則は 3 種類です。**allow** 規則は、指定したツールを手動の承認なしで使わせます。**ask** 規則は、使おうとするたびに確認を求めます。**deny** 規則は、使わせません。規則の書式は `Tool` または `Tool(specifier)` で、たとえば次のように書きます。

| 規則 | マッチするもの |
|---|---|
| `Bash` | すべての Bash コマンド（`Bash(*)` と同じ） |
| `Bash(npm run build)` | コマンド `npm run build` そのもの |
| `Bash(npm run *)` | `npm run build`、`npm run test --watch` など |
| `Read(./.env)` | カレントディレクトリの `.env` の読み取り |
| `WebFetch(domain:example.com)` | example.com への取得の要求 |

Bash の規則の `*` は、空白を含む任意の文字列に当たります。設定ファイルには次の形で書きます（公式ドキュメントの例と同じ書式の最小例）。

```json
{
  "permissions": {
    "allow": ["Bash(npm run *)", "Bash(git commit *)"],
    "deny": ["Bash(git push *)"]
  }
}
```

次の定義は記号が多いので、先に具体例で見ます。上の設定ファイルの例では、allow に `Bash(npm run *)` があるので、種類 allow の規則にマッチする呼び出しの集まりに `npm run build` が入ります。deny に `Bash(git push *)` があるので、deny の規則にマッチする呼び出しの集まりには `git push origin main` が入ります。定義の $\mathrm{Mat}_\kappa(R)$ は、種類 $\kappa$ の規則のどれかにマッチする呼び出しを全部集めた集合のことです。

<Definition id="def-permission-rule" title="権限規則とマッチ">
**パターン**の集合 $\Pi$ と、**マッチ**の関係 $\models\ \subseteq Q \times \Pi$ が与えられているとする。$q \models \pi$ を「呼び出し $q$ がパターン $\pi$ にマッチする」と読む。**権限規則**は、種類 $\kappa \in \{\mathrm{deny}, \mathrm{ask}, \mathrm{allow}\}$ とパターン $\pi \in \Pi$ の組 $(\kappa, \pi)$ である。**規則集合** $R$ は権限規則の有限集合である。種類 $\kappa$ について
$$
\mathrm{Mat}_\kappa(R) = \{\, q \in Q \mid (\kappa, \pi) \in R \text{ かつ } q \models \pi \text{ となる } \pi \text{ が存在する} \,\}
$$
と置く。
</Definition>

<Definition id="def-permission-decision" title="規則集合に対する判定">
規則集合 $R$ に対する**判定** $\mathrm{dec}_R : Q \to \{\mathrm{deny}, \mathrm{ask}, \mathrm{allow}, \bot\}$ を
$$
\mathrm{dec}_R(q) =
\begin{cases}
\mathrm{deny} & q \in \mathrm{Mat}_{\mathrm{deny}}(R) \text{ のとき} \\
\mathrm{ask} & q \notin \mathrm{Mat}_{\mathrm{deny}}(R) \text{ かつ } q \in \mathrm{Mat}_{\mathrm{ask}}(R) \text{ のとき} \\
\mathrm{allow} & q \notin \mathrm{Mat}_{\mathrm{deny}}(R) \cup \mathrm{Mat}_{\mathrm{ask}}(R) \text{ かつ } q \in \mathrm{Mat}_{\mathrm{allow}}(R) \text{ のとき} \\
\bot & \text{それ以外のとき}
\end{cases}
$$
と定める。$\bot$ は「どの規則にもマッチしない」ことを表し、そのときの扱いは権限モード（6 節）が決める。$\mathrm{Den}(R)$、$\mathrm{Ask}(R)$、$\mathrm{All}(R)$ を、それぞれ判定が $\mathrm{deny}$、$\mathrm{ask}$、$\mathrm{allow}$ になる呼び出しの集合とする。
</Definition>

この定義は、公式ドキュメントの「規則は deny、ask、allow の順に評価される。その順で最初にマッチしたものが結果を決め、規則の細かさは順序を変えない」という記述を、集合の言葉に直したものです。

**この章を通じた仮定。** 以下の命題は、写像 $\mathrm{dec}_R$（とそれを広げた $\mathrm{dec}^\ast_R$・$D_{\mu,R}$・$C_{\mu,R}$）についての主張です。それを「deny にマッチした呼び出しは実行されない」という実物の保証として読めるのは、**判定に介入するプラグイン（公式ドキュメントが mod と呼ぶもの）を導入していない構成**に限ります。mod は規則とフックが決めたあとに答えを返し、条件によっては deny 規則の拒否も置き換えられる、と公式ドキュメントに明記されているからです（<Ref to="rem-permission-model-assumptions" />）。以下、本文で「実行されない」「許可されない」と書くときは、この仮定のもとでの話です。

設定ファイルは複数の階層に置けます。組織の管理者が配る設定（managed settings）、コマンドライン引数、プロジェクトの設定、利用者の設定などです。公式ドキュメントは、権限規則について、管理者の設定が最上位であること、どの階層でも deny されたものは他の階層が allow できないこと、利用者の deny はプロジェクトの allow を止め、その逆も同じであることを述べ、その理由を「どの範囲の deny 規則も allow 規則より先に評価される」と説明しています。模型では、これを規則集合の合併として置きます。

<Definition id="def-settings-scopes" title="設定の階層と規則の合併">
階層の有限集合 $\Sigma$ と、各階層 $\sigma \in \Sigma$ の規則集合 $R^\sigma$ が与えられたとき、判定に使う規則集合を合併 $R = \bigcup_{\sigma \in \Sigma} R^\sigma$ と定める。
</Definition>

### 3.2. 判定は順序・階層・細かさに依らない

<Theorem id="thm-permission-decision" title="判定は規則の集合と呼び出しだけで決まる">
<Ref to="def-permission-decision" /> の判定について次が成り立つ。

1. $\mathrm{dec}_R$ は $Q$ 上の写像として矛盾なく定まる。すなわち、各 $q$ に対して 4 つの場合のちょうど 1 つが成り立つ。
2. $\mathrm{dec}_R(q)$ は、3 つの真偽値 $[q \in \mathrm{Mat}_{\mathrm{deny}}(R)]$、$[q \in \mathrm{Mat}_{\mathrm{ask}}(R)]$、$[q \in \mathrm{Mat}_{\mathrm{allow}}(R)]$ だけで決まる。
3. 規則を並べた列 $\rho_1, \ldots, \rho_N$ と、それを並べ替えた列 $\rho_{\pi(1)}, \ldots, \rho_{\pi(N)}$ は、同じ判定を与える。
4. 階層への分け方に依らない。$\bigcup_\sigma R^\sigma = \bigcup_{\sigma'} R'^{\sigma'}$ ならば、2 つの分け方は同じ判定を与える。
5. 規則の細かさに依らない。$q \models \pi$ かつ $q \models \pi'$ で、$\pi'$ にマッチする呼び出しがすべて $\pi$ にもマッチする（$\pi'$ のほうが狭い）としても、$(\mathrm{deny}, \pi) \in R$ ならば、$(\mathrm{allow}, \pi') \in R$ であるかどうかに関係なく $\mathrm{dec}_R(q) = \mathrm{deny}$ である。
</Theorem>

この定理は新しい事実の発見ではなく、定義した判定が、規則の並び順・設定ファイルの階層・規則の細かさに左右されないことの確認です。証明は、3 つの真偽値（deny にマッチするか、ask にマッチするか、allow にマッチするか）の組ごとに場合分けする形で進みます。当たり前に見える確認ですが、後の節で規則を足したり合成したりしても結論が崩れないことの土台になります。

<Proof of="thm-permission-decision">
(1) $d = [q \in \mathrm{Mat}_{\mathrm{deny}}(R)]$、$a = [q \in \mathrm{Mat}_{\mathrm{ask}}(R)]$、$\ell = [q \in \mathrm{Mat}_{\mathrm{allow}}(R)]$ と置きます。定義の 4 つの場合の条件は、順に「$d$」「$\lnot d \wedge a$」「$\lnot d \wedge \lnot a \wedge \ell$」「$\lnot d \wedge \lnot a \wedge \lnot \ell$」です（4 つ目の「それ以外」は、前の 3 つのどれも成り立たないことで、$d$ が偽、$a$ が偽、$\ell$ が偽と同値です）。$d$ が真なら 1 つ目だけが成り立ちます。$d$ が偽で $a$ が真なら 2 つ目だけ、$d$ と $a$ が偽で $\ell$ が真なら 3 つ目だけ、3 つとも偽なら 4 つ目だけが成り立ちます。どの $(d, a, \ell)$ でも、ちょうど 1 つです。

(2) 1 の場合分けは $(d, a, \ell)$ だけを見ています。

(3) $\mathrm{Mat}_\kappa(R)$ の定義は「条件を満たす規則が $R$ に存在する」という形で、$R$ を集合として見ています。列を並べ替えても、列に現れる規則の集合は同じなので、$\mathrm{Mat}_\kappa$ は 3 つとも変わらず、2 から判定も変わりません。

(4) 判定に使うのは合併 $R$ だけです（<Ref to="def-settings-scopes" />）。合併が等しければ $\mathrm{Mat}_\kappa$ が等しく、2 から判定も等しくなります。

(5) $(\mathrm{deny}, \pi) \in R$ かつ $q \models \pi$ なので $q \in \mathrm{Mat}_{\mathrm{deny}}(R)$、つまり $d$ は真です。1 の場合分けから、$a$ と $\ell$ の値に関係なく $\mathrm{dec}_R(q) = \mathrm{deny}$ です。$(\mathrm{allow}, \pi') \in R$ かどうかは $\ell$ にしか影響しません。
</Proof>

証明は定義の確認だけですが、内容は軽くありません。規則が数十個あり、4 つの設定ファイルに散らばっているとき、「この呼び出しはどうなるか」を知るのに、**ファイルの読み込み順も、規則の行の順も、どちらが具体的かも考えなくてよい**、ということです。確かめるのは 3 つの問い（deny のどれかにマッチするか、ask のどれかに、allow のどれかに）だけです。

<Example id="ex-aws-deny" title="広い deny と狭い allow">
規則集合 $R = \{(\mathrm{deny}, \texttt{Bash(aws *)}),\ (\mathrm{allow}, \texttt{Bash(aws s3 ls)})\}$ と、呼び出し $q = (\texttt{Bash}, \texttt{aws s3 ls})$ を考えます。$q$ は `Bash(aws *)` にも `Bash(aws s3 ls)` にもマッチします。$q \in \mathrm{Mat}_{\mathrm{deny}}(R)$ なので $\mathrm{dec}_R(q) = \mathrm{deny}$ です。allow のほうが狭く具体的ですが、<Ref to="thm-permission-decision" /> の 5 のとおり、結果は変わりません。公式ドキュメントも同じ例を挙げ、allow 規則で deny 規則に例外を作ることはできないと述べています。`aws s3 ls` だけを通したければ、広い deny をやめて、止めたいコマンドを個別に deny に書く必要があります。
</Example>

### 3.3. 単調性

<Proposition id="prop-deny-monotone" title="deny の単調性：規則を足しても安全側にしか動かない">
$R$ を規則集合とする。

1. $D$ を deny 規則だけからなる有限集合とし、$R' = R \cup D$ と置く。このとき $\mathrm{Den}(R) \subseteq \mathrm{Den}(R')$、$\mathrm{Ask}(R') \subseteq \mathrm{Ask}(R)$、$\mathrm{All}(R') \subseteq \mathrm{All}(R)$ である。
2. $A$ を allow 規則だけからなる有限集合とし、$R' = R \cup A$ と置く。このとき $\mathrm{Den}(R') = \mathrm{Den}(R)$ かつ $\mathrm{Ask}(R') = \mathrm{Ask}(R)$ である。特に、$R$ の deny 規則にマッチする呼び出しは、allow 規則をいくら足しても許可されない。$R$ の ask 規則にマッチする呼び出しも同じである。
3. $S$ を ask 規則だけからなる有限集合とし、$R' = R \cup S$ と置く。このとき $\mathrm{Den}(R') = \mathrm{Den}(R)$ かつ $\mathrm{All}(R') \subseteq \mathrm{All}(R)$ である。
</Proposition>

<Proof of="prop-deny-monotone">
$R \subseteq R'$ のとき、$\mathrm{Mat}_\kappa$ の定義は「条件を満たす規則が存在する」という形なので、$\mathrm{Mat}_\kappa(R) \subseteq \mathrm{Mat}_\kappa(R')$ です。また、足した規則の種類が $\kappa$ でなければ、種類 $\kappa$ の規則は $R$ と $R'$ で同じなので $\mathrm{Mat}_\kappa(R') = \mathrm{Mat}_\kappa(R)$ です。定義から
$$
\mathrm{Den} = \mathrm{Mat}_{\mathrm{deny}},\qquad
\mathrm{Ask} = \mathrm{Mat}_{\mathrm{ask}} \setminus \mathrm{Mat}_{\mathrm{deny}},\qquad
\mathrm{All} = \mathrm{Mat}_{\mathrm{allow}} \setminus (\mathrm{Mat}_{\mathrm{deny}} \cup \mathrm{Mat}_{\mathrm{ask}})
$$
です。

(1) $\mathrm{Mat}_{\mathrm{deny}}(R) \subseteq \mathrm{Mat}_{\mathrm{deny}}(R')$ で、$\mathrm{Mat}_{\mathrm{ask}}$ と $\mathrm{Mat}_{\mathrm{allow}}$ は変わりません。$\mathrm{Den}(R) = \mathrm{Mat}_{\mathrm{deny}}(R) \subseteq \mathrm{Mat}_{\mathrm{deny}}(R') = \mathrm{Den}(R')$ です。$\mathrm{Ask}$ と $\mathrm{All}$ は、同じ集合から、より大きい集合を引いたものになるので、小さくなるか等しいままです。

(2) $\mathrm{Mat}_{\mathrm{deny}}$ と $\mathrm{Mat}_{\mathrm{ask}}$ は変わらないので、$\mathrm{Den}$ と $\mathrm{Ask}$ は変わりません。$q$ が $R$ の deny 規則にマッチするなら $q \in \mathrm{Den}(R) = \mathrm{Den}(R')$ で、$\mathrm{dec}_{R'}(q) = \mathrm{deny} \ne \mathrm{allow}$ です。$q$ が $R$ の ask 規則にマッチするなら $q \in \mathrm{Mat}_{\mathrm{ask}}(R')$ で、$\mathrm{dec}_{R'}(q)$ は $\mathrm{deny}$ か $\mathrm{ask}$ のどちらかです。

(3) $\mathrm{Mat}_{\mathrm{deny}}$ は変わらないので $\mathrm{Den}$ は変わりません。$\mathrm{All}$ は、同じ $\mathrm{Mat}_{\mathrm{allow}}$ から、より大きい $\mathrm{Mat}_{\mathrm{deny}} \cup \mathrm{Mat}_{\mathrm{ask}}$ を引いたものなので、小さくなるか等しいままです。
</Proof>

<Corollary id="cor-deny-any-scope" title="どの階層の deny も他の階層の allow に勝つ">
階層 $\sigma_0 \in \Sigma$ の規則集合 $R^{\sigma_0}$ に deny 規則 $(\mathrm{deny}, \pi)$ があり、$q \models \pi$ ならば、他の階層の規則集合が何であっても $\mathrm{dec}_R(q) = \mathrm{deny}$ である（$R = \bigcup_\sigma R^\sigma$）。
</Corollary>

<Proof of="cor-deny-any-scope">
$(\mathrm{deny}, \pi) \in R^{\sigma_0} \subseteq R$ かつ $q \models \pi$ なので $q \in \mathrm{Mat}_{\mathrm{deny}}(R)$ です。<Ref to="def-permission-decision" /> の 1 つ目の場合から $\mathrm{dec}_R(q) = \mathrm{deny}$ です。他の階層の規則は、この議論に現れません。
</Proof>

<Example id="ex-project-allow-user-deny" title="プロジェクトの allow と利用者の deny">
あるリポジトリのプロジェクト設定に `allow: Bash(git push *)` があり、利用者の設定に `deny: Bash(git push *)` があるとします。合併した規則集合で `git push origin main` を判定すると、<Ref to="cor-deny-any-scope" /> から deny です。プロジェクトと利用者を入れ替えても（プロジェクトが deny、利用者が allow でも）deny です。リポジトリを clone した人が、そのリポジトリの設定によって、自分が禁じた操作を許可させられることはありません。逆に、組織の管理者が deny したものを、利用者が自分の設定で許可することもできません。
</Example>

<Remark id="rem-permission-model-assumptions" title="模型に入れていない実物の規則">
この節の模型は、公式ドキュメントが述べる評価順の骨格です。実物には、模型に入れていない規則があります（2026-10 時点の公式ドキュメントによる）。

- 裸のツール名の deny（`Bash` や `Bash(*)`）は、呼び出しを止めるだけでなく、そのツールの定義をモデルの文脈から取り除きます。範囲つきの deny（`Bash(rm *)`）はツールを残し、マッチした呼び出しを試みた時点で止めます。
- 一部のツールには、規則が及ばない例外があります（会話を終えるためのツールは、ほかのツールが残っている限り deny や ask で止められない、など）。
- サンドボックスを有効にして既定の設定のままにすると、サンドボックスの中で動く Bash コマンドは、裸の `Bash` の ask 規則があっても確認なしで実行されます（内容で絞った ask 規則と deny 規則は適用されます）。
- Bash の規則の照合の前に、決まったラッパー（`timeout`・`time`・`nice`・`nohup` など）が取り除かれます。
- プラグインの一種で、判定に介入できるもの（公式ドキュメントが mod と呼ぶもの）を導入している場合、規則と `PreToolUse` フックが決めたあとで mod が答えを返し、その答えが決定を置き換えることがあります。公式ドキュメントによれば、mod は、ask 規則が確認を求める呼び出しを承認でき、`PreToolUse` フックのブロックも（そのフックが管理者の設定にある場合を除いて）承認できます。deny 規則については、管理者の設定がある機械か、Team・Enterprise プランでサインインしている場合は、既定で deny 規則が mod より優先し（組織がこの既定を変えられます）、それ以外の環境では、deny 規則が拒否した呼び出しを mod が承認できます。

したがって、この章の命題（<Ref to="thm-permission-decision" />、<Ref to="prop-deny-monotone" />、<Ref to="cor-deny-any-scope" />、<Ref to="prop-compound-command" />、<Ref to="prop-mode-inclusion" />、<Ref to="prop-hook-composition" />）は「規則集合（とフックの決定・モード）だけで判定が決まる」範囲、すなわち mod を導入していない構成についての主張です。mod を導入している構成では、deny が必ず勝つという保証は上の条件つきになります。ほかの例外が関わる場面でも、出典の該当箇所を確かめる必要があります。
</Remark>

出典（確認日 2026-10-10）: [Configure permissions](https://code.claude.com/docs/en/permissions)（"Manage permissions"・"Permission rule syntax"・"Settings precedence"・"Extend permissions with hooks"・"How permissions interact with sandboxing" の節）。

## 4. 「狭い規則が優先」ではない理由

規則がぶつかったとき、より具体的な規則を優先する設計もありえます。ネットワークの経路表はその代表で、宛先にマッチするプレフィックスのうち最も長いものを使います（<Ref to="computer-science/software-engineering/networking-tcp-ip#thm-lpm" />）。プレフィックスどうしは互いに素か一方が他方を含むかのどちらかなので（<Ref to="computer-science/software-engineering/networking-tcp-ip#lem-laminar" />）、「最も狭いもの」が一意に決まり、広い規則に例外を重ねていく書き方ができます。権限規則でこれを採らないことで何が得られるかを、同じ土俵で比べます。

経路表を読んでいなくても、ここでの比較に必要なのは「パターンのうち、呼び出しに一番長く一致するものを採る」という規則だけです。

<Definition id="def-longest-match" title="最長一致方式（比較用）">
パターンを文字列 $p$ とし、呼び出し（コマンドの文字列）$c$ が $p$ にマッチすることを「$p$ が $c$ の接頭辞である」ことと定める。規則集合 $R$ に対する**最長一致方式**の判定 $\mathrm{lm}_R(c)$ を、$c$ にマッチする規則のうちパターンが最も長いものの種類と定める（マッチする規則が無ければ $\bot$）。同じパターンに 2 つの種類の規則は無いとする。
</Definition>

$c$ の接頭辞は長さごとに 1 つしかないので、マッチするパターンは長さがすべて異なり、最も長いものは一意に決まります。経路表の場合（<Ref to="computer-science/software-engineering/networking-tcp-ip#thm-lpm" />）と同じ事情です。

<Proposition id="prop-specificity-irrelevant" title="deny 優先と最長一致の違い">
接頭辞のパターンについて、deny 優先の判定 $\mathrm{dec}_R$（<Ref to="def-permission-decision" />）と最長一致の判定 $\mathrm{lm}_R$（<Ref to="def-longest-match" />）を比べる。

1. 2 つの判定が食い違う規則集合と呼び出しが存在する。
2. deny 優先では、$(\mathrm{deny}, p) \in R$ かつ $p$ が $c$ の接頭辞ならば、$R$ を含む任意の規則集合 $R'$ について $\mathrm{dec}_{R'}(c) = \mathrm{deny}$ である。
3. 最長一致では 2 が成り立たない。すなわち、$(\mathrm{deny}, p) \in R$、$p$ が $c$ の接頭辞、$R \subseteq R'$ で、$\mathrm{lm}_{R'}(c) = \mathrm{allow}$ となる例が存在する。
</Proposition>

<Proof of="prop-specificity-irrelevant">
(1) $R' = \{(\mathrm{deny}, \texttt{aws }),\ (\mathrm{allow}, \texttt{aws s3 ls})\}$、$c = \texttt{aws s3 ls}$ と置きます（1 つ目のパターンは末尾に空白 1 つを含む 4 文字です）。$c$ は両方のパターンを接頭辞に持ちます。deny の規則にマッチするので $\mathrm{dec}_{R'}(c) = \mathrm{deny}$ です。マッチするパターンの長さは 4 と 9 で、長いほうは allow の規則なので $\mathrm{lm}_{R'}(c) = \mathrm{allow}$ です。

(2) $(\mathrm{deny}, p) \in R \subseteq R'$ かつ $c \models p$ なので $c \in \mathrm{Mat}_{\mathrm{deny}}(R')$ で、<Ref to="def-permission-decision" /> から $\mathrm{dec}_{R'}(c) = \mathrm{deny}$ です。

(3) $R = \{(\mathrm{deny}, \texttt{aws })\}$ と置き、$R'$ と $c$ を 1 のものとします。$R \subseteq R'$ で、$(\mathrm{deny}, \texttt{aws }) \in R$ のパターンは $c$ の接頭辞ですが、1 で見たとおり $\mathrm{lm}_{R'}(c) = \mathrm{allow}$ です。
</Proof>

2 は、deny 規則が**ほかの規則から独立に読める**ことを言っています。設定のどこかに `deny: Bash(aws *)` と書いてあれば、ほかにどんな規則が足されても、`aws` で始まるコマンドは実行されません。最長一致では、deny 規則 1 つを見ただけでは何も言えず、それより長い allow が設定のどこか（別の階層のファイルかもしれません）に無いことを確かめる必要があります。経路表では、例外を重ねられることが利点でした。禁止を表す規則では、例外を重ねられないことが利点になります。代償は <Ref to="ex-aws-deny" /> で見たとおり、「広く禁じて一部だけ通す」が書けないことです。

出典（確認日 2026-10-10）: [Configure permissions](https://code.claude.com/docs/en/permissions)（"Manage permissions" の節。規則の細かさが順序を変えないこと、allow で deny の例外を作れないこと）。

## 5. 複合コマンド

シェルでは、`a && b` や `a | b` のように、区切りで複数のコマンドをつなげます。規則を文字列全体に対して照合すると、「`npm test` で始まるコマンドを許す」規則が、`npm test && rm -rf build` も許してしまいます。公式ドキュメントは、Claude Code がシェルの演算子を認識し、`Bash(safe-cmd *)` のような規則は `safe-cmd && other-cmd` を実行する許可を与えない、と述べています。認識する区切りは `&&`・`||`・`;`・`|`・`|&`・`&` と改行で、規則は各サブコマンドに独立にマッチする必要があります。deny と ask の規則は、どれか 1 つのサブコマンドがマッチすれば適用されます（サブシェルやコマンド置換の中のコマンドも含む、とされています）。

<Definition id="def-compound-command" title="複合コマンドとその判定">
コマンドの文字列 $c$ を区切りで分けて得られる、空でないサブコマンドの列を $\mathrm{sub}(c) = (c_1, \ldots, c_m)$（$m \ge 1$）とする。区切りを含まない $c$ では $\mathrm{sub}(c) = (c)$ であり、各 $c_i$ は区切りを含まない（したがって $\mathrm{sub}(c_i) = (c_i)$）とする。Bash の規則集合 $R$ に対する**複合コマンドの判定** $\mathrm{dec}^\ast_R(c)$ を次で定める。

- ある $i$ で $c_i \in \mathrm{Mat}_{\mathrm{deny}}(R)$ ならば $\mathrm{deny}$。
- そうでなく、ある $i$ で $c_i \in \mathrm{Mat}_{\mathrm{ask}}(R)$ ならば $\mathrm{ask}$。
- そうでなく、すべての $i$ で $c_i \in \mathrm{Mat}_{\mathrm{allow}}(R)$ ならば $\mathrm{allow}$。
- それ以外は $\bot$。
</Definition>

ここで $c_i \in \mathrm{Mat}_\kappa(R)$ は、呼び出し $(\texttt{Bash}, c_i)$ についての略記です。$m = 1$ のとき $\mathrm{dec}^\ast_R(c) = \mathrm{dec}_R(c)$ で、<Ref to="def-permission-decision" /> に一致します。deny と ask は「どれか 1 つ」、allow は「すべて」という非対称が要点です。

<Figure caption="規則が allow の `npm test *` だけのとき、複合コマンドは区切りで分けて照合される。すべてのサブコマンドが allow にマッチしないと、全体は allow にならない。">
<svg viewBox="0 0 680 230" width="100%" role="img" aria-label="npm test と rm -rf build を区切りで分け、前者は allow にマッチし後者はどの規則にもマッチしないため、全体は allow にならないことを示す図">
  <text x="340" y="26" font-size="14" text-anchor="middle" fill="currentColor">npm test &amp;&amp; rm -rf build</text>
  <line x1="300" y1="36" x2="190" y2="66" stroke="currentColor" stroke-width="1.5" />
  <line x1="380" y1="36" x2="490" y2="66" stroke="currentColor" stroke-width="1.5" />
  <text x="340" y="56" font-size="11" text-anchor="middle" fill="currentColor">区切り &amp;&amp; で分ける</text>
  <rect x="90" y="70" width="200" height="40" fill="none" stroke="currentColor" stroke-width="1.5" />
  <text x="190" y="95" font-size="13" text-anchor="middle" fill="currentColor">npm test</text>
  <rect x="390" y="70" width="200" height="40" fill="none" stroke="var(--sl-color-accent)" stroke-width="2" stroke-dasharray="5 4" />
  <text x="490" y="95" font-size="13" text-anchor="middle" fill="currentColor">rm -rf build</text>
  <line x1="190" y1="110" x2="190" y2="140" stroke="currentColor" stroke-width="1.5" />
  <line x1="490" y1="110" x2="490" y2="140" stroke="var(--sl-color-accent)" stroke-width="1.5" stroke-dasharray="5 4" />
  <text x="190" y="158" font-size="12" text-anchor="middle" fill="currentColor">allow にマッチ</text>
  <text x="490" y="158" font-size="12" text-anchor="middle" fill="currentColor">どの規則にもマッチしない</text>
  <text x="340" y="205" font-size="13" text-anchor="middle" fill="currentColor">片方が allow でないので、全体は allow にならない</text>
</svg>
</Figure>

<Proposition id="prop-compound-command" title="許可される複合コマンドのサブコマンドは単独でも許可される">
<Ref to="def-compound-command" /> の判定について次が成り立つ。

1. $\mathrm{dec}^\ast_R(c) = \mathrm{allow}$ ならば、$\mathrm{sub}(c)$ のすべてのサブコマンド $c_i$ について $\mathrm{dec}^\ast_R(c_i) = \mathrm{allow}$ である。
2. $R$ の allow 規則が $(\mathrm{allow}, \pi)$ だけで、$c' \not\models \pi$ とする。このとき、$c \models \pi$ であるどんな $c$ についても、$\mathrm{sub}(c \mathbin{\texttt{\&\&}} c')$ が $c'$ を含むならば $\mathrm{dec}^\ast_R(c \mathbin{\texttt{\&\&}} c') \ne \mathrm{allow}$ である。
3. 文字列全体を照合する素朴な方式（$\mathrm{dec}_R$ をそのまま複合コマンドの文字列に適用する方式）では、1 が成り立たない例が存在する。
</Proposition>

<Proof of="prop-compound-command">
(1) $\mathrm{dec}^\ast_R(c) = \mathrm{allow}$ とします。定義の 3 つ目の場合が成り立ち、1 つ目と 2 つ目は成り立たないので、すべての $i$ について $c_i \notin \mathrm{Mat}_{\mathrm{deny}}(R)$、$c_i \notin \mathrm{Mat}_{\mathrm{ask}}(R)$、$c_i \in \mathrm{Mat}_{\mathrm{allow}}(R)$ です。$\mathrm{sub}(c_i) = (c_i)$ なので、$\mathrm{dec}^\ast_R(c_i)$ の定義で見るサブコマンドは $c_i$ だけで、1 つ目と 2 つ目の場合は成り立たず、3 つ目が成り立ちます。したがって $\mathrm{dec}^\ast_R(c_i) = \mathrm{allow}$ です。

(2) $c'$ は $\mathrm{sub}(c \mathbin{\texttt{\&\&}} c')$ に現れます。$R$ の allow 規則のパターンは $\pi$ だけで $c' \not\models \pi$ なので、$c' \notin \mathrm{Mat}_{\mathrm{allow}}(R)$ です。定義の 3 つ目の場合（すべてのサブコマンドが allow にマッチ）は成り立たないので、判定は $\mathrm{allow}$ ではありません。

(3) $R = \{(\mathrm{allow}, \texttt{Bash(npm test *)})\}$、$c = \texttt{npm test \&\& rm -rf build}$ と置きます。`*` は任意の文字列に当たるので、文字列全体は `npm test *` にマッチし、素朴な方式の判定は $\mathrm{allow}$ です。しかしサブコマンド `rm -rf build` は `npm test *` にマッチせず、単独での判定は $\bot$ で、$\mathrm{allow}$ ではありません。
</Proof>

1 は、複合コマンドの許可が、部品の許可に**分解できる**ことを言っています。許可された複合コマンドを実行して起こることは、許可された単独のコマンドを順に実行して起こることの範囲に収まります。規則を書く人は、単独のコマンドについてだけ考えれば済みます。

公式ドキュメントには、この分割に関する細かい規則がいくつかあります。`&&` や `||` のあとに何も無いコマンド（`npm test &&`）は解析できないものとして扱い、allow 規則のためのサブコマンドへの分割をしないこと。複合コマンドを「今後確認しない」で承認すると、文字列全体ではなく、承認が必要なサブコマンドごとに規則が保存されること。解析しきれないコマンドは、読み取り専用として扱わずに確認を求めること、などです。どれも、迷ったら許可しない側に倒す選択です。

出典（確認日 2026-10-10）: [Configure permissions](https://code.claude.com/docs/en/permissions)（"Compound commands"・"Read-only commands" の節）、[Security](https://code.claude.com/docs/en/security)（解析しきれないコマンドの前に確認を求めること。Manual モードについての記述）。

## 6. 権限モード

規則にマッチしない呼び出し（判定が $\bot$）をどうするかは、**権限モード**が決めます。公式ドキュメントのモードの表（2026-10 時点）は次のとおりです。

| モード（設定値） | 確認なしで実行されるもの |
|---|---|
| `default`（画面上の名前は Manual） | 読み取りだけ |
| `acceptEdits` | 読み取り、ファイルの編集、一般的なファイルシステムのコマンド（`mkdir`・`touch`・`mv`・`cp` など） |
| `plan` | 読み取り（auto モードが使えるときは、分類器が認めたコマンドも） |
| `auto` | すべて（背後で安全性の検査が行われる） |
| `dontAsk` | 読み取りと、事前に許可したツール。確認が要るものは拒否する |
| `bypassPermissions` | すべて |

同じページは、モードは基準を決めるもので、その上に権限規則を重ねること、**deny 規則は `bypassPermissions` を含むすべてのモードで止める**こと、allow 規則は `bypassPermissions` では効果を持たないことを述べています。どのモードで始まるかは版と画面で違います（v2.1.283 以降は、対話的なターミナルと VS Code のセッションで auto が組み込みの開始モード、とされています）。

Agent SDK のドキュメントは、呼び出しの評価の順序を、フック → deny 規則 → ask 規則 → 権限モード → allow 規則 → 承認のコールバック（対話的な画面では確認の表示）と説明しています。フックを除いた部分を、表のうち 3 つのモードについて定式化します。

次の定義の 5 段は、順に「deny にマッチすれば止める」「ask にマッチすれば確認する」「モードが自動で承認する範囲なら実行する」「allow にマッチすれば実行する」「それ以外は確認する」です。上の表の「確認なしで実行されるもの」の列が、3 段目の $\mathrm{Auto}(\mu)$ にあたります。

<Definition id="def-permission-mode" title="権限モードつきの判定（3 つのモード）">
$\mathrm{Rd} \subseteq Q$ を読み取りの呼び出しの集合、$\mathrm{Ed} \subseteq Q$ をファイルの編集と一般的なファイルシステムのコマンドの呼び出しの集合とする。モード $\mu \in \{\mathtt{default}, \mathtt{acceptEdits}, \mathtt{bypassPermissions}\}$ に対して、**モードが自動で承認する集合**を
$$
\mathrm{Auto}(\mathtt{default}) = \mathrm{Rd},\qquad
\mathrm{Auto}(\mathtt{acceptEdits}) = \mathrm{Rd} \cup \mathrm{Ed},\qquad
\mathrm{Auto}(\mathtt{bypassPermissions}) = Q
$$
と定める。規則集合 $R$ とモード $\mu$ に対する**最終の判定** $D_{\mu, R} : Q \to \{\mathrm{block}, \mathrm{prompt}, \mathrm{run}\}$ を、次の順に調べて最初に当てはまるもので定める。

1. $q \in \mathrm{Mat}_{\mathrm{deny}}(R)$ ならば $\mathrm{block}$（実行しない）。
2. $q \in \mathrm{Mat}_{\mathrm{ask}}(R)$ ならば $\mathrm{prompt}$（利用者に確認する）。
3. $q \in \mathrm{Auto}(\mu)$ ならば $\mathrm{run}$（確認なしで実行する）。
4. $q \in \mathrm{Mat}_{\mathrm{allow}}(R)$ ならば $\mathrm{run}$。
5. それ以外は $\mathrm{prompt}$。
</Definition>

<Proposition id="prop-mode-inclusion" title="モードの包含と、deny がどのモードでも効くこと">
<Ref to="def-permission-mode" /> の判定について、$\mathrm{Run}_\mu(R) = \{q \mid D_{\mu,R}(q) = \mathrm{run}\}$ と置く。

1. $\mathrm{Auto}(\mathtt{default}) \subseteq \mathrm{Auto}(\mathtt{acceptEdits}) \subseteq \mathrm{Auto}(\mathtt{bypassPermissions})$ であり、任意の規則集合 $R$ について $\mathrm{Run}_{\mathtt{default}}(R) \subseteq \mathrm{Run}_{\mathtt{acceptEdits}}(R) \subseteq \mathrm{Run}_{\mathtt{bypassPermissions}}(R)$ である。
2. どのモード $\mu$ でも、$D_{\mu,R}(q) \ne \mathrm{block}$ である呼び出し（確認の有無を問わず、実行されうる呼び出し）の集合は $Q \setminus \mathrm{Mat}_{\mathrm{deny}}(R)$ に等しい。この主張は、3 の $\mathrm{Auto}(\mu)$ がどんな集合であっても成り立つ。
3. $\mathrm{Run}_{\mathtt{bypassPermissions}}(R) = Q \setminus (\mathrm{Mat}_{\mathrm{deny}}(R) \cup \mathrm{Mat}_{\mathrm{ask}}(R))$ で、$R$ の allow 規則に依らない。
</Proposition>

<Proof of="prop-mode-inclusion">
(1) 包含 $\mathrm{Rd} \subseteq \mathrm{Rd} \cup \mathrm{Ed} \subseteq Q$ は集合の定義から成り立ちます。$\mathrm{Run}_\mu(R)$ は、定義の順序から、「1 と 2 に当てはまらず、3 または 4 に当てはまる」呼び出しの集合で、
$$
\mathrm{Run}_\mu(R) = \bigl(\mathrm{Auto}(\mu) \cup \mathrm{Mat}_{\mathrm{allow}}(R)\bigr) \setminus \bigl(\mathrm{Mat}_{\mathrm{deny}}(R) \cup \mathrm{Mat}_{\mathrm{ask}}(R)\bigr)
$$
です。右辺は $\mathrm{Auto}(\mu)$ について包含を保つ（$\mathrm{Auto}(\mu) \subseteq \mathrm{Auto}(\mu')$ ならば、和をとり同じ集合を引いたあとも包含が成り立つ）ので、主張が従います。

(2) $D_{\mu,R}(q) = \mathrm{block}$ になるのは 1 の場合だけで、その条件は $q \in \mathrm{Mat}_{\mathrm{deny}}(R)$ です。1 は最初に調べられるので、$\mathrm{Auto}(\mu)$ が何であっても、$q \in \mathrm{Mat}_{\mathrm{deny}}(R)$ なら $\mathrm{block}$、そうでなければ 2〜5 のどれかで $\mathrm{prompt}$ か $\mathrm{run}$ です。

(3) 1 の証明の式に $\mathrm{Auto}(\mathtt{bypassPermissions}) = Q$ を入れると、$Q \cup \mathrm{Mat}_{\mathrm{allow}}(R) = Q$ なので、$\mathrm{Run} = Q \setminus (\mathrm{Mat}_{\mathrm{deny}}(R) \cup \mathrm{Mat}_{\mathrm{ask}}(R))$ です。右辺に $\mathrm{Mat}_{\mathrm{allow}}(R)$ は現れません。
</Proof>

3 は、公式ドキュメントの「allow 規則は `bypassPermissions` では効果を持たない」に当たります。Agent SDK のドキュメントはこれを警告として書いています。許可するツールを並べる設定（`allowed_tools`）は `bypassPermissions` を制限しません。並べていないツールはモードの段で承認されるからです。このモードで特定のツールを止めたければ、deny の側（`disallowed_tools`）に書く必要があります。

<Remark id="rem-modes-not-claimed" title="この命題が主張していないこと">
<Ref to="prop-mode-inclusion" /> は、表から包含が読み取れる 3 つのモードについての主張です。`plan`・`auto`・`dontAsk` がこの列のどこに入るかは主張しません。`plan` ではファイルの編集が allow 規則にマッチしても自動では承認されず、`dontAsk` では確認が要る呼び出しが拒否に変わり、`auto` では分類器の判断が入るので、<Ref to="def-permission-mode" /> の 5 段の形に収まらないからです。また、実物には、どのモードでも自動では承認されない行為の一覧（明示の ask 規則にマッチするツール、利用者の操作を必要とするツール、重要なパスを対象とする `rm`・`rmdir` など）と、保護されたパスへの書き込みの扱いがあり、模型には入れていません。
</Remark>

**auto モードについて公開されていること。** auto モードでは、利用者の代わりに別のモデル（分類器）が行為を見ます。公式ドキュメントによれば、判定の順序は固定で、最初に当てはまった段で決まります。(1) allow・ask・deny の規則にマッチする行為は、列挙された例外を除いて、その場で決まる。(2) 読み取り専用の行為と、作業ディレクトリの中のファイルの編集は、例外を除いて自動で承認される。(3) それ以外は分類器に送られる。分類器が 3 回続けて、または合計 20 回ブロックすると、auto モードは一時停止して確認に戻ります。この 2 つのしきい値は設定できない、と明記されています。auto モードに入るとき、任意のコードの実行を許す広い allow 規則（`Bash(*)` や、インタプリタにワイルドカードを付けた規則など）は外されます。Claude Code 自身が送る分類器の要求では、分類器は利用者のメッセージ、読み取り専用の参照を除くツール呼び出し、CLAUDE.md の内容を見て、ツール結果は取り除かれる、と説明されています。分類器がどのモデルか、判定の基準の詳細、誤りの率は、今回確認した公式ドキュメントの範囲では確認できませんでした。この記事では書きません。

出典（確認日 2026-10-10）: [Choose a permission mode](https://code.claude.com/docs/en/permission-modes)（"Available modes"・"When auto mode falls back"・"How auto mode evaluates actions" の節）、[Configure permissions（Agent SDK）](https://code.claude.com/docs/en/agent-sdk/permissions)（"How permissions are evaluated" の節）。

## 7. フックによる介入

フックは、ループの決まった時点で Claude Code が実行する、利用者が登録した処理です（仕組みの全体は [拡張の仕組み](/computer-science/claude-code-internals/extension-mechanisms) で扱います）。ツール実行の前に動く `PreToolUse` フックは、関門に介入できます。公式ドキュメントが述べる規則は次のとおりです。

- フックは、権限の確認の表示より前に実行される。終了コード 2 で終わったフックは、**権限規則の評価の前に**ツール呼び出しを止める。したがって allow 規則があっても止まる。
- フックは JSON の出力で決定（`permissionDecision`）を返せる。値は `allow`（確認を飛ばす）、`deny`（呼び出しを止める）、`ask`（利用者に確認する）、`defer`（あとで再開できるように終了する）。
- **フックの決定は権限規則を迂回しない。** フックが `allow` を返しても、マッチする deny 規則は呼び出しを止め、マッチする ask 規則は確認を出す。
- 複数の `PreToolUse` フックが異なる決定を返したときの優先順は、`deny`、`defer`、`ask`、`allow` の順である。
- 何も出力せずに終了コード 0 で終わったフックは、決定を報告しなかったものとして扱われ、呼び出しは通常の権限の流れを進む。黙っていることは承認にならない。

<Definition id="def-hook-composition" title="フックの決定と権限規則の合成">
フックの決定の集合を $H = \{\mathrm{deny}, \mathrm{ask}, \mathrm{allow}, \bot\}$ とし、順序 $\mathrm{deny} \succ \mathrm{ask} \succ \mathrm{allow} \succ \bot$ を入れる（$\bot$ は決定なし。終了コード 2 による中止は $\mathrm{deny}$ に含める。`defer` は扱わない）。呼び出し $q$ に対して複数のフックが決定 $\eta_1, \ldots, \eta_k$ を返したとき、**合成したフックの決定**を、この順序での最大元 $\eta = \max(\eta_1, \ldots, \eta_k)$ とする（フックが無ければ $\bot$）。フックの決定 $\eta$、規則集合 $R$、モード $\mu$ に対する判定 $C_{\mu,R}(\eta, q)$ を、次の順に調べて最初に当てはまるもので定める。

1. $\eta = \mathrm{deny}$ ならば $\mathrm{block}$。
2. $q \in \mathrm{Mat}_{\mathrm{deny}}(R)$ ならば $\mathrm{block}$。
3. $q \in \mathrm{Mat}_{\mathrm{ask}}(R)$ ならば $\mathrm{prompt}$。
4. $\eta = \mathrm{ask}$ ならば $\mathrm{prompt}$。
5. $\eta = \mathrm{allow}$ ならば $\mathrm{run}$。
6. それ以外（$\eta = \bot$）は $D_{\mu,R}(q)$（<Ref to="def-permission-mode" />）。
</Definition>

<Proposition id="prop-hook-composition" title="フックを合成しても deny と ask の規則は保たれる">
<Ref to="def-hook-composition" /> の判定について次が成り立つ。

1. どんなフックの決定 $\eta$ に対しても、$q \in \mathrm{Mat}_{\mathrm{deny}}(R)$ ならば $C_{\mu,R}(\eta, q) = \mathrm{block}$ であり、$q \in \mathrm{Mat}_{\mathrm{ask}}(R)$ ならば $C_{\mu,R}(\eta, q) \ne \mathrm{run}$ である。
2. $\eta = \mathrm{deny}$ ならば、$R$ の allow 規則とモードに関係なく $C_{\mu,R}(\eta, q) = \mathrm{block}$ である。
3. フックを 1 つ足すと、合成したフックの決定は順序 $\succ$ について下がらない。足したフックの決定が $\mathrm{deny}$ か $\bot$ のどちらかであれば、足したあとに $\mathrm{run}$ になる呼び出しは、足す前にも $\mathrm{run}$ である。
4. <Ref to="prop-deny-monotone" /> の 1 と 2 に当たる単調性が保たれる。すなわち、フックの決定を固定したとき、deny 規則を足しても $\mathrm{run}$ になる呼び出しは増えず、allow 規則を足しても、$R$ の deny 規則か ask 規則にマッチする呼び出しは $\mathrm{run}$ にならない。
</Proposition>

<Proof of="prop-hook-composition">
(1) $q \in \mathrm{Mat}_{\mathrm{deny}}(R)$ とします。$\eta = \mathrm{deny}$ なら 1 で、そうでなければ 2 で $\mathrm{block}$ です。$q \in \mathrm{Mat}_{\mathrm{ask}}(R)$ とします。1 か 2 に当てはまれば $\mathrm{block}$、どちらにも当てはまらなければ 3 で $\mathrm{prompt}$ です。どの場合も $\mathrm{run}$ ではありません。

(2) 1 番目の場合が最初に調べられ、当てはまります。$R$ と $\mu$ は 2 番目以降にしか現れません。

(3) 最大元をとる集合に元を足しても最大元は下がりません。足したフックの決定を $\eta'$、足す前の合成を $\eta$、足したあとを $\eta^+ = \max(\eta, \eta')$ とします。$\eta' = \bot$ なら $\eta^+ = \eta$ で判定は変わりません。$\eta' = \mathrm{deny}$ なら $\eta^+ = \mathrm{deny}$ で、判定は 1 から $\mathrm{block}$ になり、$\mathrm{run}$ になる呼び出しはありません。どちらの場合も、足したあとに $\mathrm{run}$ である呼び出しは足す前にも $\mathrm{run}$ です。

(4) deny 規則を足して $R' = R \cup D$ とします。$C_{\mu,R'}(\eta, q) = \mathrm{run}$ とすると、5 か 6 で決まっています。5 で決まる場合、1〜4 に当てはまらないので $\eta = \mathrm{allow}$ かつ $q \notin \mathrm{Mat}_{\mathrm{deny}}(R') \cup \mathrm{Mat}_{\mathrm{ask}}(R')$ です。$\mathrm{Mat}_{\mathrm{deny}}(R) \subseteq \mathrm{Mat}_{\mathrm{deny}}(R')$、$\mathrm{Mat}_{\mathrm{ask}}(R) = \mathrm{Mat}_{\mathrm{ask}}(R')$ なので、$R$ でも 1〜4 に当てはまらず、5 で $\mathrm{run}$ です。6 で決まる場合、$\eta = \bot$ で $D_{\mu,R'}(q) = \mathrm{run}$ です。<Ref to="prop-mode-inclusion" /> の証明の式から $\mathrm{Run}_\mu(R') \subseteq \mathrm{Run}_\mu(R)$（引く集合が大きくなるだけ）なので、$D_{\mu,R}(q) = \mathrm{run}$ です。allow 規則を足す場合は、$\mathrm{Mat}_{\mathrm{deny}}$ と $\mathrm{Mat}_{\mathrm{ask}}$ が変わらないので、1 の主張がそのまま $R'$ について成り立ちます。
</Proof>

合成の表を、決定の組ごとに書くと次のようになります（規則の列は、$q$ がマッチする最も強い種類）。

| フック ＼ 規則 | deny にマッチ | ask にマッチ | allow だけにマッチ | どれにもマッチしない |
|---|---|---|---|---|
| deny（または終了コード 2） | block | block | block | block |
| ask | block | prompt | prompt | prompt |
| allow | block | prompt | run | run |
| 決定なし | block | prompt | run | モードの基準 |

フックと規則は、**厳しくする方向には互いに独立に効き、緩める方向には規則の deny と ask を越えられない**、という非対称になっています。フックの `allow` が効くのは右下の 2 列だけです。公式ドキュメントは、これが deny 優先の順序を、管理者の設定の deny を含めて保つためのものだと説明しています。なお、この模型は 2 点で実物より単純です。1 つ目に、フックの `allow` にも、どのモードでも自動では承認されない行為（<Ref to="rem-modes-not-claimed" />。重要なパスを対象とする `rm`・`rmdir`、利用者の操作を必要とするツールなど）は承認できない、という例外があります。2 つ目に、<Ref to="rem-permission-model-assumptions" /> で触れた、判定に介入するプラグイン（mod）を導入している場合は、mod の答えがフックのブロックや規則の判定を条件つきで置き換えうるので、<Ref to="prop-hook-composition" /> の 1 と 2 は mod を導入していない構成についての主張です。複数のフックの決定の優先順（`deny`・`defer`・`ask`・`allow`）のうち、模型は `defer` を除いた 3 つと「決定なし」を扱っています。

出典（確認日 2026-10-10）: [Configure permissions](https://code.claude.com/docs/en/permissions)（"Extend permissions with hooks" の節）、[Hooks reference](https://code.claude.com/docs/en/hooks)（終了コードの節、`PreToolUse` の決定の節）。

## 8. サンドボックス：OS が強制する別の層

ここまでの関門は、すべて**実行の前**に、**コマンドの文字列**を見て判断するものでした。サンドボックスは種類の違う仕組みです。公式ドキュメントによれば、Bash サンドボックスは、Claude が実行するシェルコマンドの周りに OS が強制する境界で、どのファイルとネットワークのドメインに届くかを制限します。制限は Bash・PowerShell・Monitor のコマンドと、それらが起動するプロセスに適用されます。2026-10 時点の公式ドキュメントが述べる事実を整理します。

- 既定では無効で、`/sandbox` か設定の `sandbox.enabled` で有効にする。
- macOS では Seatbelt、Linux と WSL2 では bubblewrap（と socat）を使う（どれも、プロセスがさわれる範囲を絞る仕組み）。ネイティブの Windows ではコマンドはサンドボックスなしで実行される。土台は公開されているパッケージ `@anthropic-ai/sandbox-runtime` である。
- 既定で書き込めるのは、作業ディレクトリ、利用者ごとの一時ディレクトリ、追加したディレクトリ。読み取りは、既定では機械のほとんど（`~/.ssh` のような資格情報のファイルを含む）に届く。
- ネットワークは、外への直接の経路が無く、利用者の機械の上のプロキシを通る。プロキシは接続先のホスト名を許可ドメインと照合し、許可ドメインは空から始まる。
- **許可ドメインに無いホストへの接続がどうなるかは、既定では権限モードで決まる。** 対話的なセッションでは、`bypassPermissions` モードでは確認なしで許可され、Manual（既定）・`acceptEdits` モードでは利用者に確認が出て（承認するとそのホストはセッションの終わりまで、選び方によっては以後のセッションでも許可される）、auto モードではコマンドがそのホストを明示して分類器が認めた場合を除いて拒否され、`dontAsk` モードでは拒否される。`strictAllowlist`、または組織の管理設定の `allowManagedDomainsOnly` を有効にすると、どの権限モードでもプロキシが接続を拒否する。拒否ドメイン（`deniedDomains`）のホストへの接続も、どのモードでも拒否される。
- **サンドボックスが覆うのはシェルコマンドだけ**である。組み込みのファイル用・ウェブ用のツール（`Read`・`Edit`・`Write`・`WebFetch` など）、コマンドのフック、ローカルの MCP サーバなどは、サンドボックスの外で動く。
- シェルコマンドでも、除外の設定（`excludedCommands`）にマッチするものと、サンドボックスの中で失敗したあとにモデルがサンドボックスなしでの再実行を求めたもの（承認の扱いは権限モードと規則で決まる）は、外で動く。後者は設定で無効にできる。

ここまでの層を整理します。規則は、実行の前にコマンドの文字列を見て、通す・確認する・止めるを決めます。権限モードは、規則で決まらなかった呼び出しの既定の扱いを決めます。サンドボックスは、実行中のシェルコマンドの操作を OS が止めます。ただし、サンドボックスが覆うのはシェルコマンドだけで、接続先の扱いは既定では権限モードに依ります。

プロセスがファイルに書き込んだりネットワークに接続したりするには、システムコール（<Ref to="computer-science/cs-basics/operating-systems#def-syscall" />）でカーネルに頼むしかなく、ユーザモードのプロセス（<Ref to="computer-science/cs-basics/operating-systems#def-privilege-mode" />、<Ref to="computer-science/cs-basics/operating-systems#def-process" />）はその検査を飛ばせません。サンドボックスは、この検査の段に制限を置きます。コンテナ（<Ref to="computer-science/software-engineering/containers-and-kubernetes#def-container" />）が名前空間でプロセスから見える範囲を区切るのと同じ系統の仕組みで、Linux の bubblewrap は実際に名前空間を使います。

<Definition id="def-sandbox" title="サンドボックスの模型">
パスの集合を $\mathrm{Path}$、ホストの集合を $\mathrm{Host}$ とする。**操作**は、書き込み $\mathsf{w}(p)$（$p \in \mathrm{Path}$）か接続 $\mathsf{c}(h)$（$h \in \mathrm{Host}$）である。**サンドボックスの設定**は、書き込めるパスの集合 $P \subseteq \mathrm{Path}$ と接続できるホストの集合 $H \subseteq \mathrm{Host}$ の組 $S = (P, H)$ で、**許可された操作**の集合を $\mathrm{Perm}(S) = \{\mathsf{w}(p) \mid p \in P\} \cup \{\mathsf{c}(h) \mid h \in H\}$ とする。

シェルコマンド $c$ を設定 $S$ のもとで実行すると、$c$ とその子プロセスが試みる操作のうち、$\mathrm{Perm}(S)$ に属するものだけが効果を持ち、属さないものは失敗する（OS による強制）。効果を持った操作の集合を、$c$ の**効果** $\mathrm{Eff}_S(c)$ という。サンドボックスなしの実行では、試みた操作がすべて効果を持つ。その効果を $\mathrm{Eff}_{\varnothing}(c)$ と書く。
</Definition>

「許可されない操作は失敗する」は、模型の仮定です。実物でこれを保証しているのは OS の仕組みで、公式ドキュメントはその限界も挙げています（この節の末尾）。

この模型は、$S = (P, H)$ を実行の間ずっと固定された集合として扱います。書き込みについては、これは実物の規則（許可されていないパスへの書き込みは OS が失敗させる）に対応します。接続については、上の箇条書きのとおり、実物の既定の設定では $H$ の外のホストへの接続が、確認に対する利用者の承認や権限モード（`bypassPermissions`）によって許されます。したがって、接続について「$H$ は固定」という仮定が実物に当てはまるのは、許可ドメインを厳格に適用する設定（`strictAllowlist` か `allowManagedDomainsOnly`）を有効にした場合です。既定の設定では、$H$ を「許可ドメインに、権限の層があとから認めたホストを加えた集合」と読む必要があり、その集合は権限の判定に依ります。

<Proposition id="prop-sandbox-independent-layer" title="サンドボックスは権限の判定から独立な層である">
<Ref to="def-sandbox" /> の模型について次が成り立つ。ここで設定 $S = (P, H)$ は実行の間固定されていて、$H$ の外のホストへの接続が利用者の承認や権限モードによってあとから許されることは無いとする（模型の仮定。実物でこれにあたる設定は定義の直後の説明を参照）。

1. 設定 $S$ のもとで実行されたシェルコマンドの効果は、$\mathrm{Eff}_S(c) \subseteq \mathrm{Perm}(S)$ を満たす。これは、関門の判定がどんな写像であっても（すべてを許可する写像であっても）、モデルが何を出力しても成り立つ。したがって、1 回のループの実行の中でサンドボックス下で実行されたシェルコマンドの効果の合併も $\mathrm{Perm}(S)$ に含まれる。
2. サンドボックスの外で実行される呼び出しには 1 が及ばない。すなわち、$p \notin P$ であるパスについて、関門が許可すれば $\mathsf{w}(p)$ が効果を持つ呼び出しが存在する。
</Proposition>

<Proof of="prop-sandbox-independent-layer">
(1) <Ref to="def-sandbox" /> から、$\mathrm{Eff}_S(c)$ は「試みた操作のうち $\mathrm{Perm}(S)$ に属するもの」の集合なので、$\mathrm{Perm}(S)$ の部分集合です。この定義に、関門の判定もモデルの出力も現れません。関門は「$c$ を実行するかどうか」を決めるだけで、実行された $c$ の効果は $c$ と $S$ で決まります。実行されたコマンドを $c_1, \ldots, c_N$ とすると、$\bigcup_{i} \mathrm{Eff}_S(c_i)$ は $\mathrm{Perm}(S)$ の部分集合の合併なので、$\mathrm{Perm}(S)$ に含まれます。

(2) 3 つの例を挙げます。どれも $p \notin P$ とします。(a) ファイルに書き込む組み込みツールの呼び出し $q = (\texttt{Write}, p)$。このツールはシェルコマンドではないのでサンドボックスの外で動き、関門が $q$ を許可すれば $\mathsf{w}(p)$ が効果を持ちます。(b) 除外の設定にマッチするシェルコマンド $c$ で、$p$ への書き込みを試みるもの。サンドボックスなしで実行されるので、効果は $\mathrm{Eff}_{\varnothing}(c) \ni \mathsf{w}(p)$ です。(c) サンドボックスなしでの再実行が許可されたシェルコマンド。(b) と同じです。どの場合も、効果は $\mathrm{Perm}(S)$ に含まれません。
</Proof>

1 が言っているのは、2 つの層の**失敗が独立である**ということです。権限の判定は、コマンドの文字列（と、auto モードでは分類器の判断）に基づいて、実行の前に行われます。文字列から効果を読み違えることがありえます（次節）。サンドボックスは、実行中のプロセスに対して OS が強制するので、モデルが何を実行しようとしたか、許可されたコマンドが名前から想像される以上のことをするかに関係なく、境界が保たれます。ただし、接続の境界がどこにあるか（許可ドメインの外のホストを通すかどうか）は、既定の設定では権限モードと利用者の承認が決めるので、ネットワークについてこの独立性が文字どおり成り立つのは、命題の仮定にあたる厳格な設定のもとです。公式ドキュメントは、この 2 つを相補的な層と呼び、両方を使うことを勧めています。

2 は、その保証の範囲の外側です。<Ref to="prop-sandbox-independent-layer" /> を「サンドボックスを有効にすれば安全」と読んではいけません。ファイル用のツールは権限規則だけで守られています。公式ドキュメントも、サンドボックスの読み取りの拒否の設定は `Read` ツールを止めず、許可ドメインの設定は `WebFetch` を制限しない、と明記しています。全体を 1 つの境界の中に入れるには、Claude Code のプロセスそのものをコンテナや仮想マシンの中で動かす必要がある、とされています。

<Example id="ex-sandbox-home-write" title="許可された Bash コマンドがホームディレクトリに書こうとした場合">
サンドボックスの設定を $P = \{\text{作業ディレクトリ } \texttt{/work/} \text{ 以下のパス}\}$、$H = \varnothing$ とします。allow 規則 `Bash(make *)` があり、モデルが `make build` を呼んだとします。判定は allow で、コマンドは確認なしで実行されます。ところが Makefile の中に、`/work/out.txt` を作る行のほかに、`/home/u/.bashrc` に 1 行を足す行と、外部のホストに接続する行があったとします（規則を書いた人は Makefile の中身を見ていません）。

模型の上では、試みられる操作は $\mathsf{w}(\texttt{/work/out.txt})$、$\mathsf{w}(\texttt{/home/u/.bashrc})$、$\mathsf{c}(\texttt{example.com})$ で、効果を持つのは 1 つ目だけです。残りの 2 つは失敗します（接続については、$H$ が固定であるという模型の仮定のもとで。実物の既定の設定では、Manual モードなら `example.com` への接続について確認が出て、`bypassPermissions` モードなら確認なしで許可されます）。同じコマンドがサンドボックスなしで実行されると、3 つとも効果を持ちます。関門の判定は同じ「allow」で、違うのは実行のときの層だけです。
</Example>

**公式ドキュメントが挙げる限界。** サンドボックスは危険を減らすが完全な隔離の境界ではない、と明記されています。挙げられている主なものは次のとおりです。(1) ネットワークの制限はホスト名による照合で、既定では暗号化された通信の中身を調べない。広いドメインを許可すると、そこを経由した持ち出しの経路になりうる。(2) 許可した Unix ソケット（たとえばコンテナの管理用のソケット）が、ホストへの経路になりうる。(3) 書き込みを広く許すと（実行ファイルの置き場所やシェルの設定ファイルなど）、別の文脈でのコード実行につながりうる。(4) 入れ子の環境で動かすための弱いモードなど、保護を弱める設定がある。(5) ファイルシステムとネットワークの両方を制限して、はじめて効果がある（片方だけでは、他方を通じて迂回できる）。模型の言葉では、これらは $P$ と $H$ を広く取りすぎたときに $\mathrm{Perm}(S)$ そのものが危険な操作を含むこと、そして「許可されない操作は失敗する」という仮定が実物では条件つきであることに当たります。

出典（確認日 2026-10-10）: [Configure the sandboxed Bash tool](https://code.claude.com/docs/en/sandboxing)（"What the sandbox restricts"・"What runs outside the sandbox"・"Sandbox modes"・"How sandboxing works"・"Limitations" の節）、[Configure permissions](https://code.claude.com/docs/en/permissions)（"How permissions interact with sandboxing" の節）、[anthropics/sandbox-runtime](https://github.com/anthropics/sandbox-runtime)（README）、[Security](https://code.claude.com/docs/en/security)。

## 9. 規則は構文を見る：意味的な性質は決められない

権限規則のマッチは文字列の照合です。これは判定を速く確実にしますが、限界も決めます。

この節の主張は、規則の文字列照合なら必ず答えが出るが、「このコマンドは危険な効果を持つか」は一般にはどんな手順でも判定できない、ということです。2 の証明は、「書き込む」ことを「プログラムが停止する」ことに読み替え、Rice の定理で決定不能と分かっている問題に帰着させます。

<Proposition id="prop-decidable-matching" title="マッチは決定可能だが、効果についての性質は決定不能である">
文字の有限集合の上の文字列をコマンドとする。

1. `*` を任意の文字列に当てるワイルドカードのパターン $\pi$ とコマンド $c$ について、$c \models \pi$ かどうかは決定可能である。したがって、有限の規則集合 $R$ について $\mathrm{Mat}_\kappa(R)$ は決定可能な集合である。
2. パス $p$ を固定し、$W_p$ を「実行すると $p$ に書き込むコマンド」の集合とする。コマンドの言語が任意のプログラムを実行できる、すなわち、計算可能な写像 $e \mapsto c_e$ で「$c_e$ を実行すると $p$ に書き込む $\iff$ 番号 $e$ の部分計算可能関数 $\varphi_e$ が入力 $0$ で停止する」を満たすものがあるとする。このとき $W_p$ は決定不能である。
3. 2 の仮定のもとで、どんな有限の規則集合 $R$ についても $\mathrm{Mat}_{\mathrm{deny}}(R) \ne W_p$ である。すなわち、$p$ に書き込むのに deny にマッチしないコマンドがあるか、$p$ に書き込まないのに deny にマッチするコマンドがある。
</Proposition>

<Proof of="prop-decidable-matching">
(1) $\pi$ を `*` で区切って、`*` を含まない文字列の列 $s_0, s_1, \ldots, s_k$ に分けます（$k$ は `*` の個数）。$c \models \pi$ は、「文字列 $t_1, \ldots, t_k$ があって $c = s_0 t_1 s_1 t_2 \cdots t_k s_k$」と同値です。$k = 0$ なら $c = s_0$ かどうかを比べるだけです。$k \ge 1$ のとき、各 $t_i$ は $c$ の中の連続した部分で、$c$ の長さを $L$ とすると、その始まりと終わりの位置の選び方は高々 $(L+1)^2$ 通りです。したがって $(t_1, \ldots, t_k)$ の候補は高々 $(L+1)^{2k}$ 通りで、それぞれについて等式を確かめる手続きは有限の手間で終わります。$\mathrm{Mat}_\kappa(R)$ は、種類 $\kappa$ の有限個の規則のどれかにマッチするかを順に確かめれば決定できます。

(2) 部分計算可能関数の集合 $\mathcal{P} = \{f \mid f(0) \text{ が定義される}\}$ を考えます。$\mathcal{P}$ は空ではなく（つねに $0$ を返す関数を含みます）、すべての部分計算可能関数の集合とも一致しません（どこでも定義されない関数を含みません）。<Ref to="computer-science/ai-era/engineer-survival-strategy#thm-rice" /> により、添字集合 $I_{\mathcal{P}} = \{e \mid \varphi_e \in \mathcal{P}\}$ は決定不能です。$W_p$ が決定可能だと仮定します。仮定の写像 $e \mapsto c_e$ は計算可能で、$e \in I_{\mathcal{P}} \iff c_e \in W_p$ なので、$e$ から $c_e$ を計算して $c_e \in W_p$ かどうかを決めれば $I_{\mathcal{P}}$ が決定できることになり、矛盾します。

(3) 1 から $\mathrm{Mat}_{\mathrm{deny}}(R)$ は決定可能で、2 から $W_p$ は決定不能です。決定可能な集合と決定不能な集合は等しくないので、$\mathrm{Mat}_{\mathrm{deny}}(R) \ne W_p$ です。2 つの集合が等しくないとは、一方に属して他方に属さない元があることです。
</Proof>

2 の仮定は、シェルがインタプリタやコンパイル済みのプログラムを起動できることを言っています。`python3 -c '...'` のようなコマンドは、中のプログラムが止まるときにだけ次のコマンドに進めるので、仮定の写像を実際に書けます。検査の言葉でいえば（<Ref to="computer-science/software-design-testing/case-study-textbook-checks#def-sound-complete-check" />）、「危険な効果を持つコマンド」という意味的な性質について、必ず止まる検査は健全性と完全性を両立できません。構文的な性質なら両立でき、意味的な性質では両立できない、という <Ref to="computer-science/software-design-testing/case-study-textbook-checks#prop-check-soundness-completeness" /> と同じ構図です。

<Example id="ex-rule-evasion" title="同じ効果の別の書き方は規則を外れる">
公式ドキュメント自身が、deny や ask の規則が止めるものと止めないものの表を載せています。たとえば `Bash(rm *)` は `rm -rf build/` を止めますが、`/bin/rm -rf build/`（プログラムをパスで指定）や `bash -c 'rm -rf build/'`（別のシェルの中で実行）にはマッチしません。`Bash(git push *)` は `git push origin main` を止めますが、`git -C . push origin main` にはマッチしません。マッチしなかったコマンドがどうなるかは、ほかの規則と権限モードが決めます。

公式ドキュメントは、この種の規則を、モデルがふつうに書く形の呼び出しを覆うもので、プログラムの周りの安全の境界ではない、と位置づけています。そして、コマンドの文字列に依らない制限が必要なら、ファイルシステムとネットワークについてはサンドボックスを、コマンド全体を自分の論理で調べたいなら `PreToolUse` フックを使うよう案内しています。引数を縛ろうとするパターン（特定の URL だけを許す `curl` の規則など）が壊れやすいことも、オプションの順序・プロトコルの違い・転送・変数の展開の例を挙げて警告しています。
</Example>

この節の結論は、関門の設計全体の読み方を与えます。規則は、**よくある形を速く確実に仕分ける**ためのものです。規則で決まらないものは、既定で確認に回します（公式ドキュメントは、Manual モードではマッチしないコマンドが既定で承認を要することを fail-closed（判断に迷ったら許可せず、確認に回す側に倒すこと）なマッチと呼んでいます）。規則をすり抜けた効果は、サンドボックスという、文字列を見ない層で受け止めます。どの層も単独では完全ではなく、失敗のしかたが違う層を重ねています。

出典（確認日 2026-10-10）: [Configure permissions](https://code.claude.com/docs/en/permissions)（"Wildcard patterns"・"What a Bash rule doesn't match"・"Read-only commands" の節の警告）、[Security](https://code.claude.com/docs/en/security)。

## 10. コード

### 10.1. 判定の模型（実行済み）

3〜8 節の模型を Python で書き、本文の例を実行します。API は呼びません。**これは仕組みを理解するための模型で、Claude Code の実装の再現ではありません。** 引用符の中の区切り、ラッパーの除去、パスのパターンなど、実物にある多くの規則を省いています。

```python
# Python 3.9（標準ライブラリのみ）。権限規則の判定の模型。実物の実装の再現ではない。
import re

SEPARATORS = re.compile(r"&&|\|\||\|&|;|\||&|\n")

def subcommands(command):                 # 複合コマンドを区切りで分ける（引用符は扱わない）
    return [part.strip() for part in SEPARATORS.split(command) if part.strip()]

def matches(pattern, text):               # '*' は任意の文字列。末尾の ' *' は素のコマンドにも合う
    if pattern.endswith(" *") and pattern.count("*") == 1 and text == pattern[:-2]:
        return True
    regex = ".*".join(re.escape(piece) for piece in pattern.split("*"))
    return re.fullmatch(regex, text, flags=re.S) is not None

def parse(rule):                          # "Tool" または "Tool(specifier)"
    m = re.fullmatch(r"(\w+)(?:\((.*)\))?", rule, flags=re.S)
    return m.group(1), (m.group(2) if m.group(2) is not None else "*")

def hit(rules, tool, text):               # text にマッチする規則が rules の中にあるか
    return any(t == tool and matches(p, text) for t, p in map(parse, rules))

def decide(rules, tool, text):            # deny → ask → allow の順。どれも無ければ None
    parts = subcommands(text) if tool == "Bash" else [text]
    if any(hit(rules.get("deny", []), tool, p) for p in parts): return "deny"
    if any(hit(rules.get("ask", []), tool, p) for p in parts): return "ask"
    if all(hit(rules.get("allow", []), tool, p) for p in parts): return "allow"
    return None

def decide_naive(rules, tool, text):      # 比較用: 文字列全体で照合する素朴な方式
    for kind in ("deny", "ask", "allow"):
        if hit(rules.get(kind, []), tool, text): return kind
    return None

def decide_longest(rules, tool, text):    # 比較用: マッチした中で最も長いパターンの種類
    found = [(len(p), kind) for kind in ("deny", "ask", "allow")
             for t, p in map(parse, rules.get(kind, [])) if t == tool and matches(p, text)]
    return max(found)[1] if found else None

def merge(*scopes):                       # 階層ごとの規則を種類ごとに合併する
    return {k: [r for s in scopes for r in s.get(k, [])] for k in ("deny", "ask", "allow")}

def with_hook(hook, rules, tool, text):   # フックの決定と規則の合成
    base = decide(rules, tool, text)
    if hook == "deny" or base == "deny": return "block"
    if base == "ask" or hook == "ask": return "prompt"
    if hook == "allow" or base == "allow": return "run"
    return "mode default"

R1 = {"deny": ["Bash(aws *)"], "allow": ["Bash(aws s3 ls)"]}
R2 = {"allow": ["Bash(npm test *)"]}
print("1.", decide(R1, "Bash", "aws s3 ls"), "| longest-match:", decide_longest(R1, "Bash", "aws s3 ls"))
print("2.", decide(R2, "Bash", "npm test"), decide(R2, "Bash", "npm test -- --watch"),
      decide(R2, "Bash", "npm test && rm -rf build"),
      "| naive:", decide_naive(R2, "Bash", "npm test && rm -rf build"))
project, user = {"allow": ["Bash(git push *)"]}, {"deny": ["Bash(git push *)"]}
print("3.", decide(merge(project, user), "Bash", "git push origin main"),
      decide(merge(user, project), "Bash", "git push origin main"))
R4 = {"deny": ["Bash(rm *)"], "allow": ["Bash(ls *)"]}
print("4.", [decide(R4, "Bash", c) for c in
             ["rm -rf build", "ls && rm -rf build", "/bin/rm -rf build", "ls -la"]])
print("5.", decide({"deny": ["Read(./.env)"], "allow": ["Read"]}, "Read", "./.env"),
      decide({"deny": ["Read(./.env)"], "allow": ["Read"]}, "Read", "./src/a.py"))
R6 = {"deny": ["Bash(git push *)"], "ask": ["Bash(git commit *)"], "allow": ["Bash(git *)"]}
for cmd in ["git push origin main", "git commit -m x", "git status", "make"]:
    print("6.", "%-21s" % cmd, [with_hook(h, R6, "Bash", cmd) for h in (None, "allow", "ask", "deny")])

def run_shell(writes, hosts, sandbox):    # サンドボックスの模型: 効果を許可された集合に切り詰める
    if sandbox is None: return set(writes), set(hosts)
    allow_write, allow_host = sandbox
    return ({p for p in writes if any(p.startswith(d) for d in allow_write)},
            {h for h in hosts if h in allow_host})
sb = (["/work/"], set())
print("7.", run_shell({"/work/out.txt", "/home/u/.bashrc"}, {"example.com"}, sb),
      "| unsandboxed:", run_shell({"/home/u/.bashrc"}, {"example.com"}, None))
```

実行結果（Python 3.9.6、2026-10-10）:

```text
1. deny | longest-match: allow
2. allow allow None | naive: allow
3. deny deny
4. ['deny', 'deny', None, 'allow']
5. deny allow
6. git push origin main  ['block', 'block', 'block', 'block']
6. git commit -m x       ['prompt', 'prompt', 'prompt', 'block']
6. git status            ['run', 'run', 'prompt', 'block']
6. make                  ['mode default', 'run', 'prompt', 'block']
7. ({'/work/out.txt'}, set()) | unsandboxed: ({'/home/u/.bashrc'}, {'example.com'})
```

1 行目は <Ref to="ex-aws-deny" /> と <Ref to="prop-specificity-irrelevant" />（deny 優先では deny、最長一致では allow）。2 行目は <Ref to="prop-compound-command" />（`npm test && rm -rf build` は、分割する方式では許可されず、素朴な方式では許可される）。3 行目は <Ref to="ex-project-allow-user-deny" />（合併の順を入れ替えても deny）。4 行目の 3 つ目は <Ref to="ex-rule-evasion" />（`/bin/rm` は `Bash(rm *)` にマッチしない）。6 の 4 行は <Ref to="prop-hook-composition" /> の表の各列で、フックの決定を「なし・allow・ask・deny」の順に並べています。7 行目は <Ref to="ex-sandbox-home-write" /> です。

### 10.2. Agent SDK で許可するツールを限る（未実行）

Agent SDK では、許可するツールと権限モードを `query()` のオプションで指定します。**このコードは執筆時に実行していません。** 形は公式ドキュメントの説明に従っています。`allowed_tools` は allow 規則を、`disallowed_tools` は deny 規則を足します。`dontAsk` モードは、確認が要る呼び出しを拒否に変えるので、画面の無い実行で「並べたものだけが動く」構成になります。

```python
# 未実行。pip install claude-agent-sdk が必要。認証は SDK の手順に従う（キーの値はコードに書かない）。
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

async def main():
    options = ClaudeAgentOptions(
        allowed_tools=["Read", "Glob", "Grep"],   # allow 規則: 読み取りと検索だけ
        disallowed_tools=["Bash(rm *)"],          # deny 規則: どのモードでも止まる
        permission_mode="dontAsk",                # 確認が要る呼び出しは拒否する
        max_turns=10,
    )
    try:
        async for message in query(prompt="src 以下の TODO コメントを一覧にして。", options=options):
            if isinstance(message, ResultMessage):
                print(message.subtype, getattr(message, "result", None))
    except Exception as error:
        print("ended with an error:", error)

asyncio.run(main())
```

拒否された呼び出しは、前章で見たとおり、失敗の結果としてモデルに返ります（<Ref to="computer-science/claude-code-internals/agent-loop#prop-denied-call-continues" />）。モデルは別の方法を試すか、できなかったことを報告します。

出典（確認日 2026-10-10）: [Configure permissions（Agent SDK）](https://code.claude.com/docs/en/agent-sdk/permissions)（"Allow and deny rules"・"Permission modes" の節）。

## 11. 演習

<Exercise id="exr-three-rules" difficulty="易">
規則集合 $R = \{(\mathrm{allow}, \texttt{Bash(git *)}),\ (\mathrm{ask}, \texttt{Bash(git commit *)}),\ (\mathrm{deny}, \texttt{Bash(git push *)})\}$ について、次の 4 つのコマンドの判定 $\mathrm{dec}^\ast_R$ を求めよ。(a) `git status`、(b) `git commit -m fix`、(c) `git push origin main`、(d) `git status && git push origin main`。
<Solution>
(a) deny にも ask にもマッチせず、allow の `git *` にマッチするので $\mathrm{allow}$。(b) deny にはマッチせず、ask の `git commit *` にマッチするので $\mathrm{ask}$（allow にもマッチしますが、ask が先です）。(c) deny の `git push *` にマッチするので $\mathrm{deny}$。(d) サブコマンドは `git status` と `git push origin main` で、2 つ目が deny にマッチするので、<Ref to="def-compound-command" /> の 1 つ目の場合から $\mathrm{deny}$ です。(a)〜(c) は、10.1 のコードの 6 の行のうちフックなしの列（順に run・prompt・block）に対応します。
</Solution>
</Exercise>

<Exercise id="exr-allow-monotone-fails" difficulty="標準">
<Ref to="prop-deny-monotone" /> の 2 は「allow 規則を足しても $\mathrm{Den}$ と $\mathrm{Ask}$ は変わらない」と述べている。では $\mathrm{All}$ はどうか。allow 規則を足すと $\mathrm{All}(R) \subseteq \mathrm{All}(R')$ であることを示し、真に大きくなる例を挙げよ。これが「安全側にしか動かない」という標語と矛盾しない理由を説明せよ。
<Solution>
$R' = R \cup A$（$A$ は allow 規則）とします。$\mathrm{All} = \mathrm{Mat}_{\mathrm{allow}} \setminus (\mathrm{Mat}_{\mathrm{deny}} \cup \mathrm{Mat}_{\mathrm{ask}})$ で、$\mathrm{Mat}_{\mathrm{allow}}(R) \subseteq \mathrm{Mat}_{\mathrm{allow}}(R')$、引く集合は変わらないので、$\mathrm{All}(R) \subseteq \mathrm{All}(R')$ です。$R = \varnothing$、$A = \{(\mathrm{allow}, \texttt{Bash(ls *)})\}$ とすると、`ls -la` は $\mathrm{All}(R)$ に属さず $\mathrm{All}(R')$ に属します。

allow 規則を足せば許可は増えます。それは allow の役割そのものです。標語が言っているのは、(i) deny や ask を足す操作は許可を増やさないこと、(ii) allow を足す操作は、deny か ask にマッチする呼び出しには届かないこと、の 2 つです。許可が増えるのは、それまで $\bot$ だった（どの規則にもマッチしなかった）呼び出しに限られます。
</Solution>
</Exercise>

<Exercise id="exr-deny-any-all" difficulty="標準">
<Ref to="def-compound-command" /> の定義を、deny も「すべてのサブコマンドがマッチすること」に変えた判定 $\mathrm{dec}'$ を考える（ask と allow は元のまま）。$R = \{(\mathrm{deny}, \texttt{Bash(rm *)}),\ (\mathrm{allow}, \texttt{Bash(ls *)}),\ (\mathrm{allow}, \texttt{Bash(rm *)})\}$ のとき、`ls && rm -rf build` の $\mathrm{dec}'$ と $\mathrm{dec}^\ast$ を求め、$\mathrm{dec}'$ のもとでは「deny にマッチするコマンドは、どんな複合コマンドの一部としても実行されない」が成り立たないことを確かめよ。
<Solution>
サブコマンドは `ls` と `rm -rf build` です。$\mathrm{dec}'$ では、deny の条件は「両方が `rm *` にマッチ」で、`ls` がマッチしないので成り立ちません。ask の規則は無く、allow は `ls` が `ls *` に（末尾の ` *` は素のコマンドにも合います）、`rm -rf build` が `rm *` にマッチするので、$\mathrm{dec}' = \mathrm{allow}$ です。$\mathrm{dec}^\ast$ では、`rm -rf build` が deny にマッチするので $\mathrm{deny}$ です。

$\mathrm{dec}'$ のもとでは、単独では $\mathrm{deny}$ になる `rm -rf build` が、前に無害なコマンドを付けるだけで実行されます。deny を「どれか 1 つ」にしているのは、この抜け道をふさぐためです。allow を「すべて」にしているのは <Ref to="prop-compound-command" /> のためで、2 つの量化が逆向きであることに意味があります。
</Solution>
</Exercise>

<Exercise id="exr-sandbox-union" difficulty="難">
サンドボックスの設定を広げる操作を考える。$S = (P, H)$、$S' = (P', H')$ で $P \subseteq P'$、$H \subseteq H'$ とする。(1) $\mathrm{Perm}(S) \subseteq \mathrm{Perm}(S')$ を示せ。(2) 公式ドキュメントは「ファイルシステムとネットワークの両方を制限して、はじめて効果がある」と述べている。秘密のファイル $f$ の内容を外部のホスト $h_0$ に送ることを「持ち出し」と呼び、読み取りは制限しないとする。$H = \varnothing$ なら、$P$ が何であっても、サンドボックス下のコマンドの効果に $\mathsf{c}(h_0)$ が含まれないことを示せ。逆に、$H = \mathrm{Host}$（ネットワークを制限しない）なら、$P = \varnothing$ でも $\mathsf{c}(h_0)$ が効果に含まれうることを説明せよ。
<Solution>
(1) $\mathsf{w}(p) \in \mathrm{Perm}(S)$ なら $p \in P \subseteq P'$ で $\mathsf{w}(p) \in \mathrm{Perm}(S')$。$\mathsf{c}(h)$ も同じです。したがって、設定を広げると、効果を持ちうる操作は増えるか等しいままです。

(2) $H = \varnothing$ のとき $\mathrm{Perm}(S) = \{\mathsf{w}(p) \mid p \in P\}$ で、接続の操作を 1 つも含みません。<Ref to="prop-sandbox-independent-layer" /> の 1 から $\mathrm{Eff}_S(c) \subseteq \mathrm{Perm}(S)$ なので、$\mathsf{c}(h_0) \notin \mathrm{Eff}_S(c)$ です。これは $P$ に依りません（$H$ が固定であるという命題の仮定のもとでの結論です。実物の既定の設定では、許可ドメインが空でも、権限モードや利用者の承認によって接続が許されることがあります）。$H = \mathrm{Host}$ のとき $\mathsf{c}(h_0) \in \mathrm{Perm}(S)$ で、$f$ を読んで $h_0$ に接続するコマンドは、書き込みを 1 つも必要としないので、$P = \varnothing$ でも効果を持ちます。書き込みの制限だけでは持ち出しを止められません。逆向き（ネットワークだけを制限して書き込みを制限しない場合）については、公式ドキュメントが、システムの資源に細工をしてネットワークへの経路を得る可能性を挙げています。この模型は書き込みの「あと」に起こることを表していないので、そちらは模型の外の議論になります。
</Solution>
</Exercise>

## 参考文献

- Anthropic, [Configure permissions](https://code.claude.com/docs/en/permissions), Claude Code ドキュメント — 規則の種類と評価順、書式、複合コマンド、フック・サンドボックスとの関係、設定の優先順位（確認日 2026-10-10）。
- Anthropic, [Choose a permission mode](https://code.claude.com/docs/en/permission-modes), Claude Code ドキュメント — モードの表、auto モードの判定の順序としきい値（確認日 2026-10-10）。
- Anthropic, [Configure the sandboxed Bash tool](https://code.claude.com/docs/en/sandboxing), Claude Code ドキュメント — サンドボックスの範囲、外で動くもの、限界（確認日 2026-10-10）。
- Anthropic, [Configure permissions](https://code.claude.com/docs/en/agent-sdk/permissions), Claude Agent SDK ドキュメント — 評価の順序、`allowed_tools`・`disallowed_tools`（確認日 2026-10-10）。
- Anthropic, [Tools reference](https://code.claude.com/docs/en/tools-reference)、[Hooks reference](https://code.claude.com/docs/en/hooks)、[Security](https://code.claude.com/docs/en/security), Claude Code ドキュメント（確認日 2026-10-10）。
- Anthropic, [sandbox-runtime](https://github.com/anthropics/sandbox-runtime), GitHub リポジトリの README — OS の仕組みと、読み取り・書き込み・ネットワークの制限の型（確認日 2026-10-10）。
- H. G. Rice, "Classes of Recursively Enumerable Sets and Their Decision Problems", *Transactions of the American Mathematical Society* 74 (1953), 358–366. [doi:10.1090/S0002-9947-1953-0053041-6](https://doi.org/10.1090/S0002-9947-1953-0053041-6) — 意味的な性質の決定不能性。
