Claude Media
DirectoryAddedフックで/add-dir追加後のディレクトリを監査して取り消す

DirectoryAddedフックで/add-dir追加後のディレクトリを監査して取り消す

/add-dirでディレクトリが追加された直後に発火するDirectoryAddedフック。追加そのものはブロックできない仕様と、取り消しが届く範囲・届かない範囲を解説します。

DirectoryAddedフックとは

DirectoryAddedは、セッションの途中で/add-dirコマンドを実行してディレクトリを追加した直後、またはSDKクライアントがregister_repo_rootコントロールリクエストでディレクトリを登録した直後に発火するフックです。新しく増えたリポジトリの依存関係を入れておく、といった準備処理に使います。

まず押さえておきたいのは、このフックが発火しないケースです。起動時の--add-dirフラグで渡したディレクトリは対象外で、そちらはSessionStartの管轄になります。/permissionsのWorkspaceタブから追加した場合も発火しません。すでに作業ディレクトリになっているパスを再追加しようとした場合は、追加自体がエラーで失敗するのでフックも動きません。3つとも「ディレクトリが新しく増える」という結果は同じでも、経路が違うだけでこのフックの対象から外れる点は覚えておく価値があります。

もう一つ重要なのは、Claude Codeがこのフックの完了を待たないという点です。追加処理はフックの実行を待たずにその場で完了し、フックは既定600秒のタイムアウトでバックグラウンドで走ります。サンドボックスと権限の状態は、フックが起動する前にすでに更新済みなので、サンドボックス化されたツールから見ても新しいディレクトリはこの時点で存在しています。加えて、フックコマンド自体はサンドボックス化されずに実行される点も公式ドキュメントが明記しています。DirectoryAddedのスクリプトはサンドボックスの外で動くコードとして書く必要があります。

このイベントが追加されたのはv2.1.219です。同じリリースでOpus 5が既定のOpusモデルになるなど大きな変更が並んだ回の一部として、比較的地味に追加されました。地味な追加ではありますが、セッション途中でディレクトリを広げる操作に監査の手が届くようになったという意味では、/permissionsコマンドや許可ルールの運用と並べて覚えておく価値があります。

「アクセスを制御する」の実態 — ブロックではなく取り消し

DirectoryAddedにはdecision controlがありません。追加はフックが起動する前に完了しているので、そもそも止める対象が無いというのがClaude Codeの立場です。「承認直後にアクセス制御する」と聞くとブロックを想像しがちですが、このフックでできるのは追加を止めることではなく、条件に合わない追加を検知して事後に取り消すことです。

ここで注意したいのは、additionalDirectoriesという設定キーが2つの別経路から書き込まれることです。公式ドキュメントによれば、/add-dirはあくまで「現在のセッション中のファイルアクセスを追加する」コマンドで、permissions.additionalDirectoriesに設定ファイルとして列挙したエントリとは別の経路として扱われています。フックスクリプトから.claude/settings.local.jsonadditionalDirectoriesを書き換えて取り消せるのは、/add-dir --rememberなどで設定ファイルに永続化されたエントリに限られると考えるのが安全です。素の/add-dirで増えたセッション限りのアクセスまで、この書き換えで確実に失効させられるとは公式ドキュメントから読み取れません。

見落としやすいのは、このフックが無くてもファイル単位のアクセス制御自体は動いているという点です。追加ディレクトリ内のファイルも、通常の作業ディレクトリと同じRead/Editの許可ルールに従います。機微なパスを恒久的に塞ぎたいなら、DirectoryAddedでの事後の設定書き換えに頼るより、denyルールでそのパスを最初から塞いでおくほうが確実です。DirectoryAddedは「何が追加されたかを検知して記録・通知する」監査層として位置付け、恒久的な遮断は事前のdenyルールに任せるのが、公式ドキュメントの範囲で言い切れる運用です。settings.jsonでのpermissions設計を先に固めておくと、このフックは監査に専念できます。

この二層構造を理解しておくと、フックに求める役割を過大評価せずに済みます。許可ルールは同期的かつ確実に評価される一方、フックはバックグラウンドで動く監査の網で、しかも取り消しが届く範囲は設定ファイルに永続化されたエントリに限られます。両者は代替ではなく補完の関係で、恒久的な防御は許可ルール側に置くべきです。

