はじめに
はじめまして、テックバディ運営の大下です。
Claude Codeを使い始めて最初にやるのが、パーミッション(許可)設定です。毎回「このコマンドを実行していいですか?」と聞かれるのが煩わしくなり、.claude/settings.json に allow を書き始める。ここまではみんな同じ道を通ります。
問題はその先です。
-
allowを書いたのに、なぜか毎回聞かれる -
Bash(npm test *)を許可したのに、npm test && npm run buildは通らない - 逆に、許可したつもりのない
git pushが通ってしまった - 面倒になって
--dangerously-skip-permissionsに手が伸びる
このあたりは、仕様を知っていれば全部避けられるものばかりです。この記事では、公式ドキュメントの仕様を確認しながら、初心者がハマりやすい落とし穴を5つ整理します。
本記事の内容は、2026年8月時点の Claude Code公式ドキュメント(Configure permissions) を根拠にしています。Claude Codeは更新が速いため、実際の挙動は必ず手元のバージョンで確認してください(
claude --version)。
1. 大前提:ルールは3種類、評価順は「deny → ask → allow」
落とし穴の8割は、この1点を知らないことに起因します。
Claude Codeのパーミッションルールには3種類あります。
| 種別 | 挙動 |
|---|---|
allow |
確認なしで実行を許可する |
ask |
毎回確認プロンプトを出す |
deny |
実行を禁止する |
そして重要なのが、評価順が deny → ask → allow に固定されていることです。
公式ドキュメントには、こう明記されています。
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 の順で評価される。この順で最初にマッチしたものが結果を決め、ルールの具体性は順序を変えない)
1-1. 「より具体的なルールが勝つ」わけではない
CSSやファイアウォールの感覚で、「広い deny を書いても、狭い allow で例外を作れる」と思っていると必ずハマります。
{
"permissions": {
"deny": ["Bash(aws *)"],
"allow": ["Bash(aws s3 ls)"] // ← 効かない。denyが先に評価される
}
}
deny に例外は書けません。「原則禁止だが一部だけ許す」を実現したいなら、deny を書かずに allow だけで絞るのが正しい設計です。
1-2. 設定ファイルは4つあり、リストはマージされる
設定は複数の場所に書けます。優先順位は上から順です。
| 優先度 | ファイル | 影響範囲 |
|---|---|---|
| 1(最強) | 管理者配布の managed settings | 組織全体。ユーザー側から上書き不可 |
| 2 |
claude --settings などのCLI引数 |
そのセッションのみ |
| 3 | .claude/settings.local.json |
自分・このプロジェクトのみ(gitに入れない) |
| 4 | .claude/settings.json |
チーム全員(コミットして共有する) |
| 5 | ~/.claude/settings.json |
自分・全プロジェクト |
ここで初心者がハマるのが、permissions.allow のようなリスト型のキーは「上書き」ではなく「マージ」されるという点です。優先度の高いファイルに allow を書いても、下位ファイルの ask や deny は消えません。
これが、冒頭に挙げた 「allow を書いたのに毎回聞かれる」の正体です。プロンプトで「Yes, and don't ask again」を選ぶと、ルールは .claude/settings.local.json に allow として保存されますが、チームの .claude/settings.json に同じ対象の ask があれば、評価順で ask が先に当たるため聞かれ続けます。
【スクショ指定】Claude Code上で
/permissionsを実行したダイアログのスクリーンショットを掲載する。ルール一覧と、各ルールがどのsettings.json由来かが表示されている点が分かるように全体を写す。
「なぜ効かないのか」を調べるときは、まず /permissions を開いてください。どのルールがどのファイル由来かが一覧で確認できます。
1-3. CLAUDE.mdに書いても強制力はない
もう1つ、非常に大事な前提です。
Permission rules are enforced by Claude Code, not by the model. Instructions in your prompt or
CLAUDE.mdshape what Claude tries to do, but they don't change what Claude Code allows.
CLAUDE.md に「本番環境には絶対にデプロイしないこと」と書くのは有効ですが、それはモデルの振る舞いを誘導しているだけで、境界にはなりません。本当に止めたいものは deny ルールで止める。これが原則です。
2. 落とし穴①:&& とラッパーで、許可は簡単にズレる
2-1. 複合コマンドは1つずつマッチさせる必要がある
Bash(npm test *) を許可したのに npm test && npm run build が通らない。これは仕様通りの挙動です。
Claude Codeはシェル演算子を認識しており、&& || ; | |& & および改行でコマンドを分割します。そして、分割された各サブコマンドがそれぞれ独立にルールにマッチしなければ、全体は許可されません。
Bash(npm run *) を allow に設定した場合
npm run build → 通る
npm run build && npm run test → 通る(両方 npm run * にマッチ)
npm run build && rm -rf dist → 通らない(rm -rf dist がマッチしない)
これはセキュリティ上そうあるべき挙動です。もしそうでなければ、safe-cmd && 何でも で許可をすり抜けられてしまいます。
2-2. ラッパーは剥がされる。ただし「一部だけ」
Claude Codeは、マッチング前に一部のラッパーコマンドを取り除きます。剥がされるのは以下です。
timeout / time / nice / nohup / stdbuf / command / builtin / zshの noglob、およびフラグなしの xargs
つまり Bash(npm test *) は timeout 30 npm test にもマッチします。
問題は、剥がされないほうです。 公式ドキュメントは明確に警告しています。
Development environment runners such as
direnv exec,devbox run,mise exec,npx, anddocker execare not in the list.
これが何を意味するか。Bash(devbox run *) を許可すると、devbox run rm -rf . まで許可されます。 npx や docker exec も同様です。これらは「引数を実行する」性質のコマンドなので、prefix許可は事実上のフリーパスになります。
正しい書き方は、ランナーと中身をセットで書くことです。
{
"permissions": {
"allow": [
"Bash(devbox run npm test)", // ← 中身まで固定する
"Bash(docker exec myapp npm test)"
]
}
}
なお watch setsid ionice flock といったexec系ラッパーや、find の -exec / -delete 形式は prefix ルールでは自動承認されず、必ず確認が入ります。
3. 落とし穴②:ワイルドカードの位置ひとつで git push まで開く
3-1. * はサブコマンドの「後ろ」に置く
これが最も危険な落とし穴です。公式ドキュメントの表から引用します。
| 書いたルール | マッチするコマンド |
|---|---|
Bash(git log *) |
git log --oneline main、git log(末尾*は素のコマンドにもマッチ) |
Bash(git * main) |
git merge main、git push origin main、git -c core.fsmonitor=<script> diff main
|
Bash(* --version) |
node --version、bash -c 'echo hi' --version
|
Bash(git * main) は「mainブランチに対するgit操作」を許可したつもりでも、* がサブコマンドの位置を埋めてしまうため、push も -c(任意のプログラムを実行させられるオプション)も通ります。
ルールは「最初の * より前を、書いたとおりに一致させる」という動きをします。したがって、* より前にプログラム名とサブコマンドの両方が入っている必要があります。
- ❌
Bash(git * main)… サブコマンドが* - ✅
Bash(git log *)… サブコマンドが固定されている
Claude Codeは、サブコマンドの前に * がある allow ルールに対して起動時に警告を出します。この警告は無視しないでください。
【スクショ指定】
.claude/settings.jsonにわざと"allow": ["Bash(git * main)"]を書いた状態でclaudeを起動し、起動時警告が表示されている画面のスクリーンショットを掲載する。
3-2. スペースの有無で意味が変わる
地味ですが効いてきます。
| ルール | マッチ | マッチしない |
|---|---|---|
Bash(ls *) |
ls -la、ls
|
lsof |
Bash(ls*) |
ls -la、lsof
|
— |
末尾 * の直前のスペースはルールの一部です。スペースを入れ忘れると、名前が前方一致する別コマンドまで許可されます。
なお Bash(ls:*) は Bash(ls *) と同じ意味です。ただし :* が特別扱いされるのは末尾のときだけで、Bash(git:* push) のように途中に書くとコロンはただの文字として扱われ、意図したマッチをしません。
3-3. 引数でURLを絞ろうとしない
「curlはGitHubにだけ許可したい」として Bash(curl http://github.com/ *) と書くのは、公式ドキュメントが名指しで「壊れやすい」と警告しているパターンです。以下で簡単に外れます。
- オプションが先:
curl -X GET http://github.com/... - プロトコル違い:
curl https://github.com/... - リダイレクト:
curl -L http://short.example.com/xyz - 変数展開:
URL=http://github.com && curl $URL - 空白が2つ:
curl http://github.com
推奨される代替は、curl / wget 自体を deny し、URL取得は WebFetch(domain:github.com) に寄せるという設計です。ただし注意点として、Bashが使える限りWebFetchを絞ってもネットワークアクセスは防げません。deny とセットで初めて意味を持ちます。
4. 落とし穴③:パスルールの / は「絶対パス」ではない
4-1. スラッシュ1つは、設定ファイルの位置が基準
Read / Edit のルールはgitignore記法を使いますが、先頭のスラッシュの意味が独特です。
| 書き方 | 意味 |
|---|---|
//path |
ファイルシステムのルートからの絶対パス |
~/path |
ホームディレクトリ基準 |
/path |
設定ファイルの位置が基準 |
path / ./path
|
カレントディレクトリ基準 |
つまり Read(/Users/alice/.ssh/**) は絶対パスではありません。これを ~/.claude/settings.json(ユーザー設定)に書いた場合、基準になるのは ~/.claude/ です。結果として、意図した /Users/alice/.ssh/ ではなく ~/.claude/Users/alice/.ssh/ という存在しないパスを指してしまいます。
絶対パスを指定したいときはスラッシュ2つです。
{
"permissions": {
"deny": [
"Read(//Users/alice/.ssh/**)", // 絶対パス
"Read(~/.aws/**)", // ホーム基準
"Read(.env)" // 任意の深さの .env
]
}
}
4-2. allowとdenyで「深さ」の解釈が違う
同じ src/** という書き方でも、ルール種別で挙動が変わります。
-
allowルール:
Edit(src/**)は<cwd>/src直下のみ。任意の深さを許可したいならEdit(**/src/**) -
denyルール:
Read(secrets/**)は任意の深さのsecretsディレクトリにマッチする
「安全側に倒す」設計になっているわけですが、allowを書いたのに効かない原因としてよく効いてきます。
4-3. Write(...) や Glob(...) にパスを書いても読まれない
これは知らないと絶対に気づけません。
Claude Code checks file permissions against
Edit(path)andRead(path)rules only.
ファイルパスのチェックに使われるのは Edit(...) と Read(...) だけです。Write(docs/**) や Glob(docs/**)、NotebookEdit(docs/**) と書いても、ルールは受け付けられるものの参照されません(起動時に警告は出ます)。
-
Write(docs/**)と書きたいときはEdit(docs/**)と書く -
Glob(docs/**)と書きたいときはRead(docs/**)と書く
なお Read の deny ルールは、同じパスに対する Edit / Write もブロックします。ただし NotebookEdit は対象外なので、絶対に変更させたくないパスには Edit の deny も併記してください。
4-4. 「Pythonスクリプト経由の読み書き」は止められない
もう1つ重要な限界です。Read / Edit の deny ルールは、Claude Codeの組み込みファイルツールと、cat head tail sed のようなClaude Codeが認識できるBashコマンドには効きます。
しかし、任意のスクリプト経由の読み書きは止められません。
# Read(.env) を deny していても、これは素通りする
python -c "print(open('.env').read())"
OSレベルで確実に遮断したい場合は、パーミッションではなくサンドボックス機能を使う必要があります。パーミッションは「Claudeの行動の境界」であって、「OSの境界」ではないと理解しておいてください。
5. 落とし穴④:deny の副作用と bypassPermissions の誤用
5-1. Bash と Bash(rm *) は挙動がまったく違う
deny ルールは、ツール名だけを書いた場合とスペシファイア付きの場合で挙動が変わります。
-
"deny": ["Bash"]… ツール自体がClaudeのコンテキストから消える。Claudeはそもそもシェルが使えることを知らない状態になる -
"deny": ["Bash(rm *)"]… ツールは使えるが、マッチする呼び出しだけがブロックされる
「危ないから全部 deny」としてしまうと、Claudeは「シェルを使う」という選択肢自体を失い、代替手段を探して遠回りな作業をし始めます。禁止したいのは「rm -rf」なのか「シェルそのもの」なのかを、書く前に区別してください。
5-2. bypassPermissions は隔離環境専用
確認プロンプトが煩わしくなると、--dangerously-skip-permissions(bypassPermissions モード)に手が伸びます。公式ドキュメントの警告は明確です。
bypassPermissionsmode skips permission prompts, including for writes to protected paths such as.gitand.claude. Only use this mode in isolated environments like containers or VMs where Claude Code can't cause damage.
.git や .claude といった保護パスへの書き込みまで素通りになります。使ってよいのは、コンテナやVMなど「壊れても困らない隔離環境」だけです。
自分(やチーム)が誤って使わないようにロックすることもできます。
{
"permissions": {
"disableBypassPermissionsMode": "disable"
}
}
5-3. 「面倒だから全部許可」の前に、モードを選ぶ
そもそも、プロンプトが多すぎる原因が「ルールの書き方」ではなく「モードの選択」であることも多いです。Claude Codeには複数のパーミッションモードがあります。
| モード(設定値) | 確認なしで実行されるもの | 向いている場面 |
|---|---|---|
default(表示名は Manual) |
読み取りのみ | すべて自分で確認したいとき |
acceptEdits |
読み取り+ファイル編集+mkdir touch mv cp などの基本コマンド |
自分でレビューしながら実装を回すとき |
plan |
読み取り中心。ソースを編集しない | 変更前のコードベース調査 |
auto |
ほぼすべて(バックグラウンドの安全チェック付き) | 長時間タスク、確認疲れの軽減 |
dontAsk |
事前承認済みのツールのみ(それ以外はプロンプトを出さずに拒否) | CIやスクリプトなど締めたい環境 |
bypassPermissions |
すべて | 隔離されたコンテナ・VM専用 |
ここで意外と知られていないのが、 Pro / Max / Teamプランでは、セッションの開始モードが auto(Manualではない) という点です。「確認が少ないな」と感じていたら、それはルールが効いているのではなく単にモードが auto だった、というケースがあります。現在のモードはCLIなら Shift+Tab で切り替え・確認できます。
そして、モードとルールの関係も押さえておいてください。
-
denyルールは全モードで効く(bypassPermissionsでも効く) -
allowルールはbypassPermissionsでは意味を持たない(そもそも確認しないため) -
askルールにマッチしたもの、AskUserQuestion、重要パスへのrm/rmdirは、どのモードでも自動承認されない
「調査だけさせたい」なら plan、「編集は任せたいがコマンド実行は握りたい」なら acceptEdits というように、モードで大枠を決めてから、ルールで細部を詰めるのが正しい順序です。セッション開始時のモードは、設定ファイルの defaultMode で指定できます。
まとめ
Claude Codeのパーミッション設定は、「なんとなく allow を足していく」と必ずどこかで破綻します。押さえるべき原則は5つです。
-
評価順は
deny→ask→allow。 ルールの具体性は関係なく、denyに例外は書けない -
&&はコマンドを分割する。npxdocker execdevbox runはラッパーとして剥がされないので、prefix許可はフリーパスになる -
*はサブコマンドの後ろに置く。Bash(git * main)はgit pushを許可してしまう -
/pathは絶対パスではない。 絶対パスは//path。そしてパスチェックはReadとEditにしか効かない -
CLAUDE.mdに強制力はない。 本当に止めたいものはdeny、OSレベルで止めたいならサンドボックス
設定を書いたら、必ず /permissions で実際に何が有効になっているかを確認してください。起動時の警告メッセージも、そのほとんどが「あなたのルールは意図通りに動いていない」というサインです。
そして最後に、一番実践的なアドバイスを。最初から完璧な設定を書こうとしないでください。 まずは default(Manual)モードで数日使い、確認プロンプトから「よく聞かれるコマンド」が見えてきてから、それを allow に落としていく。この順序が結局は一番速く、かつ安全です。
参考リンク
- Configure permissions — Claude Code Docs — ルール構文、評価順、ツール別の仕様
- Claude Code settings — 設定ファイルの場所と優先順位
- Permission modes — 各モードの詳細と保護パス
- Sandboxing — OSレベルでの隔離
- Hooks — ルールでは表現しきれない条件をPreToolUseフックで実装する
宣伝
最後に少しだけ宣伝させてください。
私たちは テックバディ という、フルスタックAIエンジニア育成コースを運営しています。
この記事で扱ったパーミッション設計も、カリキュラムの序盤で必ず手を動かして身につけるテーマの1つです。AI駆動開発では、AIに何をさせるかと同じくらい、何をさせないかの設計がとても大切です。
- Claude Codeの動作モードとパーミッション設計を、実際に事故らせながら学ぶ
-
CLAUDE.md・ルール・フックによる3層のガードレール設計 - Next.js / TypeScript / FastAPI / PostgreSQL といった実務スタックでの開発演習
- メンターとの週1回の1on1
といった内容で構成しています。
「AIに任せるのは怖いが、全部自分でやるのも遅い」という状態を抜け出したい方は、ぜひ一度ご覧ください。
最後まで読んでいただきありがとうございました。