playpark
ホーム会社概要サービスソリューションブログお知らせ気軽に相談する
playpark

あらゆる仕事を楽しむ

会社概要サービスソリューション気軽に相談する特定商取引法に基づく表記

© 2019-2026 合同会社playpark All Rights Reserved.

  1. ホーム
  2. ブログ
  3. Claude Code エージェント・安全設計 完全ガイド
  4. 【Claude Code Hooks 安全設計】170超のdenyルールとPreToolUseフックで「やらかし」を防ぐ実践パターン
ブログ一覧に戻る
技術TipsClaude Code エージェント・安全設計 完全ガイド

【Claude Code Hooks 安全設計】170超のdenyルールとPreToolUseフックで「やらかし」を防ぐ実践パターン

Claude Code Hooksのprotect-branches.pyによるブランチ保護、auto-approve設計、170超のdenyルール分類を実コード付きで解説。PreToolUse・PostToolUse・Notificationの使い分けも紹介します。

2026年3月10日32分で読める
Claude CodeClaude Code Hooksセキュリティ設定settings.jsonAI開発環境
【Claude Code Hooks 安全設計】170超のdenyルールとPreToolUseフックで「やらかし」を防ぐ実践パターン

Claude Code Hooks シリーズ

(3記事)
  1. 1.【Claude Code Hooks】テスト自動化で品質を仕組み化 - テスト忘れゼロ件を実現する設定ガイド
  2. 2.【Claude Code Hooks 安全設計】170超のdenyルールとPreToolUseフックで「やらかし」を防ぐ実践パターン
  3. 3.【Claude Code 安全設計】HooksからPermissionsへ — 3つのPythonスクリプトを捨てた理由と移行ガイド

2026-03-07 追記: 本記事で紹介したHooksベースの安全設計は、現在Permissions + deny rulesベースの構成に移行しています。最新の構成については「HooksからPermissionsへ — 3つのPythonスクリプトを捨てた理由と移行ガイド」をご覧ください。

「え、mainにpushされてる...誰が...あ、Claude Codeか」

AIエージェントにコードを書かせるの、もう日常ですよね。でも権限まわりの設計をサボると、ある日突然「本番ブランチに直pushされました」事件が起きる。人間ならgitのブランチルール叩き込まれてるけど、AIはそんな空気読めません(読めたら怖い)。

settings.jsonのpermissions.denyに「git push --forceは禁止ね」と書いておけば安心? いや、AIは賢いからgit push -fとかgit push origin HEAD:refs/heads/mainとか、微妙にパターンを変えてすり抜けてくるんですよ。正確には「すり抜けようとしている」わけじゃなくて、単にバリエーションが多すぎてdenyリストの網目から漏れるだけなんですが。結果は同じ。

「denyリスト書いたのに防げなかった」「毎回の承認ダイアログが多すぎて作業が進まない」...こんな経験、ありませんか?

私たちのdotfilesリポジトリでは、この問題に対して3つの防御レイヤーで対処しています。denyリスト(静的ブロック)、Pythonフック(動的検査)、auto-approveパターン(安全な操作の自動承認)。この記事では、実際に運用しているコードと設計意図を全部見せます。

この記事で学べること

  • settings.jsonのpermissions.denyを170超のルールに体系化する設計手法
  • protect-branches.pyの正規表現・refspecパース・gh API連携の仕組み
  • auto-approveフックで「安全な操作」だけを自動承認するパターン
  • PreToolUse / SessionStart / Notification、4種のHookの使い分け

前提知識

  • Claude Codeの基本操作を理解している
  • settings.jsonのhooks設定について概要を把握している
  • gitのrefspec記法(src:dst)を知っていると理解が早い

あわせて読みたい

AIエージェントが夜中にコードを巡回・修正する「night-patrol」の設計と実践【Claude Code Skills】
技術Tips18分

AIエージェントが夜中にコードを巡回・修正する「night-patrol」の設計と実践【Claude Code Skills】

AIエージェントが深夜にコードベースを自律巡回し、lint警告・テスト失敗・未対応issueを検出→修正→PRまで自動化する「night-patrol」スキルの設計パターンと安全策を解説。

読む

全体アーキテクチャ:3層の防御ライン

まず全体像から。Claude Codeのツール実行に対して、3つのレイヤーで安全性を確保しています。