v2.1.234で/add-dirがターン中でも使えるようになった

v2.1.234より前、Claudeが応答している最中に/add-dirを実行すると、コマンドはターンが終わるまでキューに入って待たされていました。このバージョン以降は、作業中でもその場で確認ダイアログが出て、承認すれば同じターン内の次のツール呼び出しから新しいディレクトリへアクセスできます。

この変更はDirectoryAddedフックの設計に直接影響します。フックはバックグラウンドで動き、Claude Codeはその完了を待ちません。ターン中の追加が即座に有効になる以上、Claudeがそのディレクトリのファイルを読みに行くタイミングと、監査フックが取り消し処理を終えるタイミングのどちらが先になるかは保証されません。取り消しを前提にした設計では、フックが完了するまでの短いwindowで最低限のアクセスが発生しうることを織り込んでおく必要があります。

追加ディレクトリが一緒に持ち込む設定

/add-dir--add-dirで追加したディレクトリは、ファイルアクセスを広げるだけでなく、一部の設定も自動で読み込みます。permissions.additionalDirectoriesにリストしただけのディレクトリはファイルアクセスのみで、この対象にはなりません。

設定の種類追加ディレクトリからの読み込み
.claude/skills/のSkills追加ディレクトリからの読み込み読み込む(ライブリロードあり)
.claude/commands/のコマンド追加ディレクトリからの読み込み読み込む(ライブリロードなし)
.claude/agents/のSub-agents追加ディレクトリからの読み込み読み込む(ライブリロードなし)
settings.json/settings.local.json追加ディレクトリからの読み込みenabledPluginsextraKnownMarketplacesのみ
CLAUDE.md.claude/rules/追加ディレクトリからの読み込み環境変数CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1を設定した場合のみ

これは監査の設計に直結します。追加したディレクトリにSkillsが置かれていれば、ライブリロード付きでそのまま読み込まれます。信頼できないリポジトリを/add-dirする運用があるなら、DirectoryAddedフックで.claude/skills/.claude/commands/の有無をチェックし、想定外のファイルが入っていたら即座に取り消してアラートを出すのが実用的な防御線です。

レシピ: 許可リスト外のディレクトリを検知して通知・取り消す

ワークスペースのルート配下しか許可しない、という単純なポリシーを実装します。ただし前節のとおり、.claude/settings.local.jsonの書き換えで確実に失効させられるのは/add-dir --rememberなどで永続化されたエントリだけです。素の/add-dirによるセッション限りの追加まで含めて恒久的に遮断したいなら、このレシピの通知部分だけを使い、遮断自体は事前のdenyルールで行ってください。

#!/bin/bash
# DirectoryAdded: 許可されたルート配下以外は取り消す
input=$(cat)
dir=$(echo "$input" | jq -r '.directory')
source=$(echo "$input" | jq -r '.source')
allowed_root="$HOME/workspace"
 
