Use Cases (更新: 2026/6/7)

Claude Code 権限はデプロイ前のどこで点検する? CIで機械的に止めるゲート設計

Claude Codeの権限点検は「いつ」やるかで事故率が変わる。デプロイ前のゲートをCIに組み込み、危険な操作を機械的に止める手順を、コピペで動く設定つきで紹介します。

Claude Code 権限はデプロイ前のどこで点検する? CIで機械的に止めるゲート設計

金曜の夕方、僕は「ステージングに上げといて」とだけ頼みました。月曜に出社したら、Claude Code は確かに上げていた。ステージングに、ではなく本番のブランチに、です。

幸いデプロイ自体は走っていませんでした。理由は単純で、本番デプロイのワークフローに「人間の承認待ち」が一段あっただけ。あの一段がなかったら、と思うと今でも背筋が寒くなります。

このとき痛感したのは、権限を「どう設定するか」より、その点検を「いつ」やるかのほうが事故率に効くということです。設定がどれだけ正しくても、確認が公開ボタンの後ろにあったら手遅れになる。だから今日は、権限点検を「デプロイ前のどのタイミングに、どんなゲートとして置くか」に絞って書きます。

この記事の要点

  • 権限点検は「設定の話」ではなくタイミングとゲートの話。確認を公開ボタンの前に機械的に置けるかで事故率が決まる。
  • 点検すべき瞬間は4つ。依頼を渡す前・編集の直後・デプロイ直前・権限を広げる直前。それぞれゲートの形が違う。
  • 人間の目視チェックは忙しい日に必ず破綻する。CIで機械的に止めるゲートを一つ置くだけで夜中の確認が激減する。
  • 何を点検するかの項目リストは 権限監査チェックリスト へ、settings.json の書き方そのものは 権限設定リファレンス へ。この記事は「いつ・どう止めるか」に専念する。
  • deny -> ask -> allow の評価順を知っておくと、CIで一行 deny を足すだけで危険操作を確実に塞げる。

なぜ「設定」より「タイミング」なのか

権限の解説記事はたいてい「settings.json にこう書く」で終わります。それも要るんですが、現場で事故るのは設定が間違っていたからではありません。正しい設定を、確認すべき瞬間に確認していなかったから事故ります。

たとえば僕の金曜の件。ブランチ名を取り違えるミスは設定では防げません。防げるのは「本番デプロイの前に、対象ブランチと差分を人間が見る一段」を置いていたかどうか。これは設定値ではなく、ワークフローのどこにゲートを刺すかという設計の問題です。

Claude Code の公式ドキュメントにも、権限ルールはモデルではなく Claude Code 本体が強制するものだと明記されています。プロンプトや CLAUDE.md は「やろうとすること」を変えるだけで、「許されること」は変えない、と。だからこそ、止めたい操作はプロンプトでお願いするのではなく、ゲートで物理的に塞ぐ必要があるわけです。

順番で考えると分かりやすい。証拠を集める → 変更範囲を狭くする → 確認コマンドを決める → 危険操作にゲートを置く → 通ったら次に進む。この流れのうち、点検が効くのは特定の4つの瞬間だけです。

点検すべき4つの瞬間

権限点検を「常に気をつける」だと続きません。4つの瞬間にだけゲートを置くと決めると、ぐっと運用しやすくなります。

瞬間何を点検するかゲートの形
依頼を渡す前Claude Code に何を許し、何を承認制にするかsettings.json の allow/ask/deny を確認
編集の直後触ってはいけないファイルに手が入っていないかPreToolUse フック / 差分レビュー
デプロイ直前ビルド・差分・rollback 手順が揃っているかCI ジョブ + 人間承認
権限を広げる直前その allow 昇格に検証の裏付けがあるか昇格条件のチェック

この4つを混ぜないのがコツです。とくに「デプロイ直前」と「権限を広げる直前」は性質が違う。前者は今回の一回を安全に出すための点検、後者は次回以降のために常設ルールを緩める判断です。後者を急ぐと、一回うまくいっただけの操作を恒久 allow にして、後から効いてくる事故の種を撒きます。

各瞬間で「何を」チェックするかの細目は 権限監査チェックリスト にまとめてあります。この記事では「いつ・どう止めるか」に集中します。

瞬間1: 依頼を渡す前 — deny を先に置く

