Claude Code Auto Modeの権限設定をPythonで点検する
結論から言うと、Claude CodeのAuto Modeを使う前に見るべきなのは、普段は意識しないsettings.local.jsonです。便利だからと残したBash(git *)やBash(python *)は、Auto Modeの安全策とは別に、チームの運用ルールとして一度棚卸しした方がいいです。
Auto Modeは、承認ボタンを連打してしまう問題を減らすための仕組みです。Anthropicは、従来の許可プロンプトの93%が承認されていたと説明しています。これはかなり実感があります。昨日、検証用の設定を作ってスクリプトを回したら、ローカル設定にdefaultMode: bypassPermissionsを入れたケースが一発で赤く出ました。テスト用だから平気、と言いかけて止まりました。設定は残ります。
Q. Auto Modeがあれば、権限設定は見なくていい?
見ないままは危ないです。
Auto Modeは、ツール出力に混じるプロンプトインジェクションを検査し、実行前の操作は別の分類器で判定します。さらに、Bash全体やBash(python *)のような任意コード実行につながる広い許可は、Auto Modeに入ると持ち越さない設計です。Anthropicの技術解説には、このあたりがかなり具体的に書かれています。
ただし、設定ファイルが消えるわけではありません。Claude Codeはユーザー設定、共有プロジェクト設定、プロジェクトローカル設定を読みます。permissions.allowのようなリストは上書きではなく併合されます。公式のsettings解説を読むと、ローカル設定は共有設定より優先される一方、許可ルールは足し合わされると明記されています。
Auto Modeを安全装置にするなら、その前に「何を常時許可しているか」を読める状態にしておく。僕はここを分けて考えています。
Q. どの設定を拾えばいい?
次のスクリプトは、指定した設定JSONから広すぎる許可を拾います。Bash全体、git全体、スクリプト実行、パッケージ操作、外向き通信を伴うコマンドを対象にしました。denyも一緒に出すので、.envを読ませない設定が入っているかも確認できます。
監査対象はユーザー設定、共有プロジェクト設定、ローカル設定の3か所です。
# claude_permission_audit.py
import argparse
import json
import re
from pathlib import Path
RISKY_ALLOW = [
(re.compile(r"^Bash(?:\(\*\))?$"), "Bash全体を常時許可"),
(
re.compile(r"^Bash\((?:python|python3|node|ruby|perl)(?:\s|:|\*).+\)$"),
"スクリプト実行を広く許可",
),
(re.compile(r"^Bash\((?:npm|pnpm|yarn)\s+\*\)$"), "パッケージ操作を広く許可"),
(re.compile(r"^Bash\(git\s+\*\)$"), "gitの全操作を許可"),
(re.compile(r"^Bash\((?:curl|wget)\s+\*\)$"), "外部送信を伴うコマンドを広く許可"),
]
REVIEW_ALLOW = [
(re.compile(r"^WebFetch(?:\(\*\))?$"), "取得先を絞らないWebFetch"),
(re.compile(r"^mcp__.+__\*$"), "特定MCPサーバーの全ツールを許可"),
]
def classify(rule: str):
for pattern, reason in RISKY_ALLOW:
if pattern.fullmatch(rule):
return "HIGH", reason
for pattern, reason in REVIEW_ALLOW:
if pattern.fullmatch(rule):
return "REVIEW", reason
return None
def get_rules(permissions: dict, key: str):
rules = permissions.get(key, [])
if not isinstance(rules, list):
print(f" ERROR: permissions.{key} は配列である必要があります")
return []
return rules
def inspect(label: str, path: Path):
print(f"[{label}] {path}")
try:
settings = json.loads(path.read_text(encoding="utf-8"))
except FileNotFoundError:
print(" SKIP: ファイルなし")
return
except json.JSONDecodeError as exc:
print(f" ERROR: JSONが不正です (line {exc.lineno}, column {exc.colno})")
return
mode = settings.get("defaultMode")
if mode == "bypassPermissions":
print(" HIGH: defaultMode=bypassPermissions (隔離環境以外では使わない)")
elif mode == "auto":
print(" OK: defaultMode=auto")
permissions = settings.get("permissions", {})
if not isinstance(permissions, dict):
print(" ERROR: permissions はオブジェクトである必要があります")
return
for rule in get_rules(permissions, "allow"):
if not isinstance(rule, str):
print(f" ERROR: allow に文字列以外があります: {rule!r}")
continue
result = classify(rule)
if result:
level, reason = result
print(f" {level}: allow {rule} ({reason})")
for rule in get_rules(permissions, "deny"):
if isinstance(rule, str):
print(f" PROTECT: deny {rule}")
def main():
parser = argparse.ArgumentParser(
description="Claude Code settings の許可ルールをざっくり点検する"
)
parser.add_argument(
"--scope",
action="append",
default=[],
metavar="名前=PATH",
help="例: --scope user=~/.claude/settings.json",
)
args = parser.parse_args()
if not args.scope:
parser.error("--scope 名前=PATH を少なくとも1つ指定してください")
for item in args.scope:
if "=" not in item:
parser.error("--scope は 名前=PATH の形式です")
label, raw_path = item.split("=", 1)
inspect(label, Path(raw_path).expanduser())
if __name__ == "__main__":
main()
実行例です。ファイルがない場所はSKIPになるので、個人設定しかない人もそのまま実行できます。
python3 claude_permission_audit.py \
--scope user=~/.claude/settings.json \
--scope project=.claude/settings.json \
--scope local=.claude/settings.local.json
検証用のJSONでは、こう出ました。
[user] /tmp/claude-check/settings.json
OK: defaultMode=auto
HIGH: allow Bash(git *) (gitの全操作を許可)
HIGH: allow Bash(python *) (スクリプト実行を広く許可)
REVIEW: allow mcp__github__* (特定MCPサーバーの全ツールを許可)
PROTECT: deny Read(./.env)
PROTECT: deny Bash(curl *)
[local] /tmp/claude-check/settings.local.json
HIGH: defaultMode=bypassPermissions (隔離環境以外では使わない)
Q. HIGHが出たら、全部消す?
消す前に、許可の粒度を小さくします。たとえばBash(git *)を置いたままにせず、必要な作業だけBash(git log *)やBash(git diff *)にします。公式ドキュメントでも、Bash(git *)はpushを含むすべてのgit操作に一致すると説明されています。権限ルールの仕様を読むと、ワイルドカードがサブコマンドの前にあるか後ろにあるかで、許可範囲が大きく変わります。ここ地味に効きます。
このスクリプトは設定の優先順位を完全再現するものではありません。--settings付きの起動や組織のmanaged settingsまで含めた実効値は、Claude Code内の/statusと/permissionsで確認してください。JSONにコメントや末尾カンマがあると設定自体が無効になるので、そのエラーも先に直します。
で、現場でどう使うか
僕なら、Auto Modeへ切り替える日と、MCPサーバーを増やす日にはこの点検を一度回します。HIGHは即削除ではなく、誰が何の作業で必要なのかを決めて、狭いルールに置き換えます。共有の.claude/settings.jsonは便利な設定ファイルである前に、チーム全員に効く権限表です。
Auto Modeが承認疲れを減らしてくれるのは事実です。だからこそ、常時許可だけは人が読める大きさにしておく。これで使い始めの安心感がかなり変わります。