公開日
実行許可を Slack のボタンで返す:迷ったら deny に倒す設計
Claude Code の実行許可の確認を Slack に中継し、ボタンで答えられるようにした。答えが無いときに allow に倒れる経路を潰す話、ID の検証、見えない文字の可視化、「今後も許可」で作るルールの絞り方を書く。
はじめに
claude-slack-channel の中で一番気を使っているのが、実行許可の扱い。Claude Code は、許可リストに無い操作をする前に確認を出す。ブリッジはこの確認を Slack に中継し、ボタンで答えられるようにしている。
Slack から手元の PC を操作する以上、ここが緩いとマジで何でもできてしまう。設計の方針は 1 つで、判断できないときは deny に倒す。AI と壁打ちしながら経路を洗い出したが、どこを deny にするかは自分で決めた。
答えが無い経路をすべて deny にする
最初の実装では、Slack に確認を出したあと、答えが来るまで保留のままだった。レビューで、答えが来ない経路が 3 つあると分かった。
- 期限切れ。30 分答えが無ければ、Claude に deny を送り、メッセージを「期限切れのため自動で拒否した」表示に書き換える。期限切れのボタンを押しても Claude には送らない
- 配信失敗。スレッドにも、どの DM にも投稿できなかったときは、誰も答えられない。その場で deny を返し、ログに残す
- 終了時。ブリッジが止まるとき、保留中の確認をすべて deny で返す。メッセージの書き換えは、切断する前に間に合った分だけ行う
deny を明示するのは、Claude に「拒否された」という結果を返し、セッションを次に進ませるため。保留のまま放置すると、どちらにも倒れていない状態のまま、後から押されたボタンが効く余地が残る。期限を過ぎた確認は、ブリッジ側で終わらせる。
ID を検証してから Slack に出す
確認には 5 文字の ID が付き、yes xxxxx のように返信で答えられる。この ID は Claude 側から届く値をそのまま使っていた。
想定外の形の ID が届いたときは、Slack に出さずに deny を返すようにした。期待する形は、l を除く英小文字 5 文字だけ。形を 1 か所で定義し、表示と照合の両方で使う。
逆方向の問題もあった。保留中でない ID への yes maybe のような返信まで、回答として扱われていた。これは通常のメッセージとして Claude に届けるように変えた。回答として扱うのは、手元に記録がある ID だけ。
見えない文字を見えるようにする
確認のメッセージには、実行しようとしているコマンドや差分が表示される。ここに向きを変える制御文字やゼロ幅文字が仕込まれていると、見た目と実際の中身が食い違う。
対策はシンプルで、見えない文字を \u{202E} のような表記に置き換えて表示する。対象は、双方向制御文字、ゼロ幅文字、BOM、ソフトハイフン、空白に見える埋め文字、タブと改行を除く制御文字。最初は双方向制御とゼロ幅と BOM だけだったが、翌日に範囲を広げた。
長い入力は先頭と末尾を残し、間を省略する。省略したときだけ「See more で全文を確認すること」の警告を出し、See more ボタンで全文をスレッドに送る。真ん中に危ないものを隠されても、全文を見ればちゃんと分かる。
「今後も許可」のルールを絞る
同じ確認に何度も答えるのはやっぱ手間なので、「今後も許可」ボタンを付けた。押すと、確認の中身からルールを作り、許可リストに足すかをスレッドで確かめる。次の起動から、その操作は確認なしで通る。
ルールは機械的に作る。自由入力は受け付けない。
- Bash / PowerShell は、コマンドの先頭の語をプレフィックスにする。
gitやnpmはサブコマンドまで含める - 複数のコマンドをつないだもの、リダイレクトを含むものはルールにしない
- 削除・移動・ネットワーク・プロセスの起動、任意のコードの実行、
git pushなどの破壊的な操作はルールにしない
入れた日のうちに、抜け道を 1 つ塞いだ。claude のようなエージェントの CLI を危険なコマンドに入れていなかったので、PowerShell(claude:*) のルールが作れてしまい、別のエージェントに確認なしで任意の操作をさせられた。流石にまずい。エージェントの CLI と、npm test のようなスクリプトの実行もルールの対象から外した。スクリプトは、ファイル編集が確認なしだと中身を書き換えてから実行できるから。
deny に当たるものは追加しない。専用の設定ファイルと作業フォルダーの設定の deny と照合し、当たったら理由をスレッドに返す。確認したあと、追加する直前にも照合し直す。
残ること
塞げないものもある。許可ユーザーの Slack アカウントが乗っ取られれば、手元の Claude をそのまま操作される。確認で許可した操作は、その内容どおりに実行される。プロンプトインジェクションの最後の防御は、確認での拒否。
だからこそ、確認の中身を読める状態にしておくことと、答えが無いときに allow に倒れないことに、時間をかけているんだよな。
次回は、Slack に安全にテキストを流すための小さな Tips をまとめる。