Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions dot_claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
- 一時ファイルは `/tmp` 直下ではなくスクラッチパッド(`$TMPDIR`)に置くこと。`/tmp` はサンドボックスが書き込みを拒否する
- `git` と `gh` はサンドボックス外で走る扱いのため、ビルドやテストと同じコマンドに混ぜないこと。`go test ./... && git commit ...` のような複合はコマンド全体が昇格対象になり、`gh` をループやパイプの中で呼ぶと自分の設定ファイルを読めずに失敗する
- 破壊的な git 操作(force push、`reset --hard`、`clean -f`、履歴改変)は `bash-guard.sh` フックが拒否する。回避せず、必要なら人手での実行を提案すること
- ループバック接続(`localhost` / `127.0.0.1`)とローカルの listen は `sandbox.network.allowLocalBinding` で許可済み。統合テスト、E2E、dev サーバ、Playwright、Go の `httptest`、`curl http://localhost:*` はサンドボックス内で走るので escape を付けないこと
- `git` `gh` `docker` などは `sandbox.excludedCommands` により既にサンドボックス外で走る。これらに `dangerouslyDisableSandbox` を付けても実行結果は変わらず、承認プロンプトだけが増えるので付けないこと
- 承認プロンプトを伴うコマンド(`dangerouslyDisableSandbox` 付き、`docker exec` / `docker run` / `docker compose exec` などの変更系)では、`description` に日本語で次の4点を書くこと。ユーザはこの説明だけを見て可否を判断する
- 目的:何のために実行するのか
- 副作用:書き込むパス、触る DB とテーブル、接続先ホスト、削除や上書きの有無
- 可逆性:元に戻せるか。戻し方、またはバックアップの場所
- escape の理由:`dangerouslyDisableSandbox` を付ける場合、サンドボックス内で何がどう失敗したか
ヒアドキュメントで渡すスクリプトは、コマンド文字列を読ませて理解させるのではなく、`description` 側で何をするスクリプトかを説明すること

## 文章執筆

Expand Down
23 changes: 22 additions & 1 deletion dot_claude/hooks/bash-guard.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ input=$(cat)
cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // empty') ||
emit ask "フック入力の解析に失敗しました"
escape=$(printf '%s' "$input" | jq -r '.tool_input.dangerouslyDisableSandbox // false')
desc=$(printf '%s' "$input" | jq -r '.tool_input.description // ""')

# ヒアドキュメントの本文は実行されるコマンドではないので検査対象から外す。
# 含めたままにすると、コミットメッセージやドキュメントに書いた
Expand Down Expand Up @@ -79,9 +80,29 @@ if git_sub filter-repo || git_sub filter-branch; then
fi
gh_sub 'repo[[:space:]]+delete' && emit deny "リポジトリの削除は禁止です"

# ---- deny: 承認を求める前に説明を書かせる ----
# 承認プロンプトに出るのは description だけで、ヒアドキュメントで渡す長い
# スクリプトはユーザが読んで判断できる形になっていない。説明が薄いまま承認を
# 求めるのを止める。ask ではなく deny なのは、deny の理由だけがモデルに返り、
# 説明を書き直して再提示させられるため。ユーザに丸投げしない。
#
# サンドボックス内で完結するコマンドは自動許可されプロンプトが出ないので、
# ここは実際に承認を要する経路(escape と docker の変更系)だけを対象にする。
needs_detail=false
[ "$escape" = "true" ] && needs_detail=true
has '(^|[^[:alnum:]_-])docker[[:space:]]+(exec|run|cp|rm|rmi|build|buildx|volume|network|push|kill|stop)([^[:alnum:]_-]|$)' &&
needs_detail=true
has '(^|[^[:alnum:]_-])docker[[:space:]]+compose[[:space:]]+([a-z-]+[[:space:]]+)*(up|down|exec|run|restart|rm|kill|stop)([^[:alnum:]_-]|$)' &&
needs_detail=true

# 80 バイト=日本語で約 27 文字。目的・副作用・可逆性を書けばまず超える。
if [ "$needs_detail" = true ] && [ "${#desc}" -lt 80 ]; then
emit deny "承認が必要なコマンドですが description が短すぎて可否を判断できません(${#desc} バイト)。日本語で「目的/副作用(書き込むパス・触る DB とテーブル・接続先ホスト・削除や上書きの有無)/元に戻せるか/サンドボックス外で実行する理由」を書いた description を付けて再提示してください。ヒアドキュメントの中身も description 側で説明してください"
fi

# ---- ask: サンドボックス外実行 ----
[ "$escape" = "true" ] &&
emit ask "サンドボックス外実行には承認が必要です。一時ファイルは /tmp ではなく \$TMPDIR を使い、git/gh はビルドやテストと同じコマンドに混ぜないでください"
emit ask "サンドボックス外実行には承認が必要です(説明: ${desc})。ループバック接続とローカル listen は allowLocalBinding で許可済みなので、それが理由なら escape は不要です"

# ---- ask: 認証情報・書き込み系 API ----
gh_sub 'auth[[:space:]]+token' && emit ask "認証トークンの取り出しには承認が必要です"
Expand Down
Loading
Loading