Claude CodeのReadルールのパス起点 — /pathと//pathの違い
Read・Editルールのパスは//path・~/path・/path・./pathで起点が変わります。ユーザー設定に/secrets/**と書くと~/.claude/secretsを指す落とし穴と書き分けを示します。
Read(/secrets/**)をユーザー設定に書くと、守られるのはプロジェクトのsecretsではなく~/.claude/secretsです。ルールが有効なのに秘密ファイルが読めてしまう原因の多くは、このパス記法の起点の取り違えにあります。
Read・Editルールのパスには4つの書き方があり、先頭が//か~/か/か./かで基準になる場所が変わります。しかも/pathは、ルールを書いた設定ファイルによって指す場所が動きます。
4つの記法は何を起点にするのか
ReadとEditのルールはgitignoreのパターン構文で書き、先頭の形で起点が決まります。
| 書き方 | 起点 | 例 | 実際に指す場所 |
|---|---|---|---|
//path | 起点ファイルシステムのルート | 例Read(//Users/alice/secrets/**) | 実際に指す場所/Users/alice/secrets/** |
~/path | 起点ホームディレクトリ | 例Read(~/Documents/*.pdf) | 実際に指す場所/Users/alice/Documents/*.pdf |
/path | 起点ルールを書いた設定ファイル | 例Edit(/src/**/*.ts) | 実際に指す場所プロジェクト設定なら作業ディレクトリ配下のsrc |
path・./path | 起点現在のディレクトリ | 例Read(*.env) | 実際に指す場所カレント配下の*.env |
見落としやすいのは2点です。/Users/alice/fileのように書いても絶対パスにはなりません。先頭のスラッシュ1本は設定ファイルの側を起点にするので、絶対パスは//Users/alice/fileと書きます。
もう1点は、ルールが起点より外のファイルには効かないことです。Read(.env)が塞ぐのは「カレント配下の.env」で、親ディレクトリや別プロジェクトの.envは対象になりません。
/pathは書いた場所で行き先が変わる
/pathの起点は、ルールを書いた設定ファイルごとに決まります。
| ルールを書いた場所 | /pathの行き先 |
|---|---|
プロジェクト設定(.claude/settings.json) | /pathの行き先作業ディレクトリのpath |
ローカル設定(.claude/settings.local.json) | /pathの行き先作業ディレクトリのpath |
ユーザー設定(~/.claude/settings.json) | /pathの行き先~/.claude/path |
--settings <file>で渡したファイル | /pathの行き先そのファイルがあるディレクトリのpath |
| CLIフラグ・セッション中のルール | /pathの行き先作業ディレクトリのpath |
同じEdit(/src/**)でも、プロジェクト設定なら自分のリポジトリのsrcを指します。ユーザー設定に置けば~/.claude/srcになります。/permissionsから追加したルールは、保存先に選んだ設定ファイルの行に従います。
ローカル設定の/pathは、リポジトリのルートではなくセッションの作業ディレクトリが起点です。worktreeのセッションでは、共有のルールEdit(/src/**)がそのworktree自身のsrcに当たります。
ユーザー設定の/pathで外す落とし穴
次の設定を~/.claude/settings.jsonに書いたとします。
{
"permissions": {
"deny": ["Read(/secrets/**)"]
}
}意図は「どのプロジェクトのsecretsも読ませない」でしょう。実際に塞がれるのは~/.claude/secrets/**だけで、プロジェクトのsecretsはそのままです。/permissionsの一覧ではルールが有効なまま表示されるため、行き先のずれに気づきにくい形です。
落とし穴
ユーザー設定の/pathはどのプロジェクトにも届かない
ユーザー設定の/secrets/**は~/.claude/secretsを指します。全プロジェクトに効かせたいなら//の絶対パスか~/のホーム相対パスで書き、プロジェクトごとのsecretsならプロジェクト設定の./secrets/**かsecrets/**にします。
直し方は目的で分かれます。
ユーザー設定に書くときの直し方
どのプロジェクトでも守りたい
ホーム配下の固定の場所ならRead(~/.aws/**)、ルート起点の固定パスならRead(//etc/**)と書きます。
名前が同じ場所を各プロジェクトで守りたい
Read(.env)やRead(**/.env)のように、起点を持たない形にします。deny側の相対パターンは、カレント配下のどの深さでも当たります。
.envのような裸のファイル名はgitignoreの規則で任意の深さに当たるため、Read(.env)とRead(**/.env)は同じ意味です。マシン上のどこにある.envも塞ぎたいときは、Read(//**/.env)と書きます。ファイルシステム全体が対象になります。
./secrets/とsecrets/は同じか
./pathと素のpathは、どちらも現在のディレクトリが起点で同じです。違いが出るのは、先頭が単一のディレクトリ名のパターン(src/**など)を、allowとdenyのどちらに置くかです。
- allowルール:
Edit(src/**)は<cwd>/srcとその配下だけに当たる - deny・askルール:
Read(secrets/**)は、カレント配下のどの深さのsecretsにも当たる
つまりRead(secrets/**)と書くと、vendor/pkg/secrets/のような入れ子のコピーまで塞がります。これを避けて直下だけに絞りたいなら、Read(/secrets/**)をプロジェクト設定に書くか、Read(./secrets/**)にします。
プロジェクト内にsrcが2か所あるときの当たり方を並べると、差がはっきりします。
| ルール | src/app.ts | vendor/pkg/src/lib.js |
|---|---|---|
Edit(src/**)をallowに置く | src/app.ts当たる | vendor/pkg/src/lib.js当たらない |
Edit(src/**)をdeny・askに置く | src/app.ts当たる | vendor/pkg/src/lib.js当たる |
Edit(/src/**)(どの種類でも) | src/app.ts当たる | vendor/pkg/src/lib.js当たらない |
Edit(**/src/**)(どの種類でも) | src/app.ts当たる | vendor/pkg/src/lib.js当たる |
深さを揃えたいなら、先頭に/を付けるか**/を付けて、意図を書き手の側で固定するのが確実です。
書いたルールが効いているかを確かめる
起点を取り違えたルールは、意図と別の場所を塞ぎます。確かめる手順は次のとおりです。
ルールの起点を確認する流れ
- 1
書いた場所を確かめる
ルールが
~/.claude/settings.json、.claude/settings.json、.claude/settings.local.jsonのどれにあるかを確認します。/pathはここで行き先が決まります。 - 2
行き先を言葉にする
上の表で
/pathを実パスに置き換え、「このルールはどこのファイルを塞ぐのか」を1文で書き出します。 - 3
対象ファイルを読ませてみる
守りたいファイルを実際に読ませ、拒否されるかを見ます。拒否されなければ起点がずれています。
/permissionsコマンドを使うと、保存先の設定ファイルを選んでルールを追加できます。画面の操作はClaude Codeの/permissionsコマンドにまとめています。
起点が正しくても残る穴
記法が正しくても、Readのdenyルールは万能ではありません。denyルールが効くのは、Claudeの組み込みファイルツール、cat・head・tail・sed・teeのようにBashで認識できるコマンド、> fileや< fileのリダイレクト先です。ファイル名を書かずに読むコマンド(カレントでgrep -r pattern .を実行するなど)や、PythonやNodeのスクリプトが自分でファイルを開く場合は対象外です。
evalなどを経由して拒否ルールを迂回される問題はClaude Codeの拒否ルールをeval等ですり抜けられる問題と対策で扱っています。OSレベルで全プロセスの読み取りを止める方法は、サンドボックスの設定が受け持ちます。sandbox.filesystem.denyReadで認証情報ファイルを読ませないに手順があります。
Readのdenyルールは同じパスのEditとWriteも塞ぐ仕様です。ただしNotebookEditは対象外なので、ノートブックを含むパスにはEditの拒否も併記します。エラーの読み方は「Read deny rule」エラーの対処にあります。
否定パターンは同じ設定ファイルの中だけで効く
denyやaskのパターンを!で始めると、gitignoreの否定として働き、直前に並べたpath・./path系のルールから一部を除外できます。
{
"permissions": {
"deny": ["Read(*.env)", "Read(!sample.env)"]
}
}この例は、sample.envを除くすべての*.envを任意の深さで塞ぎます。!のルールを先に書くと、何も除外されません。
効く範囲には制限があります。
- 除外が届くのは同じ設定ファイルのルールだけ。プロジェクト設定の
Read(!.env)では、管理設定や別ファイルのRead(./.env)は解除されない !のパターンは、直後が/・~/・//でも現在のディレクトリ基準で読まれる。Read(!~/notes/public/**)はRead(~/notes/**)から何も除外しない- ディレクトリ全体を塞ぐルールの中は、再び開けられない。
Read(secrets/**)とRead(!secrets/public/**)を並べても、secrets/publicは塞がれたまま
記法の起点とあわせて覚えておくと、「例外を足したつもりで効いていない」状況を避けられます。
複数の設定ファイルに書いたときの優先順位と作業ディレクトリ
起点を直しても、置き場所の選び方で効き方は変わります。拒否ルールは、どの設定スコープにあっても許可ルールより先に評価されます。ユーザー設定で許可していてもプロジェクト設定で拒否されていればブロックされ、逆にユーザー設定の拒否はプロジェクト設定の許可を上書きします。管理設定の拒否は、コマンドライン引数でも覆せません。
起点が「作業ディレクトリ」になるルールは、セッションの主たる作業ディレクトリを基準にします。この作業ディレクトリは、/cdで別のディレクトリへ移すまで起動したディレクトリのままです。--add-dirや/add-dir、additionalDirectoriesで足したディレクトリも同じ権限ルールに従います。足したディレクトリは起点にはならず、/pathの行き先は主たる作業ディレクトリのままです。
シンボリックリンクにも注意が要ります。リンク経由のパスは、要求されたパスと解決先の2つが検査されます。許可ルールは両方が一致したときだけ働き、拒否ルールはどちらかが一致すれば働きます。たとえば./project/**を許可し~/.ssh/**を拒否していても、./project/keyが~/.ssh/id_rsaを指していれば読み取りはブロックされます。
macOSとLinuxでは、シンボリックリンクのディレクトリ越しに書いた//・~/・/の拒否ルールが、実体の場所にも効きます。macOSで/etcが/private/etcに解決される環境では、Read(//etc/**)が/private/etc/hostsも塞ぎます。
ツール名にも落とし穴がある:Write・Glob・NotebookEditのパスルール
パスの起点とは別に、ルールの左側のツール名でも空振りが起きます。ファイルのパス検査はEdit(path)とRead(path)のルールだけを見ます。Write・NotebookEdit・Glob、そして旧来のMultiEditにパス付きのルールを書いても、Claude Codeはルールを受け付けるだけで参照しません。起動時に警告が出ます。
書き換えは次のとおりです。
| 書いたルール | 書き直す先 |
|---|---|
Write(docs/**) | 書き直す先Edit(docs/**) |
NotebookEdit(docs/**) | 書き直す先Edit(docs/**) |
MultiEdit(docs/**) | 書き直す先Edit(docs/**) |
Glob(docs/**) | 書き直す先Read(docs/**) |
パスを付けないツール名だけのルール(Writeの拒否など)は、ツール単位でどこでも一致するため警告されません。この警告はv2.1.210以降で出ます。起点の取り違えと同じく、構文は通るのに守れていない型の失敗で、起動時の警告が手がかりになります。この症状はファイルのパス検査に一致しないというエラー文言として表に出ることもあります。
Windowsと記号を含むパス
Windowsではパスが正規化されてからマッチします。C:\Users\aliceは/c/Users/aliceになるので、.envをそのドライブ全体から塞ぐなら//c/**/.env、全ドライブなら//**/.envと書きます。
「Yes, and don't ask again」で承認したパスは、[・]・*のようなgitignoreの記号が自動でエスケープされ、リテラルのパスだけに当たるルールになります。手で書いたルールはエスケープされません。括弧はEdit(./Finance (2024)/**)のようにそのまま書けます。
パス記法の選び方
最後に、場面ごとの書き方を並べます。
| 守りたいもの | 書く場所 | 記法の例 |
|---|---|---|
プロジェクト直下のsecrets | 書く場所プロジェクト設定 | 記法の例Read(./secrets/**) |
どの深さの.envも | 書く場所どこでも | 記法の例Read(.env) |
| ホームの認証情報 | 書く場所ユーザー設定 | 記法の例Read(~/.aws/**) |
| 固定の絶対パス | 書く場所どこでも | 記法の例Read(//etc/**) |
ユーザー設定に書くときは、先頭が/だけの記法を避けるのが無難です。迷ったら、書いたルールを1文で言葉にして、行き先がホームの.claudeになっていないかを見てください。