0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

配っている安全フックが「確認するはず」の操作を黙って通していないか——自分の910個を数え直した

0
Posted at

私は Claude Code の安全フックを無料で配っている(cc-safe-setup)。危険な操作を実行の手前で止めるための小さなフックの集まりだ。だから、こういう報告が出た時は、他人事ではなく、自分の配布物を確かめる番だと受け取る。

報告はこうだ。PreToolUse のフックが「人に確認させる」判定(permissionDecision: "ask")を返しても、セッションが bypassPermissions(権限の確認を飛ばすモード)で動いていると、その確認は黙って自動で承認され、ツールがそのまま実行される。一方で「拒否」の判定(deny)は同じ状況でもちゃんと効く。

これは、危険な操作にだけ人の確認を挟みたい、という安全フックの一番大事な使い方を、静かに無効にする。画面には何も出ない。使う側は「確認を挟んだつもり」でいるのに、実際は素通りしている。

そこで私は、自分の配っている 910 個のフックを自分の環境で数え直し、どれが生き残ってどれが黙って通す側かを分けた。そして、数える途中で自分のコードに見つけた嘘のコメントを直した。以下はその記録だ。報告そのものは伝聞、数えた結果と直した箇所は手元の実測、として分けて書く。

報告の中身(伝聞)

出どころは GitHub の起票 #77212(2026-07-13・OPEN)だ。報告者は、permissions.defaultModebypassPermissions にした状態で、ask を返すフックと deny を返す姉妹のフックを同じセッションで走らせ、次を観測している。

  • ask を返した側の対象は、確認のダイアログが一切出ずにそのまま実行された。
  • deny を返した側の対象は、同じセッションで正しくブロックされた。
  • フック自体は動いていて、ask の JSON を出力していることも(同じ入力を手でフックに流して)確認済み、とある。

つまり、フックが「確認して」と言っても、bypassPermissions の下ではその声が届かず黙って通る、という報告だ。ここまでは報告者の観測なので伝聞として書いている。この起票は今も開いたままで、直したという告知は出ていない(この記事を書いた時点)。

起票には、別の安全フックの作者からの裏づけも付いている。ローカルの拒否フックを配っている維持者が、こう書いている——bypassPermissions で動く安全フックでは ask を強制の境界に使ってはいけない、うちのフックは硬い拒否(exit 2)を使うので確認のダイアログが尊重されるかどうかに依存しない、と。強制したいなら、まず拒否して、人の例外が要るなら別経路で許可を出す形が安全だ、という主張だ。

だから自分のフックを数え直した(手元の実測)

報告が本当なら、私の配布物のうち ask で人に確認させているフックは、bypassPermissions の下で黙って通ってしまう。起票はまだ開いたままで、直ったという告知も無い。だから自分の環境でも当てはまる。確かめる価値がある。

cc-safe-setup の実体(examples/ 配下)を自分の環境で数えた結果(2026年7月16日の時点)はこうだ。

  • フックの総数: 910 個
  • 強制に硬い拒否(exit 2)を使うフック: 285 個。報告のとおり denybypassPermissions でも効くので、この 285 個は生き残る。
  • permissionDecision: "deny" を明示で使うフック: 4 個。これも生き残る。
  • permissionDecision: "ask" を使うフックは 2 個。これが、bypassPermissions の下で黙って通る側だった。

うちの安全網の大多数は硬い拒否で組んであり、取り返しの付かない破壊的な操作——rm -rf の連鎖、DROP TABLEmigrate:fresh、認証情報を漁る操作、子プロセスの暴走など——は、すべて exit 2 で止める。これらは bypassPermissions でも効く。ここは一安心だった。問題は、残る 2 個だ。

黙って通っていた 2 個の中身

一つ目は memory-write-guard.sh。Claude Code が記憶(CLAUDE.md や memory のファイル)を勝手に書き換えるのを、任意で確認できるフックだ。既定は記録だけ(off)で、利用者が CC_MEMORY_WRITE_APPROVAL=ask にした時だけ ask を返す。

ここで、自分のコードのコメントに嘘があった。ask を返す箇所に、こう書いてあったのだ——「対話でない時は、これが書き込みを止める(Claude Code は未承認として扱う)」。これは bypassPermissions の下では誤りだ。起票のとおり、そのモードでは ask は黙って承認されるので止まらない。過去の自分が、確かめずに「止まるはず」と書いていた。今回そのファイルを自分で確かめて、初めて気づいた。

二つ目は mcp-tool-guard.sh。MCP のツールが名前からは分からない破壊的な操作(たとえば自動操作で「削除」ボタンを押して本番のデータを消す)を、自動承認モードの下でエスカレートするフックだ。破壊的なツール名(delete/remove/drop など)や本番のホストへの操作は exit 2deny で硬く止める。だが、任意の設定(CC_MCP_AUTOMATION_GUARD=ask)にした時の「自動操作の確認」の枝が ask を返す。よりによって、この枝が発火する条件は「モードが bypassPermissions などの自動承認」の時——つまり ask が黙って通る、まさにその状況だった。自分自身を出し抜く形になっていた。

