1
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?

Claude Code の git ガード hook、危険な git コマンド30パターン中15個は素通りでした

1
Posted at

最終更新: 2026年11月8日

Skillselion の git スキルガイドでインストール数1位になっている git-guardrails-claude-code の hook を、2026年11月8日に手元で検証しました。危険系として用意した30パターンのうち、止まったのは15個です。git -C . push やスペース2つの git push、git clean -xfd は終了コード0で通りました。jq が入っていない環境では git push も通ります。

私たちは Claude Code などのスキルを実際のインストール数で並べるディレクトリ Skillselion を運営しています。この記事は、そのデータで1位だった git スキルを自分たちで叩いてみた記録です。

git-guardrails-claude-code は何をするスキルですか?

mattpocock/skills に入っているスキルで、git-guardrails-claude-code スキルの掲載ページでは 542,705 インストールです(2026年11月8日時点、skills.sh 集計)。リポジトリ mattpocock/skills のスター数は同日 318,023 でした。

中身は PreToolUse hook の設定手順と、block-dangerous-git.sh という25行のシェルスクリプトです。Claude Code が Bash ツールを呼ぶ直前に、コマンド文字列を JSON で受け取り、危険なパターンに当たれば終了コード2で止めます。Claude Code の hooks ドキュメントでは、PreToolUse の終了コード2は「Blocks the tool call」と書かれています。

スクリプトの判定部分はこれだけです(upstream のスクリプトから、2026年11月8日取得)。

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')

DANGEROUS_PATTERNS=(
  "git push"
  "git reset --hard"
  "git clean -fd"
  "git clean -f"
  "git branch -D"
  "git checkout \."
  "git restore \."
  "push --force"
  "reset --hard"
)

grep -qE で部分一致を見るだけなので、文字列の形が少しずれると当たりません。SKILL.md 側は「git push (all variants including --force)」をブロックすると書いていますが、実測では全部の書き方は止まりませんでした。

どうやって測りましたか?

upstream の最新版を gh api で取得し、SKILL.md の検証手順と同じ形の JSON({"tool_input":{"command":"..."}})をパイプで流して終了コードを記録しました。スクリプト自体の最終変更は 2026年4月28日のコミット 62f43a1817 です。

jq -cn --arg c "git -C . push origin main" '{tool_input:{command:$c}}' \
  | ./block-dangerous-git.sh
echo $?   # 0 なら通過、2 ならブロック

環境は macOS、bash 3.2.57、jq 1.8.1 です。Claude Code のセッション内で実行したわけではなく、スクリプト単体の挙動を見ています。

どのコマンドが hook を素通りしましたか?

危険系30パターンのうち、元のスクリプトで終了コード0(通過)になった15個です。右の列は後半で紹介する強化版の結果です。

コマンド 元の hook 強化版
git -C . push origin main 0(通過) 2
git -c push.default=current push origin main 0 2
git 'push' origin main 0 2
git push origin main(スペース2つ) 0 2
git<TAB>push origin main(タブ区切り) 0 2
git clean -xfd 0 2
git clean -dfx 0 2
git clean --force -d 0 2
git branch --delete --force feature 0 2
git checkout -- . 0 2
git checkout -f main 0 2
git stash drop 0 2
git stash clear 0 2
git reflog expire --expire=now --all 0 2
git config alias.p push 0 2

原因は3種類に分けられます。

  1. git とサブコマンドの間に何か挟まると「git push」という連続文字列ではなくなる(-C .、-c、クォート、スペース2つ、タブ)
  2. オプションの並び順や長い形式が登録パターンと違う(-xfd、--force -d、--delete --force)
  3. そもそもパターンに入っていない(checkout -- .、checkout -f、stash drop/clear、reflog expire、エイリアス作成)

git -C <パス> は worktree を複数使うときに出てくる書き方です。なお git -C . reset --hard HEAD~1 は止まりました。汎用パターン reset --hard が別に登録されているからです。push には同じ保険がありません。

逆に、ちゃんと止まった15個はこちらです。

元の hook で終了コード2になったコマンド(15個)
  • git push origin main / git push --force-with-lease / git push origin HEAD / git push origin HEAD --force-with-lease
  • /usr/bin/git push origin main / command git push origin main / cd /tmp && git push / bash -c "git push"
  • git reset --hard HEAD~1 / git -C . reset --hard HEAD~1
  • git clean -fd / git clean -f
  • git branch -D feature / git checkout . / git restore .