レイヤー仕組み役割
L1: 静的denypermissions.denyパターンマッチで即座にブロック
L2: 動的フックPreToolUse + Pythonコマンド内容を解析して判定
L3: 自動承認PreToolUseのallow応答安全な操作の承認ダイアログを省略

L1で大半の危険操作を止め、L2でL1の隙間を埋め、L3で安全な操作のUXを改善する。「止めるべきものは確実に止め、通すべきものはスムーズに通す」 がコンセプトです。

あわせて読みたい

AIエージェントが同じミスを繰り返さない3つの設計パターン【Claude Code実装例】
技術Tips34分

AIエージェントが同じミスを繰り返さない3つの設計パターン【Claude Code実装例】

Claude Codeが同じエラーを繰り返す? 構造化ログ・8軸パターン検出・SKILL.md自動修正の3パターンで解決。CLAUDE.mdへの3行追記から始められる実装例付き。

読む

L1:170超のdenyルール設計

なぜこんなに多いのか

「git push --forceを禁止」だけで済むと思いますよね。ところがClaude Codeは以下のようなバリエーションでpushしてくることがある。

git push --force origin main        # 明示的なforce push
git push -f origin feature           # 短縮フラグ
git push origin HEAD:refs/heads/main # refspecで直接指定
git push --set-upstream origin main  # upstream設定と同時にpush
git push --force-with-lease origin HEAD:refs/heads/dev # 安全そうに見えて保護ブランチ宛て
git push origin main:main           # ブランチ名:ブランチ名形式
git push origin --delete main       # ブランチ削除

1つのdenyルールでは1パターンしかブロックできません。全バリエーションをカバーするには、組み合わせ爆発に付き合うしかないんです。

7カテゴリの分類体系

170超のルールを整理するために、以下の7カテゴリに分類しています。

1. Git危険操作(force push・保護ブランチ)

最も重要なカテゴリ。保護ブランチ(main, master, dev, develop, development)への全pushパターンを網羅します。

"Bash(git push --force:*)",
"Bash(git push -f:*)",
"Bash(git push origin HEAD:refs/heads/main)",
"Bash(git push --set-upstream origin main)",
"Bash(git push --force-with-lease origin HEAD:refs/heads/main)",
"Bash(git push origin main:main)",
"Bash(git push origin --delete main)",

保護ブランチ5種 x pushパターン7種 = 35ルール。ここだけでルール数の2割を占めます(几帳面すぎ?いや、事故った時のダメージを考えたら安い)。

2. Git破壊的操作

"Bash(git reset --hard:*)",
"Bash(git clean -f:*)",
"Bash(git clean -fd:*)",
"Bash(git checkout -- .:*)",
"Bash(git restore .:*)",
"Bash(git branch -D:*)",

git reset --hardとgit cleanの組み合わせで未コミットの変更が全部消える。AIが「きれいにしておきますね」と善意でやってくれることがあるので、確実にブロックします。

3. GitHub CLI / API操作

"Bash(gh repo delete:*)",
"Bash(gh pr merge:*)",
"Bash(gh api --method DELETE:*)",
"Bash(gh api -X POST:*)",
"Bash(gh api graphql:*)",

gh CLIの読み取り操作(gh pr view、gh issue list)は許可しつつ、変更・削除系のAPIメソッドはブロック。MCP経由の操作も同様に制御します。

"mcp__gh__merge_pull_request",
"mcp__gh__delete_repo",
"mcp__gh__delete_ref",
"mcp__gh__secret_set",

MCPツール名はmcp__gh__プレフィックスで識別できるので、ツール名そのものをdenyリストに入れます。

4. ファイル破壊・権限変更

"Bash(rm -rf:*)",
"Bash(rm -fr:*)",
"Bash(rm -r -f:*)",
"Bash(/bin/rm -rf:*)",
"Bash(/bin/rm -r:*)",
"Bash(chmod -R:*)",
"Bash(chown -R:*)",

ここで注目してほしいのが/bin/rmのパターン。シェルエイリアスでrmを安全な削除コマンド(ripなど)に置き換えている場合、AIが「エイリアスが効かないのでフルパスで実行します」と/bin/rm -rfを叩いてくることがある。エイリアスのバイパスも想定して塞ぐのがポイントです。

