0
2

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 の deny ルール、こっそりスキップされてませんか? — 無効な permission 構文の落とし穴と正しい書き方

0
Last updated at Posted at 2026-06-10

TL;DR

  • Claude Code の settings.json の permission ルールは、構文が無効だとそのルールが黙ってスキップされることがある。
  • スキップされても通常の操作中は気づきにくく、deny を書いたのに効いていない状態になりうる。
  • よくある無効パターンは4つ:
    1. defaultMode: "ask""ask" という値は存在しない)
    2. Bash(cat:*.env*) のように :* を末尾以外で使う
    3. WebFetch(http://*)(WebFetch は URL ではなくドメイン形式
    4. Bash(bash:<(*) のように 括弧が閉じないパターン
  • 正しい構文は公式ドキュメントに明記されている。検知は対話起動 or /permissions が確実(ヘッドレス -p では表面化しないことがある)。

この記事は社内のセキュリティ勉強会で Claude Code のベースライン設定を作っていて実際に踏んだ内容です。エラーメッセージは筆者環境(執筆時点の Claude Code)で実際に表示されたものを引用しています。挙動の一部は公式ドキュメントに明記が無く、観察ベースである点は本文で都度ことわります。

きっかけ:全社配布する直前に気づいた

Claude Code のセキュリティ設定(settings.jsonpermissions)を社内標準として配ろうとしていました。秘密ファイルの読取や curl | shrm -rf / などを deny で塞ぐ、よくあるやつです。

ところが、いざ実機に置いて claude を起動したら Settings Error / Warning が出ました。よく読むと「一部のルールは無効でスキップされた」と書いてある。つまり書いたつもりの deny が効いていなかったわけです。全社に配る前に気づけて冷や汗ものでした。

前提:permission は deny → ask → allow の3層

まず軽くおさらい。Claude Code の permission は3種類のルールで制御します(公式: Configure permissions)。

ルール 効果
deny そのツール使用を禁止(機械的にブロック)
ask 使用のたびに確認
allow 無確認で許可

評価順は deny → ask → allow。最初にマッチしたものが勝ち、deny が最優先。
そして重要なのが、permission は「モデル」ではなく「Claude Code 本体」が enforce するという点(公式に明記)。CLAUDE.md の指示は「Claude が何をしようとするか」を変えるだけで、許可・禁止の境界そのものは settings.json 側が決めます。

この deny が無効構文でスキップされると、防御が穴になる——というのが本題です。

ハマった無効構文 4パターンと、正しい書き方

ここからが本編。筆者が実際に踏んだ4つです。正しい書き方は公式のPermission rule syntaxに準拠しています。

defaultMode: "ask" — そんな値は無い

// ❌ 無効
{ "permissions": { "defaultMode": "ask" } }

実機で出たエラー:

defaultMode: Invalid value. Expected one of:
"acceptEdits", "auto", "bypassPermissions", "default", "dontAsk", "plan"

"ask" という mode は存在しません。「未許可の操作は確認する」標準挙動が欲しいなら "default" です。

// ✅ 正しい
{ "permissions": { "defaultMode": "default" } }

有効値は公式のPermission modesの通り:default / acceptEdits / plan / auto / dontAsk / bypassPermissions

:* を末尾以外で使っている

// ❌ 無効(:* が途中にある)
"Bash(cat:*.env*)"
"Bash(git:*--no-verify*)"

実機で出たエラー:

Invalid permission rule "Bash(cat:*.env*)" was skipped:
The :* pattern must be at the end. Move :* to the end for prefix matching,
or use * for wildcard matching

公式の記述がそのまま答えです:

The :* form is only recognized at the end of a pattern. In a pattern like Bash(git:* push), the colon is treated as a literal character and won't match git commands.

:* は「末尾のワイルドカード(前方一致)」の糖衣構文で、末尾でしか効きません。途中にワイルドカードを置きたいなら スペース+* を使います。ワイルドカードは任意位置に置けます(公式: "Wildcards can appear at any position")。

// ✅ 正しい(* を任意位置に)
"Bash(cat *.env*)"
"Bash(git * --no-verify*)"

ちなみに Bash(ls *)(スペースあり)は ls -la にマッチするが lsof にはマッチしない、Bash(ls*)(スペースなし)は両方にマッチ、というスペースの有無で語境界が変わる仕様も公式に書かれています。

WebFetch(http://*) — WebFetch は URL 形式が使えない

// ❌ 無効
"WebFetch(http://*)"

実機で出た警告:

Invalid permission rule "WebFetch(http://*)" was skipped:
WebFetch permissions use domain format, not URLs. Use "domain:hostname" format

WebFetch の specifier は ドメイン形式 domain:... のみ(公式の例も WebFetch(domain:example.com) だけ)。http:// のようなスキーム指定はできません。

// ✅ 正しい(ドメイン単位)
"WebFetch(domain:internal.example.com)"

「http だけ deny したい」は WebFetch ルールでは表現できない点に注意。

Bash(bash:<(*) — 括弧が閉じていない

プロセス置換 bash <(curl ...) を塞ごうとして書いたもの:

// ❌ 無効
"Bash(bash:<(*)"

実機で出た警告:

Invalid permission rule "Bash(bash:<(*)" was skipped:
Mismatched parentheses. Ensure all opening parentheses have matching closing parentheses

<(( が閉じ括弧なしと判定されます。permission パターンで素直に表現できないので、このケースは別レイヤ(CLAUDE.md の指示や PreToolUse hook、curl 系の deny/ask)で対応するのが現実的でした。

一番大事な話:「スキップ」の範囲は?

ここが記事のキモであり、注意が必要な部分です。

筆者の環境では、エラーの種類によって表示が違いました

  • 個別ルールが無効(例 WebFetch(http://*))→ Settings Warning

    The values listed above were skipped; the rest of the file is in effect.
    (= その無効ルールだけスキップ。残りは有効)

  • defaultMode の値が無効など → Settings Error

    Files with errors are skipped entirely, not just the invalid settings.
    (= ファイル全体がスキップされる、と表示)

つまり、

「どんな構文ミスでも全 deny が吹き飛ぶ」わけではない。
個別ルールの無効はその行だけスキップ。ただし構造的な値(defaultMode 等)の無効は影響が大きい(筆者環境では「ファイル全体スキップ」と表示)。

⚠️ 公式ドキュメントには、この「スキップの範囲(個別か全体か)」の明記が見当たりませんでした。 上記はあくまで筆者環境のダイアログ表示に基づく観察です。バージョンによって挙動が変わる可能性があるので、自分の環境で必ず確認してください。

いずれにせよ教訓は同じです:

無効な構文を1つでも残すと、少なくともそのルールは黙って無効になる。最悪ファイル全体が無効になりうる。

「deny を書いた=守られている」と思い込まず、実際にロードされているか確認することが必要です。

どうやって検知する?

確実なのは対話起動 or /permissions

  • claude対話モードで起動すると、無効ルールがあれば起動時に Settings Error/Warning ダイアログが出ます。これが一番確実でした。
  • セッション中は /permissions で、現在有効なルールとどの settings.json 由来かを一覧できます(公式機能)。

ヘッドレス -p は当てにしない(観察)

筆者環境では、claude -p(ヘッドレス)だと無効設定があってもエラーが表面化せず、そのまま動いてしまう挙動でした。CI などヘッドレスで使っている場合、設定の妥当性検証には向かない点に注意(これも観察ベース)。

# JSON として壊れていないかの最低限チェック(構文ルール違反は検知できない点に注意)
jq empty ~/.claude/settings.json

jq は JSON の文法エラーは見つけますが、:* の位置や WebFetch 形式のようなClaude Code 固有のルール違反は検知できません。最終確認は対話起動が確実です。

おまけ:公式から拾った「誤解しやすい」トリビア

記事を書くにあたり公式を読み込んで、知っておくと得なものをいくつか。

  • bypassPermissions(いわゆる YOLO)でも rm -rf /rm -rf ~ は止まる。サーキットブレーカーとして残っていると公式に明記。「全部スキップ」と思いがちだが例外がある。
  • Read/Edit の deny は Bash の cat/head/tail/sed にも適用される。なので Read(.env) を deny すれば cat .env も止まる。Bash(cat *.env*) は部分的に冗長(ただし Python/Node スクリプトが自前で開く読取は防げない)。
  • Bash の引数を絞るパターンは脆い。公式が「curl http://github.com/ * はオプション順・リダイレクト・変数展開で回避できる」と警告し、ドメイン allowlist や PreToolUse hook を使えと推奨している。
  • Bash(括弧なし)や Bash(*) を deny にすると、ツールごと Claude の文脈から消える(Claude はそのツールの存在を見えなくなる)。一方 Bash(rm *) のようなスコープ付き deny はツールは残してマッチ時だけ止める。

まとめ

  • settings.json の permission ルールは構文が無効だとスキップされる。「書いた deny が効いていない」事故につながる。
  • よく踏む無効パターンは defaultMode:"ask" / :* の位置 / WebFetch の URL 形式 / 括弧不一致 の4つ。正しい書き方は公式のPermission rule syntaxに準拠。
  • 「全 deny が無効化される」は言い過ぎ。個別ルールはその行だけスキップ、構造的な値の無効は影響大(範囲は公式に明記が無いので各自確認)。
  • 検知は対話起動 or /permissions。ヘッドレス -p は表面化しないことがある(観察)。
  • 設定を配る前に、実機で一度ロードして確認するのが結局いちばん安全。

参考


本記事のエラーメッセージ・挙動は執筆時点の筆者環境での観察を含みます。Claude Code のバージョンにより異なる場合があるため、最新は公式ドキュメントと自身の環境でご確認ください。

0
2
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
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?