0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Codeのパーミッション設定でハマりやすい落とし穴

0
Posted at

はじめに

はじめまして、テックバディ運営の大下です。

Claude Codeを使い始めて最初にやるのが、パーミッション(許可)設定です。毎回「このコマンドを実行していいですか?」と聞かれるのが煩わしくなり、.claude/settings.jsonallow を書き始める。ここまではみんな同じ道を通ります。

問題はその先です。

  • 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 実行を禁止する

そして重要なのが、評価順が denyaskallow に固定されていることです。

公式ドキュメントには、こう明記されています。

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 を書いても、下位ファイルの askdeny は消えません。

これが、冒頭に挙げた allow を書いたのに毎回聞かれる」の正体です。プロンプトで「Yes, and don't ask again」を選ぶと、ルールは .claude/settings.local.jsonallow として保存されますが、チームの .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.md shape 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, and docker exec are not in the list.

これが何を意味するか。Bash(devbox run *) を許可すると、devbox run rm -rf . まで許可されます。 npxdocker 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 maingit log(末尾*は素のコマンドにもマッチ)
Bash(git * main) git merge maingit push origin maingit -c core.fsmonitor=<script> diff main
Bash(* --version) node --versionbash -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 -lals lsof
Bash(ls*) ls -lalsof

末尾 * の直前のスペースはルールの一部です。スペースを入れ忘れると、名前が前方一致する別コマンドまで許可されます。

なお 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) and Read(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. BashBash(rm *) は挙動がまったく違う

deny ルールは、ツール名だけを書いた場合とスペシファイア付きの場合で挙動が変わります

  • "deny": ["Bash"]ツール自体がClaudeのコンテキストから消える。Claudeはそもそもシェルが使えることを知らない状態になる
  • "deny": ["Bash(rm *)"] … ツールは使えるが、マッチする呼び出しだけがブロックされる

「危ないから全部 deny」としてしまうと、Claudeは「シェルを使う」という選択肢自体を失い、代替手段を探して遠回りな作業をし始めます。禁止したいのは「rm -rf」なのか「シェルそのもの」なのかを、書く前に区別してください。

5-2. bypassPermissions は隔離環境専用

確認プロンプトが煩わしくなると、--dangerously-skip-permissionsbypassPermissions モード)に手が伸びます。公式ドキュメントの警告は明確です。

bypassPermissions mode skips permission prompts, including for writes to protected paths such as .git and .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つです。

  1. 評価順は denyaskallow ルールの具体性は関係なく、deny に例外は書けない
  2. && はコマンドを分割する。 npx docker exec devbox run はラッパーとして剥がされないので、prefix許可はフリーパスになる
  3. * はサブコマンドの後ろに置く。 Bash(git * main)git push を許可してしまう
  4. /path は絶対パスではない。 絶対パスは //path。そしてパスチェックは ReadEdit にしか効かない
  5. CLAUDE.md に強制力はない。 本当に止めたいものは deny、OSレベルで止めたいならサンドボックス

設定を書いたら、必ず /permissions実際に何が有効になっているかを確認してください。起動時の警告メッセージも、そのほとんどが「あなたのルールは意図通りに動いていない」というサインです。

そして最後に、一番実践的なアドバイスを。最初から完璧な設定を書こうとしないでください。 まずは default(Manual)モードで数日使い、確認プロンプトから「よく聞かれるコマンド」が見えてきてから、それを allow に落としていく。この順序が結局は一番速く、かつ安全です。

参考リンク


宣伝

最後に少しだけ宣伝させてください。

私たちは テックバディ という、フルスタックAIエンジニア育成コースを運営しています。

この記事で扱ったパーミッション設計も、カリキュラムの序盤で必ず手を動かして身につけるテーマの1つです。AI駆動開発では、AIに何をさせるかと同じくらい、何をさせないかの設計がとても大切です

  • Claude Codeの動作モードとパーミッション設計を、実際に事故らせながら学ぶ
  • CLAUDE.md・ルール・フックによる3層のガードレール設計
  • Next.js / TypeScript / FastAPI / PostgreSQL といった実務スタックでの開発演習
  • メンターとの週1回の1on1

といった内容で構成しています。

「AIに任せるのは怖いが、全部自分でやるのも遅い」という状態を抜け出したい方は、ぜひ一度ご覧ください。

👉 テックバディの詳細・お問い合わせはこちら

最後まで読んでいただきありがとうございました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?