deny は Bash コマンドを止めるものだと思っていました。
{
"permissions": {
"deny": [
"Bash(rm -rf *)"
]
}
}
ファイル単位でも書けます。
"Read(.env)",
"Read(.env.*)"
自分の設定にはこれが入っていました。入れたのは自分ですが、効いているか確かめたことがありませんでした。
確かめたら、止まるところと止まらないところがはっきり分かれました。
止まった
プロジェクト直下にダミーを置いて、開こうとしました。中身は DUMMY_KEY=this-is-a-fake-value-for-testing だけです。
File is in a directory that is denied by your permission settings.
読めません。 ここまでは期待どおりです。
面白いのは次で、cat でも読めませんでした。
$ cat .env.denytest
Permission to use Bash with command cat ... has been denied.
Read を止めたら Bash から回り込める、と思っていたのですが、そうはなりませんでした。 Bash 側も同じパスで弾かれます。ここは思っていたより硬いです。
止まらなかった①:消せる
同じファイルを、消してみました。
$ rm .env.denytest
(消えた)
読めないファイルが、消せます。
自分はこれが一番まずいと思いました。.env を守っているつもりで守れているのは「中身を見られること」だけで、「無くなること」は守っていません。
.env は Git に入れていないことが多いので、消えたら手元にしか無かった値が消えます。読まれるより困る場合があります。
(rm 自体は別途 Bash(rm -f *) などで止めていますが、それは .env を守る設定ではなく、rm を止める設定です。別の話として書いておかないと、守れている気になります。)
止まらなかった②:書ける
書き込みも通りました。
$ printf 'x\n' > .env.denytest
(作成された)
Read(...) は読みの設定なので、これは仕様として自然です。ただ「.env を deny した」と言うとき、書き換えも含んでいると思いがちなので、一応。
止まらなかった③:プロジェクトの外
ここが自分には一番の見落としでした。
別のディレクトリに置いた .env を、絶対パスで開いてみました。
/private/tmp/.../scratchpad/.env
→ 読めた
Read(.env) はプロジェクトからの相対で効いています。 外にあるものは守りません。
普段1つのプロジェクトだけで動かしているなら問題になりません。複数のディレクトリに触れる状態で動かしているなら、穴になります。 自分は1つの親フォルダの下に案件を並べているので、まさにこれでした。
外も守るなら、パスを明示して足すことになります。
"Read(//Users/you/projects/other-project/.env)",
"Read(//Users/you/projects/other-project/.env.*)"
書いた分しか守りません。
Edit も書ける
読みだけでなく、書き換えも止められます。自分がいま一番使っているのはこれです。
"Edit(//Users/you/projects/another-project/**)"
複数の案件を1台で回していると、別の案件のファイルを触られるのが一番こわいところです。指示で「触らないで」と書くより、設定で止めたほうが確実でした。
CLAUDE.md に「担当外のディレクトリを変更しない」と書いてはいますが、文章はお願いで、設定は仕組みです。 両方あっていいと思っています。
まとめ
-
denyは Bash だけでなくRead()Edit()も書ける -
Readを止めるとcatも止まる(思ったより硬い) - 読めないファイルが消せる。 守っているのは中身だけ
- プロジェクトの外は守らない。 パスを書いた分だけ
- 複数案件を1台で回すなら
Edit()でディレクトリごと止めるのが効く
自分は文系の大学生で、プログラミングは得意ではありません。設定を書いた時点で守れた気になっていたのですが、入れたものが本当に効いているかは、確かめないと分からないという、いつもの結論になりました。
確かめていないことは、確かめていないと書きました。