一番効くのに一番忘れられるのが、ここです。作業を始める前に、危険操作を deny に入れておく。Claude Code はルールを deny -> ask -> allow の順で評価し、最初に一致したものが勝ちます。つまり deny は何より優先される。だから「絶対やられたくないこと」を deny に書けば、後続の allow がどうあろうと塞がります。

最低限こう書いておくと、僕の金曜の事故は起きません。

{
  "permissions": {
    "allow": [
      "Bash(npm run build)",
      "Bash(npm test *)",
      "Bash(git commit *)"
    ],
    "ask": [
      "Bash(git push *)"
    ],
    "deny": [
      "Bash(git push * main)",
      "Bash(git push * production)",
      "Bash(rm -rf *)",
      "Read(.env)",
      "Read(.env.*)"
    ]
  }
}

ポイントは2つ。本番ブランチへの push は ask ではなく deny にしている点と、.env の読み取りを deny にしてシークレットを Claude の文脈に入れない点です。git push 全体は ask にして「ボタンを押させる」一方、main と production だけは deny で完全に塞ぐ。こうすると「普段の push は楽、危ない push は不可能」という非対称が作れます。

deny の細かい書き方(Bash(rm *) のような scoped 形と、ツール名だけの bare 形の違いなど)は 権限設定リファレンス に整理しました。ここでは「危険操作は ask で迷わせず deny で塞ぐ」とだけ覚えてください。

瞬間2: 編集の直後 — フックで保護ファイルを守る

allow/deny は強力ですが、文字列マッチなので穴があります。たとえば Bash(devbox run *) のような環境ランナー越しのコマンドや、Python スクリプトがファイルを直接開くケースは、ルールをすり抜けることがある。公式も「Bash の引数を縛るパターンは壊れやすい」と警告しています。

そこで編集直後のゲートとして PreToolUse フックを一段足します。フックはツール呼び出しの前に走り、終了コード 2 を返すとその呼び出しを止められる。しかもフックの停止は allow ルールより優先されるので、「Bash は全部 allow、でもこのファイルへの書き込みだけはフックで止める」という運用ができます。

.claude/settings.json にこう登録します。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "node .claude/guard-protected.mjs"
          }
        ]
      }
    ]
  }
}

フック本体はこれだけです。保護したいパスに編集が入ったら、終了コード 2 で止めます。

// .claude/guard-protected.mjs
// 保護ファイルへの Edit/Write を検出したら exit 2 で止める。
// 標準入力に { tool_input: { file_path } } 形式の JSON が渡される。
import { readFileSync } from "node:fs";

const protectedPatterns = [
  /(^|\/)\.env/,           // 環境変数ファイル
  /billing/i,              // 課金まわり
  /auth/i,                 // 認証まわり
  /production\.config/i,   // 本番設定
];

const raw = readFileSync(0, "utf8");           // stdin を読む
const payload = JSON.parse(raw || "{}");
const target = payload?.tool_input?.file_path ?? "";

if (protectedPatterns.some((re) => re.test(target))) {
  // stderr に理由を書くと Claude 側に伝わり、自己修正しやすい
  console.error(`保護ファイルへの変更は禁止です: ${target}(人間に確認してください)`);
  process.exit(2);        // ここで Claude Code がツール呼び出しを中断する
}

process.exit(0);          // それ以外は素通り

僕がこれを入れて一番変わったのは、理由を stderr に書いたことです。最初はただ exit 2 で黙って止めていたら、Claude が何度も同じ操作を試して堂々巡りになりました。「保護ファイルだから人間に確認して」と一言返したら、別の安全な手段に切り替えるようになった。門番は、止めるだけでなく次の一手を教えると賢く回ります。

瞬間3: デプロイ直前 — CIで機械的に止める

ここが今日の本題です。人間の目視チェックは、忙しい日に必ず飛びます。だからデプロイ直前のゲートはCIに任せて機械的に止める。Claude Code はヘッドレスで動かせるので、CIの中で「点検 → 通ったら次へ」を組めます。

考え方はこうです。本番デプロイのジョブの前に「点検ジョブ」を一つ置く。点検ジョブの中で Claude Code を --print(-p)で非対話実行し、権限を --allowedTools で読み取り系だけに絞り、--permission-mode planソースを編集させない。点検が落ちたらデプロイには進ませない。