部分一致なので、&& でつないだものや bash -c で包んだものは拾えています。git commit -m "docs: never git push from CI" のように、コミットメッセージに「git push」と書いただけでも止まります(誤検知)。

jq が入っていないとどうなりますか?

全部通ります。PATH に cat、grep、tr だけを置いた環境で流した結果です。

$ printf '{"tool_input":{"command":"git push origin main"}}' \
    | env -i PATH=./nojq-bin /bin/bash block-dangerous-git.sh
block-dangerous-git.sh: line 4: jq: command not found
$ echo $?
0

git push origin main、git reset --hard HEAD~1、git clean -fd の3つとも終了コード0でした。jq が無いと COMMAND が空文字になり、どのパターンにも当たらずに最後の exit 0 へ進みます。

ここで注意したいのは、Claude Code の onFailure: "block" でも救えない点です。hooks ドキュメントによると onFailure が拾う「失敗」は、起動できない、0と2以外の終了コード、タイムアウトなどです。このスクリプトは正常に終了コード0を返すので、Claude Code から見ると「問題なし」です。

Claude Code の permissions の deny ルールで代わりになりますか?

半分だけです。settings.json の permissions.deny に Bash(git push *) を書けば、標準的な git push origin main は止まります。ただし Claude Code の permissions ドキュメント自身が、Bash(git push *) は git -C . push origin main、git -c push.default=current push origin main、git 'push' origin main を止めない、と表で明記しています。hook の素通りパターンとほぼ同じです。

ドキュメントは、コマンド文字列を自前のロジックで検査したいなら PreToolUse hook を使うよう案内しています。deny ルールは hook が壊れたときの下支え、hook は書き方の揺れを拾う役、と分けて両方入れるのが現実的です。もう一つ、allow に Bash(git *) を入れていると、hook が止めず deny にも当たらなかった書き方は、確認なしで実行されます。

実際にどう固めればいいですか?

3つの手を重ねます。

1. hook の冒頭で jq を確認し、無ければ止める

if ! command -v jq >/dev/null 2>&1; then
  echo "BLOCKED: jq が見つからないため git ガードがコマンドを検査できません。" >&2
  exit 2
fi

jq の入れ方は macOS なら brew install jq、Ubuntu や WSL なら sudo apt install jq です。この1ブロックを足した強化版は、jq の無い環境で3コマンドとも終了コード2になりました。

2. パターンを「git とサブコマンドの間に何かあっても当たる」形にする

空白をまとめてクォートを外してから、git と危険なサブコマンドの間に -C <パス>、-c <設定>、--no-pager のようなオプションだけを許す正規表現で照合します。オプション以外の単語を挟めないようにしているので、コミットメッセージに push と書いただけでは止まりません。

COMMAND=$(jq -r '.tool_input.command // empty')
NORM=$(printf '%s' "$COMMAND" | tr -s '[:space:]' ' ' | tr -d "'\"")

G='(^|[;&|( /])git( (-C|-c) [^ ]+| --?[a-zA-Z][a-zA-Z-]*(=[^ ]*)?)* '
DANGEROUS_PATTERNS=(
  "${G}push( |$)"
  "${G}reset --hard"
  "${G}clean (-[a-zA-Z]*f|--force)"
  "${G}branch (-D|--delete --force|-d --force|-df|-fd)"
  "${G}checkout (-- )?\.( |$)"
  "${G}checkout (-f|--force)( |$)"
  "${G}restore \.( |$)"
  "${G}stash (drop|clear)"
  "${G}reflog expire"
  "${G}config [^;&|]*alias\."
)

for pattern in "${DANGEROUS_PATTERNS[@]}"; do
  if printf '%s' "$NORM" | grep -qE "$pattern"; then
    echo "BLOCKED: '$COMMAND' matches '$pattern'. The user has prevented you from doing this." >&2
    exit 2
  fi
done
exit 0

同じ38コマンド(危険系30、それ以外7、誤検知確認1)で比べると、強化版は危険系30個すべてで終了コード2を返しました。git status、git log --oneline -5、git diff --stat、git restore src/、git rebase -i origin/main、gh pr create ... は元のスクリプトと同じく通過です。

