公開日

Slack に安全にテキストを流す Tips

Claude の返信やコマンドの中身を Slack に投稿するときに踏んだ落とし穴と、その対処を短くまとめた。分割送信でサロゲートペアを切らない、リンクの展開を止める、mrkdwn をメンションの形に限る、ログのトークンを伏せるなど。

はじめに

claude-slack-channel は、Claude の返信や、実行しようとしているコマンドの中身を Slack に投稿する。どちらも自分で書いた文ではないので、中身を信用せずに流すしかない。

この記事は、そのために入れた小さな工夫を Tips として並べる。1 つ 1 つは短い話だが、レビューで AI に指摘されて初めて気付いたものも多いんだよな。指摘はちゃんとコードとテストで確かめてから取り込んでいる。

分割送信でサロゲートペアを切らない

Slack のメッセージには長さの上限があるので、長い返信は複数のメッセージに分けて送る。文字数で機械的に切ると、絵文字などのサロゲートペアの真ん中で切れて、文字が壊れる。

分割の処理は、切る位置が上位サロゲートの直後なら 1 文字戻す。コードブロックの途中で切れるときは、フェンスを閉じてから次のメッセージで開き直す。切ったあとに再び上限を超えないか確かめる安全弁も入れた。

この処理は最初から動いていたが、1 つの関数に詰まっていて読みにくすぎた。関数を 5 つに分けるとき、分割前の実装をテスト用に残し、固定シードで 2,400 ケースの入力に対して出力が一致するのを確かめた。挙動を変えない変更は、こうして変えていないことを示す。

途中で失敗したら、何件送れたかを返す

分割して送る途中で失敗すると、Claude には何件目まで送れたかが分からない。分からないと、Claude が全文を再送して重複する。

1 件以上送れていた場合は、エラーに sent=N を含めて返すようにした。1 件目で失敗したときは、元の例外をそのまま投げる。

See more は全文をコードブロックで分けて送る

実行許可の確認で長い入力を省略したとき、See more ボタンで全文を送る。最初は全文を 1 つのブロックで送っていたので、長いと Slack の上限を超えて送れなかった。

全文をコードブロックで包み、上の分割処理で複数のメッセージに分けて送る形に変えた。中身の行頭に ``` があるとフェンスと誤認されるので、ゼロ幅スペースを前に置いて潰す。送り先も、ボタンが押されたスレッドに揃えた。

リンクの展開を無効にする

Claude の返信や、確認の中身に URL が含まれることがある。Slack がプレビューを展開すると、外部へのアクセスが発生して、中身が意図せず表示される。

すべての chat.postMessage に unfurl_links: false と unfurl_media: false を付けた。テストでは、テキスト・markdown・ブロックの各経路で指定が付くことを確かめている。

mrkdwn はメンションの形に限る

許可・拒否のあと、確認のメッセージを「誰が答えたか」付きの表示に書き換える。回答者をメンションにしたいが、mrkdwn として解釈させる範囲を広げると、Claude 側から来た文字列が書式として効いてしまう。

回答者は、Slack のユーザー ID の形のときだけメンションにし、それ以外は plain_text で出す。確認の ID も plain_text で出す。mrkdwn に渡すのは、形を検証した文字列だけ。

同じ理由で、@channel / @here / @everyone とユーザーグループへのメンションは、送信前に無効にする。

ログのトークンを伏せる

ログには Slack のトークンが混ざる経路がある。最初は xoxb- など 6 種類の接頭辞だけを伏せていたが、それだとリフレッシュトークンの xoxe- のようなものが漏れる。

正規表現を xox[a-z]- に広げた。xox の後ろの 1 文字は種別で、将来増えても英小文字なら拾える。xapp- と Bearer も対象にしている。修正の前に、xoxe- が伏せられないことを示すテストを書き、修正後に通した。

空の text を出さない

ツール名・説明・入力内容のどれかが空の確認が来たとき、Block Kit のブロックに空の text を出していた。空の項目は省き、ブロックの組み立てで空の要素を作らないようにした。

小さな話に見えるが、確認の投稿がどこにも成功しなければ、ブリッジはその場で deny を返す。表示の組み立てが、実行の可否に直結する経路だった。

次回は、Windows のコンソールを外から操作するために書いた PowerShell スクリプトの話。