GitHub Actions ならこんな形です。

# .github/workflows/predeploy-audit.yml
name: predeploy-audit

on:
  pull_request:
    branches: [main]

jobs:
  permission-audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # 1) まずビルドと差分の事実確認(人間が後で読む証拠を残す)
      - run: npm ci
      - run: npm run build

      # 2) Claude Code を読み取り専用で走らせ、リスクを要約させる
      - name: audit with claude code
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          npx -y @anthropic-ai/claude-code \
            -p "このPRの差分を読み、本番に出して危険な変更(課金・認証・本番設定・破壊的コマンド)があれば列挙。なければ 'AUDIT_OK' とだけ出力。" \
            --permission-mode plan \
            --allowedTools "Read" "Bash(git diff *)" "Bash(git log *)" \
            --disallowedTools "Bash(git push *)" "Edit" "Write" \
            --max-turns 8 \
            --output-format text | tee audit.txt

      # 3) 危険フラグが立っていたらジョブを失敗させ、デプロイを止める
      - name: gate
        run: |
          if grep -q "AUDIT_OK" audit.txt; then
            echo "監査OK。次へ進めます。"
          else
            echo "::error::監査で要確認項目あり。人間レビューまで本番デプロイを止めます。"
            exit 1
          fi

仕掛けは3つです。

  1. --permission-mode plan でソースを触らせない。 plan モードは読み取りと read-only シェルだけを許し、ソースの編集をしません。点検なのに勝手に直されたら本末転倒なので、ここで物理的に封じます。
  2. --allowedTools をホワイトリストにする。 読み取りと git diff/git log だけ許可し、push と編集は --disallowedTools で明示的に拒否。CIの中でも権限を最小に絞る。
  3. 出力を grep してゲートにする。 Claude の判定をそのまま信じるのではなく、合言葉(AUDIT_OK)が出たかだけを機械的に見ます。出なければ exit 1 でジョブを落とし、後段のデプロイジョブを needs: で止める。

念のため:CIで --dangerously-skip-permissions(= --permission-mode bypassPermissions)を使いたくなる場面がありますが、本番に触れる経路では避けてください。これはほぼ全ての確認を飛ばします。使うなら、本番に手が届かないコンテナや使い捨て環境に限る、というのが公式の指針です。

このジョブの嬉しいところは、Claude の賢さに賭けていない点です。賭けているのは「読み取り専用で走らせ、合言葉が出なければ止める」という枠組み。賢いAIを探すより、転んでも本番に届かない足場を先に作る——この発想はハーネス煙テストループの記事でも書いた通りです。

瞬間4: 権限を広げる直前 — 昇格に条件をつける

最後の瞬間は、デプロイそのものではなく「次回から楽にするために allow を増やす」判断です。ここを雑にやると、一回うまくいっただけの操作を恒久 allow にして、3週間後に効いてくる事故の種を撒きます。

僕のルールはシンプルで、昇格には3つの裏付けを要求します。

  1. その操作が ask のまま3回以上安全に通った実績があるか。
  2. 失敗したときの rollback 手順が書いてあるか。
  3. 誰が責任者かが決まっているか(運用者とレビュアーが互いに任せた、をなくす)。

3つ揃わない操作は ask のまま据え置きます。組織で運用するなら、managed settings の allowManagedPermissionRulesOnlytrue にすると、個人やプロジェクトの設定で勝手に allow/ask/deny を足せなくなる。disableBypassPermissionsMode を併せて入れておけば、誰かが bypass モードに切り替える事故も塞げます。

設定の優先順位は managed > コマンドライン引数 > ローカルプロジェクト > プロジェクト > ユーザー の順で、どこか一段で deny されたら他のどの段でも allow できません。だから「絶対に緩めさせたくない一線」は managed の deny に置くのが堅い。段階的に allow を広げる具体的な順番は権限のはしごにまとめてあります。

コピペで動く: 点検ゲートのチェッカー

CIに組み込む前に、手元で「点検に必要な材料が揃っているか」を確かめる小さなチェッカーを置いておくと便利です。証拠・保護ファイル・確認コマンド・rollback・責任者が空でないかを見て、欠けていたら止めます。Node.js があればそのまま動きます。

