2026-09-10 · claudecode · security · 設定 · 個人開発 · devops
書いた deny ルールが、一度も参照されていなかった
エージェントを1つのディレクトリの中に留めるつもりで、これを書いていました。
{ "permissions": { "deny": ["Write(src/generated/**)"] } }
構文は正しい。読み込まれる。そして一度も参照されません。
ファイルパスのルールが効くのは Read(...) と Edit(...) だけです。
Write / NotebookEdit / Glob / 旧 MultiEdit に書いても、
受け付けられたうえで一度も見られません。
起動時に警告は出ます。私は起動時の出力を読んでいませんでした。 それまで一度も、そこに何か問題が出たことがなかったので。
これは安いほうです。高いほうを書きます。
Bash(git * main) は git の全サブコマンドを許す
Bash ルールの * は、その位置の文字列すべてに当たります。
だから * を前に置くほど、ルールは広くなります。
| 書き方 | 実際に許可されるもの |
|---|---|
Bash(git log *) |
git log から始まるコマンド |
Bash(git * main) |
git の全サブコマンド。 git push origin main も含む |
Bash(* --version) |
そのマシンのあらゆるプログラム |
Bash(git * main) は「main に対する git コマンド1つ」に読めます。
git push origin main が通ります。
git -c core.pager=… diff main も通り、-c は git に別のプログラムを起動させます。
私は Bash(git * main) を、diff を許したつもりで書いていました。
* がコマンドの途中にある allow ルールには、起動時に警告が出ます。
上と同じで、警告が流れていく間もルールは効いています。
空白1つで、lsof が許可されるかどうかが変わる
Bash(ls *) 当たる: ls -la, ls 当たらない: lsof
Bash(ls*) 当たる: ls -la, lsof
末尾の * の前の空白は、ルールの一部です。
そして Bash(ls *) が引数なしの ls にも当たるのは、
その末尾の * がそのルール唯一のワイルドカードのときだけです。
Bash(* --help *) は npm --help x に当たり、npm --help には当たりません。
読み込まれて何もしないものが、あと3つ
アンカーの無い allow グロブはスキップされます。
allow に書いた "*" / "B*" / "mcp__*" は、何も自動承認しません。
allow でグロブが使えるのは mcp__<サーバ名>__ まで書いたあとだけです。
どのサーバかを名指ししている必要があるためです。
本体の入力へのパラメータ指定は無視されます。
Bash(command:rm *) は複合コマンドで抜けられるので、効きません。
Bash, PowerShell → command Grep, Glob → path
Read, Edit, Write → file_path WebFetch → url
NotebookEdit → notebook_path
Bash(rm *) / Read(./.env) / WebFetch(domain:host) と書きます。
括弧つきの mcp__ ルールは読み込み時にスキップされます。
mcp__memory(read) は効きません。
起動時のダイアログと claude doctor に出ますが、CI ではどちらも見えません。
もう1つは構文の罠です。Bash(ls:*) は Bash(ls *) と同じですが、
末尾に置いたときだけ。 Bash(git:* push) の : はただの文字で、
何にも当たりません。
見た目より重い理由
エージェントを並列で走らせているなら、権限を落とすことが安全側の設計そのものです。 私の場合もそうでした。実装するエージェントは、Issue が宣言したファイルだけ触れる。
それを Write(...) のパス指定で表現していました。
頼っていた仕組みが、一度も参照されないほうでした。
実際に scope を守らせていたのは、Read / Edit のパス指定と、
Issue 本文に書いた宣言と、依頼文の書き方です。
どれも、荷重がかかっている部分として扱っていませんでした。
引いた教訓は「ドキュメントを読め」より狭いです。
何もしていない権限ルールは、効いているルールとまったく同じに見える。 どちらも黙っている。
設定を見る
npx @quintetkit/ccheck
warn .claude/settings.json:14
`Write(src/generated/**)` is accepted but never consulted. File path rules
apply only to `Read(...)` and `Edit(...)`.
why: https://code.claude.com/docs/en/permissions
この検査を作るとき、面白かったのはテストのほうでした。
ドキュメントに載っている文字列そのものを流します。
指摘が出るべき6件と、出てはいけない11件。
Read(./src/**/*.ts) や Bash(ls*) を含みます。
11対6は意図的です。権限ルールは正しい書き方が多いので、 正しいものを「壊れている」と言うほうが起こりやすい失敗で、 誤検出を1件出したら出力全体が読まれなくなります。
5通りの全一覧(見た目と、実際に何をするか)はこちらです。
https://quintetkit.github.io/reference/claude-code-permission-rules.html
Claude Code に設計・実装・レビューを別々の人格として分担させ、GitHub Issue と
ブランチを軸に並列開発を回すための設定一式を MIT で公開しています。
コピーして ./setup.sh を叩けば動きます。技術スタックには依存しません。
https://github.com/quintetkit/quartet
このワークフローだけで実際にツールを 1 つ作りました。Issue の分割から PR、 レビュー、マージまで記録が全部残っています。うまくいかなかった箇所も消していません。
https://github.com/quintetkit/mdlinkcheck
UI 設計人格・レビュー基準・Issue 単位の並列実行スクリプト・実践ガイド 11 章を 足したものは製品ページにあります。
実践ガイド全 11 章は Zenn Book で読めます(¥1,500・2章まで無料)。
https://zenn.dev/quintetkit/books/claude-code-parallel-workflow
設定一式(5人格・スクリプト・ガイド全文)の配布はこちらです。