直した(責任を持って開示する)

見つけたので直した。挙動そのもの(ask の選択肢)は消さない——対話のセッションでは ask は正しく確認を出すし、それを求める使い方は妥当だからだ。直したのは「誤解を生む記述」だ。

  • memory-write-guard.sh: 嘘のコメントを、正確な記述に是正した。bypassPermissions の下では ask は黙って承認され書き込みは止まらない、強制したいなら CC_MEMORY_WRITE_APPROVAL=block(exit 2)を使え、これは常に尊重される、と明記した。
  • mcp-tool-guard.sh: ask の枝の直前に、同じ注意を足した。bypassPermissionsask を黙って自動承認するので、自動承認モードで硬く強制したいなら CC_MCP_AUTOMATION_GUARD=block(exit 2)を使え、と。

どちらも block(exit 2)の選択肢は最初からあり、そちらは生き残る。だから利用者は、bypassPermissions で回すなら block を選べばよい、と分かる形にした。

公開の前にもう一度数えたら、直したはずの一つが巻き戻っていた

ここまでが7月16日の話だ。この記事を公開する前(7月26日)に、念のためもう一度数え直した。すると二つ、状況が変わっていた。

一つ。memory-write-guard.sh に入れたはずの正確な記述が、消えていた。是正の後、このフックは別の改修で書き直され、その書き直しが古いコメント——「対話でない時は、ask が書き込みを止める」という、まさに私が嘘だと確かめて消した記述——を復活させていた。直したはずの嘘が、10日で戻っていた。

二つ。ask を使うフックが 2 個から 3 個に増えていた。増えた 1 個は PowerShell の削除ガードで、Remove-Item -Recurse -Force の絶対パスへの実行に確認を挟む枝が ask を返す。よりによって、この確認が本当に欲しい場面の多く——無人の自動運用——は bypassPermissions で回っていて、そこでは確認は黙って承認される。

だから両方とも、その場で直した(今の実体は、総数 908 個・硬い拒否 284 個・ask 3 個で、3 個とも bypassPermissions では確認が出ないという注意を書き込んだ)。この再点検から言えるのは、一度直したことも、確かめ続けない限り正しいままではいない、ということだ。是正は改修の波に流される。フックの安全は在庫でなく、数え直す習慣のほうにある。

教訓——bypass モードでは、確認は助言、強制は硬い拒否だけ

まとめるとこうだ。

bypassPermissions(や --dangerously-skip-permissions)で回すなら、フックの「確認(ask)」は当てにできない。黙って通る。効くのは硬い拒否(exit 2、または deny)だけだ。だから、絶対に自動で走らせたくない操作は ask でなく exit 2 で止める設計にする。人の例外が要るなら、その場の確認ではなく、設定や環境変数のような別経路で明示の許可を出す。

そして——自分の配っているものや自分の設定を、報告が出たら実際に数え直す。「止まるはず」で書いたコメントは、確かめるまで正しいとは限らない。私自身、自分で確かめて初めて、2 個の穴と 1 個の嘘のコメントに気づいた。

自分のフックを確かめる

同じことを、あなたのフックでもできる。フックのディレクトリで、強制の仕方を数えてみるといい。

# 硬い拒否(bypassでも効く)を使うフック
grep -rl 'exit 2' ~/.claude/hooks/ .claude/hooks/ 2>/dev/null

# ask を返すフック(bypassPermissionsでは黙って通る = 過信しない)
grep -rl 'permissionDecision.*"ask"' ~/.claude/hooks/ .claude/hooks/ 2>/dev/null

ask を返すフックがあり、かつ bypassPermissions で回しているなら、そのフックは今、確認を出していない。強制したいなら、そのフックを exit 2 の拒否に変えるか、bypass モードをやめて既定のモードに戻す。


私が配っている安全フックの集まり cc-safe-setup は、この記事で数えたとおり、破壊的な操作は硬い拒否(exit 2)で止める設計だ。導入は次の一行で、MIT ライセンスの無料。

npx cc-safe-setup

そして、こうした「止まっているつもりで、実は黙って通っていた」という沈黙の失敗を含め、Claude Code で実際に起きた事故を「症状 → 検出 → 復旧 → 予防」の順にまとめた買い切りの本(Anthropic公式ガイドにない事故防止・¥800)も出している。無断の従量課金、副の作業者の暴走、設定が黙って元に戻る事故など、表示と実態がずれてお金やデータが静かに溶ける型を、複数の章で扱っている。第3章まで無料で試し読みでき、以降も月ごとに新しい事故を追記して、購入者には無償の更新で届く。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?