// predeploy-gate.mjs
// デプロイ前ゲート: 点検に必要な材料が揃っているかを機械的に確認する。
// 揃っていなければ exit 1 で止め、CIから呼べばデプロイを止められる。

const plan = {
  evidence: ["差分URL", "ビルドログ"],   // 事実(推測ではない)
  protectedFiles: ["billing", "auth", "production.config"],
  proofCommand: "npm run build && npm test",
  rollback: "git revert <sha> && redeploy previous tag",
  owner: "masa",                          // rollback の責任者
};

function checkGate(p) {
  const required = ["evidence", "protectedFiles", "proofCommand", "rollback", "owner"];
  const missing = required.filter((key) => {
    const v = p[key];
    return Array.isArray(v) ? v.length === 0 : !v;
  });
  return { ok: missing.length === 0, missing };
}

const result = checkGate(plan);
console.log(result);

if (!result.ok) {
  console.error(`デプロイ前ゲート不合格。欠けている材料: ${result.missing.join(", ")}`);
  process.exit(1);   // CIならここでデプロイを止める
}
console.log("ゲート合格。デプロイへ進めます。");

これは Claude Code の判断を置き換えるものではありません。作業が広がる前に「材料不足」を見える化するためのガードレールです。rollbackowner が空のままデプロイに進もうとしたら止まる。僕はこれを package.jsonpredeploy スクリプトに差して、人間が忘れても機械が止めるようにしています。

よくある質問

Q. 点検は毎回やるべきですか? PRごとは重くないですか? 本番ブランチに向かう PR だけにゲートを刺せば十分です。上の Actions も branches: [main] に限定しています。feature ブランチ同士のマージまで止めると疲れて形骸化するので、止めるのは本番に届く経路だけに絞るのがコツです。

Q. CIで Claude Code を走らせると、勝手にコードを直しませんか? --permission-mode plan を付ければソースは編集されません。plan モードは読み取りと read-only シェルだけを許します。さらに --disallowedTools "Edit" "Write" を併記すれば二重に塞げます。

Q. askdeny はどう使い分けますか? 「人間が判断すれば許してよい」なら ask、「どんな場合も不可」なら deny です。本番への push は普段は ask でいいですが、main/production だけは deny にして物理的に塞ぐ、という非対称が安全です。deny は評価順で最優先なので、後続の allow を気にせず一線を引けます。

Q. デプロイ後に問題が見つかったら、まず何を見ますか? 「Claude を信じたか」ではなく「どのゲートで止まらなかったか」を見ます。止まらなかったゲートを一段強くする(deny を一行足す、フックの保護パターンを増やす)のが、一般論を足すより速い再発防止です。

Q. 個人開発でもここまで要りますか? 全部は要りません。まず瞬間1の deny(本番 push と .env)と、瞬間3のCIゲート一つだけでも入れてください。この2つで「寝てる間に本番が壊れる」系の事故はほぼ消えます。

実際に試した結果

金曜の事故以来、僕は権限の「設定値」を磨くのをやめて、ゲートを置く瞬間を4つに固定しました。依頼前に deny、編集後にフック、デプロイ前にCI、昇格前に条件。このうち効果が一番大きかったのは、文句なしにデプロイ前のCIゲートです。

--permission-mode plan で読み取り専用にし、合言葉が出なければ exit 1 で止める——たったこれだけで、本番ブランチに向かう PR の「うっかり」が公開前に止まるようになりました。目視で頑張っていた頃の、夜中に差分を見返す時間がほぼゼロになった。

賢いAIを探すより、転んでも本番に届かない足場を先に作る。遠回りに見えて、これが一番速い、というのが今の実感です。次に詰まったら、設定をいじる前に「どのゲートで止められたはずか」を一度考えてみてください。


権限点検をチーム全体に展開する設計や、収益導線まで含めたワークフローづくりで迷ったら、研修・導入相談で具体的な構成を一緒に詰められます。手を動かす型を先に揃えたい人は教材一覧もどうぞ。

#Claude Code #権限 #デプロイ #CI #リリース
無料

無料PDF: Claude Code はじめてのチートシート

まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。

スパムは送りません。登録情報は厳重に管理します。

Claude Codeを仕事で使える形にしませんか?

無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。

Masa

この記事を書いた人

Masa

Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。

PR

関連書籍・参考図書

この記事のテーマに関連する書籍を楽天ブックスで探せます。

※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。