5. ネットワーク・パッケージ管理

"Bash(curl:*)",
"Bash(wget:*)",
"Bash(ssh:*)",
"Bash(nc :*)",
"Bash(brew:*)",
"Bash(pip install:*)",
"Bash(npm publish:*)",

curlやwgetは開発中に使いたい場面もありますが、AIが勝手に外部からスクリプトをダウンロードして実行する可能性がある。確認ダイアログを出すprompt相当にしたいところですが、denyリストは即座にブロックなので、必要な場合はpermissions.allowで個別に穴を開ける運用にしています。

6. システム・OS操作

"Bash(sudo:*)",
"Bash(reboot:*)",
"Bash(shutdown:*)",
"Bash(defaults write:*)",
"Bash(diskutil:*)",
"Bash(launchctl unload:*)",
"Bash(security find-generic-password:*)",
"Bash(security dump-keychain:*)",

macOS固有のコマンドも入っています。defaults writeでシステム設定を変えたり、securityコマンドでキーチェーンにアクセスしたり。開発ツールの設定中に「ちょっと最適化しておきますね」とAIがシステム設定を弄り始める事故は、実際に起きます(体験談)。

7. 機密ファイルの読み取り保護

"Read(./.env)",
"Read(./.env.*)",
"Read(./.ssh/**)",
"Read(./.gnupg/**)",
"Read(./.aws/**)",
"Read(./.config/gh/**)",

denyリストはBash実行だけでなく、Claude CodeのReadツールにも適用できます。.envにAPIキーが入っている場合、AIにそれを読ませるとコンテキストウィンドウに載ってしまう。読ませないこと自体が防御です。

L2:Pythonフックによる動的検査

denyリストはパターンの完全一致なので、どうしても漏れが出ます。そこでPreToolUseフックのPythonスクリプトが動的にコマンドを解析して判定します。

protect-branches.py:3段構えのブランチ保護

最も重要なフックがこれ。80行ほどのPythonで3つの検査を行います。

検査1:gh pr mergeの宛先チェック

PROTECTED = {"main", "master", "dev", "develop", "development"}

if re.match(r"^(?:env\s+\S+=\S+\s+)*gh\s+pr\s+merge\b", norm):
    result = subprocess.run(
        ["gh", "pr", "view", "--json", "baseRefName", "-q", ".baseRefName"],
        capture_output=True, text=True, timeout=10,
    )
    base = result.stdout.strip()
    if base in PROTECTED:
        deny(f"Blocked: merging into protected branch {base}")

gh pr mergeが実行されようとしたら、そのPRのマージ先ブランチをgh APIで確認する。denyリストでは「どのブランチにマージしようとしているか」までは判定できないので、実際にAPIを叩いて動的に判定しています。

ポイントはenv \S+=\S+のプレフィックスも許容していること。AIがenv GH_TOKEN=xxx gh pr mergeのように環境変数付きで実行するケースにも対応します。

検査2:保護ブランチからのpush検出

result = subprocess.run(
    ["git", "rev-parse", "--abbrev-ref", "HEAD"],
    capture_output=True, text=True, timeout=5,
)
br = result.stdout.strip()
if br in PROTECTED:
    deny(f"Blocked: pushing from protected branch {br}")

現在のブランチが保護ブランチなら、pushそのものをブロック。feature branchからのpushは通す。

検査3:refspecの宛先パース

ここが一番複雑で、一番重要な部分です。

parts = norm.split()
# "push"キーワードの位置を特定
i = max(j for j, p in enumerate(parts) if p == "push")
# オプションフラグをスキップしてリモート名を飛ばし、refspecを収集
j = i + 1
while j < len(parts) and parts[j].startswith("-"):
    j += 1
j += 1  # skip remote
refspecs = []
while j < len(parts):
    if parts[j].startswith("-"):
        j += 1
        continue
    refspecs.append(parts[j])
    j += 1

for rs in refspecs:
    rs = rs.lstrip("+")       # force prefixを除去
    if ":" in rs:
        _, dst = rs.split(":", 1)  # src:dst の dst を取得
    else:
        dst = rs
    if dst.startswith("refs/heads/"):
        dst = dst.rsplit("/", 1)[-1]  # refs/heads/main → main
    if dst in PROTECTED:
        deny(f"Blocked: pushing to protected branch {dst}")