トレードオフもあります。git -C "/tmp/my repo" push のように、スペースを含むパスをクォートで渡すと強化版でも通ります。また、コミットメッセージの中に「git push」と続けて書いた場合は、元の hook と同じく強化版でも止まります。git worktree add、git branch -d(小文字)、git stash list、git pull --rebase は通過を確認しました。

3. settings.json に deny と onFailure を足す

{
  "permissions": {
    "deny": [
      "Bash(git push *)",
      "Bash(git reset --hard *)",
      "Bash(git clean *)",
      "Bash(git branch -D *)",
      "Bash(git stash drop *)",
      "Bash(git stash clear *)",
      "Bash(git reflog expire *)"
    ]
  },
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous-git.sh",
            "onFailure": "block"
          }
        ]
      }
    ]
  }
}

onFailure: "block" は Claude Code v2.1.295 以降で使えます。スクリプトのパス間違いや chmod +x 忘れで hook が起動できないとき、未設定だと Bash コマンドはそのまま実行されます(hooks ドキュメントでは non-blocking error 扱い)。push を Claude に任せつつ毎回確認したい場合は、Bash(git push *) を deny ではなく ask に置き、hook の push パターンを外す選択肢もあります。

エイリアスは hook では追いきれません。git p のように既存のエイリアス経由だと、元の hook も強化版も通過しました。git config --get-regexp '^alias\.' で、push や reset を指すエイリアスが無いか一度確認しておくと安心です。

ほかの git スキルと組み合わせるときの注意は?

git スキルガイドで紹介しているほかのスキルも、upstream の SKILL.md を読んで hook との相性を確認しました。インストール数は各掲載ページの2026年11月8日時点の値です。用途別の比較は Claude Code 向け git スキルガイドにまとめています。

  • caveman-commit スキル(429,597 インストール、juliusbrussee/caveman): コミットメッセージを書くだけのスキルです。SKILL.md に「Does not run git commit, does not stage files, does not amend.」とあり、hook と衝突しません。件名は50文字以内を目安に、上限72文字です。パック全体は caveman スキルの解説ガイドにまとめています。
  • using-git-worktrees スキル(228,418 インストール、obra/superpowers): 既存の分離を確認し、ネイティブの worktree 機能を優先、最後に git worktree add へ落ちる順番です。git worktree add は元の hook でも強化版でも止まりません。
  • create-pull-request スキル(813 インストール、cline/cline): 現行の SKILL.md は git push origin HEAD で push し、gh pr create --body-file /tmp/pr-body.md で PR を作ります。この git push origin HEAD は git-guardrails で終了コード2になりました。両方入れると push の手順で止まるので、push だけ自分で打つか、前述の ask 運用にします。gh pr create 自体は通過です。
  • git-commit スキル(48,232 インストール、github/awesome-copilot)と graphite スキル(9,991 インストール、withgraphite/agent-skills)は、コミット形式とスタック PR 向けです。graphite の gt submit --no-interactive は文字列に git push を含まないため、元の hook でも強化版でも終了コード0でした。文字列で照合する hook は gt 経由の push を止めない、と覚えておいてください。

開発ツール系スキル全体は開発ツール向けスキルのおすすめランキングと開発ツールスキルのカテゴリ一覧、hook 以外の守りはセキュリティスキルのカテゴリ一覧で探せます。

よくある質問

git-guardrails-claude-code は入れる価値がありますか?

あります。標準的な git push、git reset --hard、git clean -fd は実測で止まりました。ただ文字列の部分一致なので、書き方の揺れと jq 未導入には弱いです。入れたら jq の確認とパターン追加までやって完成と考えてください。

deny ルールだけで十分ですか?

十分ではありません。Claude Code の permissions ドキュメントが、Bash(git push *) は git -C . push などを止めないと明記しています。deny は hook が動かないときの下支えとして併用します。

自分の hook が効いているかはどう確認しますか?

この記事と同じく、jq -cn --arg c "<コマンド>" '{tool_input:{command:$c}}' | <スクリプト> を流して echo $? を見ます。2なら止まり、0なら通ります。git -C . push と、jq を外した状態の2つは必ず試してください。

出典: Skillselion の git スキルガイド(インストール数は skills.sh、GitHub、MCP レジストリから毎日更新している実インストール数のランキング)。hook の実測値は2026年11月8日に私たちが計測したものです。

1
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
1
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?