case "$dir" in
  "$allowed_root"/*) exit 0 ;;
esac
 
settings_file="$CLAUDE_PROJECT_DIR/.claude/settings.local.json"
tmp=$(mktemp)
jq --arg d "$dir" '.permissions.additionalDirectories -= [$d]' "$settings_file" > "$tmp" \
  && mv "$tmp" "$settings_file"
 
curl -s -X POST "$SLACK_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -d "$(jq -nc --arg text "⚠️ 許可外ディレクトリの追加を検知し取り消しました: $dir ($source)" '{text: $text}')"
 
exit 0

exit 0で終えているのは、このフックにdecision controlが無いためです。exit codeを変えても追加の可否には影響しません。スクリプト内のjqによる設定書き換えは、additionalDirectoriesに永続化されたエントリが対象になるところまでしか公式ドキュメントから保証されないため、恒久的な遮断が目的ならdenyルール側を主、この書き換えと通知を補助と位置付けてください。

matcher — slash_commandregister_repo_root

matcherはディレクトリがどう追加されたかで絞り込めます。

matcher発火条件
slash_command発火条件ユーザーが/add-dirを実行
register_repo_root発火条件SDKクライアントがregister_repo_rootコントロールリクエストで追加

出力の届き先もsourceで変わります。slash_command経由なら、フックのsystemMessageは次の会話ターンでClaudeへコンテキストとして渡され、失敗したフックの件数だけがトランスクリプトに表示されます。詳細な失敗内容はデバッグログ止まりです。register_repo_root経由の場合はsystemMessageも失敗内容もすべてデバッグログにしか残らず、ユーザーには見えません。SDK経由の追加を監査したいなら、通知はフックスクリプト側で完結させる必要があります。

よくあるつまずき

  • exit 2で追加をブロックできると考える: 追加はフック起動前にすでに完了しているので、exit codeは何も変えません。取り消したいなら設定ファイルの書き換えで対応します
  • --add-dir起動フラグでも発火すると思い込む: 起動時に渡したディレクトリはSessionStartの管轄で、DirectoryAddedの対象外です
  • register_repo_root経由の失敗に気づけない: このsourceではsystemMessageも失敗理由もデバッグログにしか出ません。SDK統合では自前の通知が必須です
  • 追加ディレクトリの設定はすべて信頼済みだと思い込む: 読み込まれるのはSkills・コマンド・Sub-agentsと一部の設定キーだけです。CLAUDE.mdはデフォルトでは読み込まれません
  • フックの完了を待ってからClaudeがアクセスすると期待する: フックはバックグラウンド実行で、Claude Codeは完了を待たずに次の処理へ進みます

まとめ

DirectoryAddedは追加そのものを止める仕組みではなく、追加された事実を検知して事後に対応するための入口です。許可リスト外のディレクトリを検知して通知し、設定に永続化されたエントリなら取り消しまで自動化しておけば、/add-dirの運用をブロックせずに監視できます。ただし恒久的な遮断まで任せられる範囲は限られるので、機微なパスの防御はdenyルールに置き、DirectoryAddedはその上に重ねる監査層として位置付けるのが実態に合っています。

複数人でClaude Codeを使う環境では、誰がいつどのディレクトリを追加したかのログが残ること自体にも価値があります。取り消しの是非を判断するロジックを組む前に、まず全件をログへ書き出すだけの構成から始めても、監査証跡としては十分に機能します。

よくある質問

DirectoryAddedで追加自体を拒否できますか

できません。このフックにはdecision controlが無く、フックが起動する時点で追加はすでに完了しています。条件に合わない追加を防ぎたい場合は、追加後に設定ファイルから取り消す形で対応します。

--add-dir起動時に指定したディレクトリでも発火しますか

発火しません。起動時に渡したディレクトリはSessionStartが扱う領域で、DirectoryAddedはセッション途中の追加だけを対象にしています。

追加したディレクトリのSkillsは自動で読み込まれますか

読み込まれます。しかもライブリロード付きなので、追加後にそのディレクトリへSkillsが足されると、セッション中でも即座に反映されます。信頼できないディレクトリを追加する運用では、この点が最大の注意点です。

取り消したディレクトリへのアクセスはいつ止まりますか

additionalDirectoriesに永続化されたエントリであれば、設定ファイルの書き換えが反映された時点からです。ただし/add-dirは本来セッション中のファイルアクセスを追加するコマンドで、permissions.additionalDirectoriesへの静的な列挙とは別経路だと公式ドキュメントは説明しています。素の/add-dirによるセッション限りの追加まで、この設定書き換えだけで確実に止められるとは限らないので、恒久的に遮断したいパスは事前のdenyルールで塞いでおくのが確実です。

サンドボックスの権限はいつ更新されますか

フックが起動する前です。Claude Codeはサンドボックスと権限の状態を先に更新してからDirectoryAddedを発火させるので、フック実行中にサンドボックス化されたツールもすでに新しいディレクトリを認識しています。

.claude/settings.jsonに書いたadditionalDirectoriesでも発火しますか

発火しません。設定ファイルへ静的に列挙したadditionalDirectoriesはファイルアクセスの許可だけを行う経路で、/add-dirregister_repo_rootのようなセッション途中のイベントを経由しないため、DirectoryAddedの対象外です。

この記事を共有:XはてブLinkedIn