refspecはsrc:dst形式で「ローカルのsrcブランチをリモートのdstブランチにpushする」という指定。+プレフィックスはforce pushを意味します。このパーサーは以下のパターンを全部検出します。

git push origin feature:main           # feature を main に push
git push origin +feature:main          # force push で main に
git push origin HEAD:refs/heads/main   # HEADを main に
git push -u origin feature:dev         # upstream 設定付きで dev に

denyリストだけではgit push origin feature:mainのようなローカルブランチ名が可変のパターンに対応できません。このPythonパーサーがあることで、denyリストの「網目」を補完しています。

auto-approve-git-safe.py:安全なgit操作の自動承認

denyリストとprotect-branches.pyで危険を止めた上で、今度は安全な操作のUXを改善します。

SAFE_SUB = re.compile(
    r"^git\s+(?:add\b.*|commit\b.*|status\b.*|restore\s+--staged\b.*)$"
)

# チェーンされたgitコマンドも全部安全なら自動承認
parts = re.split(r"\s*(?:&&|\|\||;)\s*", norm)
if parts and all(SAFE_SUB.match(p) for p in parts if p):
    allow("auto-approve safe git sequence")

git add && git commit -m "..."のようなチェーンコマンドを&&、||、;で分割し、全パートが安全なコマンドなら自動承認。1つでも安全リストに含まれないコマンドが混じっていたら承認しません。

git commitはさらに個別の正規表現でもカバーしています。

if re.search(
    r"(^|\s)(env\s+\S+=\S+\s+)*git(\s+-c\s+\S+=\S+)*\s+commit(\s+(-m|-F)\b|\b)",
    norm,
):
    allow("auto-approve git commit")

env GIT_AUTHOR_NAME=xxx git -c user.email=xxx commit -m "msg"のような環境変数・config付きコミットにも対応。Claude Codeがco-author情報を付けてコミットするケースを想定しています。

auto-approve-gh-mcp.py:MCPツールの読み取り操作を自動承認

DANGEROUS = re.compile(r"(merge|delete|transfer|archive|secret|token|ref|workflow)")

if name.startswith("mcp__gh__") and not DANGEROUS.search(name):
    allow("auto-approve safe gh MCP tool")

gh MCP経由のツール呼び出しで、ツール名に破壊的キーワードが含まれていなければ自動承認。mcp__gh__list_issuesやmcp__gh__get_pull_requestは通して、mcp__gh__merge_pull_requestやmcp__gh__delete_refはブロック。シンプルだけど効果的。

あわせてチェック

15分で課題を整理しませんか?

記事の内容について質問や相談があれば、営業トークなしの15分ヒアリングで整理します。

15分ヒアリングを予約気軽に相談する

Hookイベントの使い分けガイド

settings.jsonで使用可能な4種のHookイベントと、それぞれの適切な使い所を整理します。

PreToolUse:ツール実行前の門番

"PreToolUse": [
  {
    "matcher": "Bash",
    "hooks": [{
      "type": "command",
      "command": "python3 \"$HOME/.claude/hooks/protect-branches.py\""
    }]
  }
]

使い所:

  • コマンドの内容を検査して許可/拒否を判定
  • 安全な操作の自動承認でUXを向上
  • matcherでツール名を絞り込み、不要な発火を防止

matcherには前方一致で"Bash"や"mcp__gh__"を指定できます。全ツールに発火させたい場合は空文字""を使いますが、パフォーマンスへの影響を考えて絞り込む方がベターです。

SessionStart:セッション開始時のコンテキスト注入

"SessionStart": [
  {
    "matcher": "compact",
    "hooks": [{
      "type": "command",
      "command": "echo 'Context compacted. Reminder: Read CLAUDE.md for project context. Run git status before making changes.'"
    }]
  }
]

使い所:

  • トークン圧縮後のリマインダー注入
  • セッション固有の環境情報の提供
  • matcher: "compact"でコンテキスト圧縮時のみ発火

長い会話でトークン圧縮(compact)が走ると、冒頭のCLAUDE.mdの内容が薄まることがあります。compactのタイミングで「CLAUDE.md読み直してね」とリマインドすることで、会話が長くなっても品質を維持できます。

Notification:人間への通知

"Notification": [
  {
    "matcher": "",
    "hooks": [{
      "type": "command",
      "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
    }]
  }
]

使い所:

  • 承認待ちなどでAIが止まった時のデスクトップ通知
  • macOSのosascript、Linuxならnotify-sendを使用
  • matcher: ""で全通知に対応

Claude Codeを裏で走らせてると、承認ダイアログで止まってることに気づかない。数分間放置してから「あ、待ってたの?」 となる。Notification Hookでデスクトップ通知を飛ばせば、その無駄な待ち時間がなくなります(地味だけど効果抜群)。

実践Tips:deny設計で陥りがちな罠

罠1:ワイルドカードの:*を忘れる

// NG: 完全一致のみブロック
"Bash(git push --force)",

// OK: 後続の引数も含めてブロック
"Bash(git push --force:*)",

:*をつけないと「git push --force」という完全一致のみブロックし、「git push --force origin main」はすり抜けます。ほぼ全てのdenyルールに:*が必要です。

罠2:bare pushの扱い

// これをdenyに入れると feature branchのpushも全部止まる
"Bash(git push:*)",

// 代わりにbare pushだけを狙い撃ち
"Bash(git push)",
"Bash(git push origin)",

:*なしの完全一致で「引数なしのgit push」だけをブロックし、git push -u origin feature/xxxは通す。ワイルドカードの有無で挙動が大きく変わるので注意。

罠3:allowとdenyの優先順位

"permissions": {
  "allow": ["Bash", "mcp__*"],
  "deny": ["mcp__gh__merge_pull_request"]
}

Claude Codeではdenyが常にallowより優先されます。allowでmcp__*を全許可しても、denyに入っている特定ツールはブロックされる。この仕様を理解した上で、allowは広めに取ってdenyで穴を塞ぐ設計がお勧めです。

Nixで設定をコード管理する

ここまでの設定を手動で管理するのは現実的じゃない。私たちはNix(home-manager)でsettings.jsonとhookスクリプトをdotfilesリポジトリから自動デプロイしています。

# home-manager activation script(抜粋)
# settings.json へのシンボリックリンク
ln -sf "$DOTFILES_CLAUDE/settings.json" "$CLAUDE_DIR/settings.json"

# hooks ディレクトリ内のスクリプトへのシンボリックリンク
if [ -d "$DOTFILES_CLAUDE/hooks" ]; then
  mkdir -p "$CLAUDE_DIR/hooks"
  for f in "$DOTFILES_CLAUDE"/hooks/*.py; do
    target="$CLAUDE_DIR/hooks/$(basename "$f")"
    ln -sf "$f" "$target"
  done
fi

nix run .#update一発で、settings.jsonもhookスクリプトも最新の状態にデプロイされる。新しいマシンでも同じセキュリティ設定が再現されます。dotfilesでのNix管理については別記事で詳しく紹介しています。

このアプローチの良いところは、セキュリティルールの変更がGitの履歴で追跡できること。「いつ、誰が、どのルールを追加/変更したか」が全部残ります。170超のルールをメンテナンスするには、この追跡可能性が欠かせません。

まとめ

Claude Code Hooksの安全設計は、「止めるべきものを確実に止め、通すべきものをスムーズに通す」の一言に尽きます。

  • L1の静的denyで170超のパターンを即座にブロック
  • L2のPythonフックでrefspecパースやAPI連携による動的検査
  • L3の自動承認で安全な操作のUXを改善

どれか1つでは穴がある。3つを組み合わせて初めて「AIに任せても安心」な環境ができます。settings.jsonは1回書いたら終わりじゃなくて、新しいパターンを発見するたびに育てていくもの。170ルールは多く見えますが、それぞれに「こういう事故があったから追加した」という理由がある。AIエージェント時代の安全設計は、地道なパターン潰しの積み重ねです。

なお、本記事で紹介したHooks + Pythonスクリプトによる安全設計は、その後Permissions + deny rulesベースの構成に移行しました。移行の経緯と判断理由については「HooksからPermissionsへ — 3つのPythonスクリプトを捨てた理由と移行ガイド」で詳しく解説しています。

あわせて読みたい

  • 【2026年版】AIコーディングツール完全比較 — Claude Code・Codex・Antigravityの選び方
  • 【Claude Code Hooks】テスト自動化で品質を仕組み化 — テスト忘れゼロ件を実現する設定ガイド

Claude Code エージェント・安全設計 完全ガイド — この記事を含む10本の記事で、エージェント活用・Hooks安全設計・並列開発を体系的に解説しています。

4 / 10 記事
前の記事
【Claude Code Hooks】テスト自動化で品質を仕組み化 - テスト忘れゼロ件を実現する設定ガイド
次の記事
【Claude Code 安全設計】HooksからPermissionsへ — 3つのPythonスクリプトを捨てた理由と移行ガイド
シリーズ一覧を見る
Claude Code Hooks シリーズ — 2 / 3 記事
前の記事
【Claude Code Hooks】テスト自動化で品質を仕組み化 - テスト忘れゼロ件を実現する設定ガイド
次の記事
【Claude Code 安全設計】HooksからPermissionsへ — 3つのPythonスクリプトを捨てた理由と移行ガイド

この技術が解決した業務課題

記事の技術が実際のプロジェクトでどう活かされているかをご紹介します

【自社導入事例】ブログ運用を完全自動化 - GitHubリポジトリから記事・サムネイル・SNS投稿まで

「ブログ書くのしんどい」「SNS投稿めんどくさい」を解決。GitHubリポジトリから記事生成、サムネイル作成、SNS投稿文まで自動化した、playpark自身の導入事例を紹介します。

事例を読む

【AI採用管理】Gemini API × Next.jsで書類選考を自動化した受託開発事例

Gemini APIとNext.jsで採用書類選考を自動化。中小企業の採用DXをplayparkが受託開発で支援した事例をご紹介します。

事例を読む

【AIシフト管理】AIが提案して店長が決める - 「全自動が不安」なサロンのためのシフト最適化

「AIに全部任せるのは怖い」という店長さんへ。Shift Budの「AI提案→人が判断」ワークフローなら、AIの提案と差分を確認してから採用できます。完全自動化ではなく、人の判断を残したシフト管理の新しいかたちを紹介します。

事例を読む
AI開発の導入支援

Claude CodeやAIコーディングツールの導入・カスタマイズでお困りですか?playparkでは、AI開発環境の構築から運用まで、実践に基づいた技術支援を行っています。

サービス
AI開発について相談する
ブログ一覧に戻る

関連記事

すべての記事
【Claude Code 安全設計】HooksからPermissionsへ — 3つのPythonスクリプトを捨てた理由と移行ガイド
技術Tips
2026年3月13日26分で読める
【Claude Code 安全設計】HooksからPermissionsへ — 3つのPythonスクリプトを捨てた理由と移行ガイド

Claude Codeの安全設計をHooksベース(Pythonスクリプト3本)からPermissions+deny rulesベースに移行した経緯・判断理由を解説。Before/After比較と移行判断フローチャート付き。

Claude CodeClaude Code Hooksセキュリティ設定+3
【Claude Code】settings.json × CLAUDE.md 完全ガイド — 設定ファイルの使い分けと実践テンプレート
実験レポート
2026年2月17日50分で読める
【Claude Code】settings.json × CLAUDE.md 完全ガイド — 設定ファイルの使い分けと実践テンプレート

Claude Codeのsettings.json設定項目一覧・CLAUDE.mdとの使い分け早見表・hooks/permissionsによる事故防止パターンを網羅。コピペで使えるテンプレート付きで、settings.jsonの書き方からdotfiles管理まで実践的に解説。

Claude CodeClaude Code カスタマイズsettings.json+6
【実測比較】Claude Code vs Codex 料金・性能 — 実プロジェクトで使い比べてわかった選び方
技術Tips
2026年3月16日21分で読める
【実測比較】Claude Code vs Codex 料金・性能 — 実プロジェクトで使い比べてわかった選び方

Claude Code vs Codex徹底比較【2026年・比較表付き】実プロジェクトで使い比べた実測データで、開発速度・品質・料金・拡張性の4軸から最適な選び方を解説。

Claude CodeCodexAIコーディングツール+3

この技術、実際の現場ではこう使われています

記事で紹介した技術が、実際のビジネス課題をどう解決したか。導入事例で具体的なイメージをつかめます。

導入事例を見る気軽に相談する