開発効率化

GitHub Actions実行保護入門——CIを誰が動かせるか制御する

GitHub ActionsのWorkflow execution protectionsを題材に、AIやbotがPRを作る時代のCI/CD実行権限を整理します。Next.js、Firestore、TypeScriptの検証例とともに安全な導入手順を解説します。

2026年6月20日
GitHub ActionsCI/CDセキュリティNext.jsFirestore
GitHub Actions実行保護入門——CIを誰が動かせるか制御する

はじめに

GitHub Actionsを使っていて、こんな不安はありませんか?

状況困りごと
AIエージェントやbotがPRを作る生成コードがCI上でどこまで実行されるのか見えにくい
workflow_dispatchを便利に使っている誰でも重い検証やデプロイ準備を起動できてしまう
pull_request_targetを使っているフォークPRのコードを特権付きで実行する事故が怖い

2026年6月18日、GitHubは Workflow execution protections をpublic previewとして発表しました。これは「どのActorが」「どのイベントで」GitHub Actionsを起動できるかを、workflow YAMLの外側から制御する仕組みです。

本記事では、架空の販売管理SaaS「OrderDock」を題材に、Next.js + Firestore + GitHub Actions + TypeScriptの現場でどう使うかを整理します。読み終えると、PR用CI、手動実行、bot生成PRの実行権限を分けて設計できるようになります。

何が変わったのか

Workflow execution protectionsは、GitHub Enterprise、Organization、Repositoryレベルで利用できる実行保護です。GitHub公式ドキュメントではpublic previewであり、今後変更される可能性があると明記されています。

ポイントは、workflowファイルの中身だけを信頼しないことです。従来は、トリガーとなったコミットに含まれるworkflow定義に従って実行されました。攻撃者がworkflowを書き換えられる立場にいると、CIを使って危険なコードを動かせる余地がありました。

新しい保護では、管理者がRulesetsベースのルールを定義し、GitHub Actionsが実行前に評価します。最初に使えるルールは次の2種類です。

ルール制御できるもの
Actor rules誰がworkflowを起動できるかユーザー、Repository role、GitHub Apps、Copilot、Dependabot
Event rulesどのイベントでworkflowを起動できるかpushpull_requestpull_request_targetworkflow_dispatch

また、同じ日にGitHubは actions/checkout v7も発表し、pull_request_targetでフォークPRのheadやmerge commitをチェックアウトする典型的な危険パターンをデフォルトで拒否するようにしました。CI/CDの防御線が、YAMLのレビューだけでなく、プラットフォーム側の実行判断へ広がっていると捉えるとわかりやすいです。

OrderDockでの導入方針

OrderDockは、受注・請求・承認ログをFirestoreに保存するNext.js App Router製の業務アプリという想定です。IssueはGitHub Issuesで管理し、AIエージェントが小さな修正PRを作る運用にしています。

この場合、すべてのworkflowを同じ信頼度で扱うのは危険です。次のように「読み取りだけの検証」と「権限が強い処理」を分けます。

workflow主な用途起動を許可するActor/Event
PR Quality Gateyarn build、型チェック、Firestore接続を伴わない単体検証pull_requestを許可。外部PRやbot PRは必要に応じて承認
Release Candidateステージング向けビルド、成果物作成Maintainer以上のworkflow_dispatch
DeployFirebase Hosting本番反映push to mainと管理者の手動実行に限定

GitHubの設定画面では、Actionsの新しい「Policies」セクションでrulesetを作成し、まずevaluate modeで影響を見ます。いきなりactiveにすると、既存の自動化が止まる可能性があります。1週間ほど評価して、実際にブロックされるActor/Eventを確認してから適用するのが現実的です。

ハンズオン1: PR用CIを最小権限にする

まず、PRで動くCIは「検証だけ」に寄せます。GITHUB_TOKENは明示的に読み取りへ絞り、Firestoreの本番資格情報やFirebase Hostingのデプロイ権限を渡しません。

name: PR Quality Gate

on:
  pull_request:
    branches: [main]

permissions:
  contents: read

concurrency:
  group: pr-quality-${{ github.event.pull_request.number }}
  cancel-in-progress: true

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

      - uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "yarn"

      - run: yarn install --frozen-lockfile
      - run: yarn build

ここで重要なのは、PRのCIに「デプロイできる能力」を持たせないことです。GitHub Docsでは、permissionsキーでGITHUB_TOKENの権限をworkflow全体またはjob単位で制御でき、最小権限にすることが推奨されています。

Workflow execution protections側では、pull_requestは許可しつつ、起動できるActorを組織メンバー、Dependabot、承認済みbotなどに絞ります。AIが作ったPRについては、6月11日のGitHub Changelogで示されたように、github-actions[bot]作成PRもユーザー承認後にCIを走らせられるようになっています。つまり「bot PRはCI不能」ではなく、「承認してからCI」という運用にできます。

ハンズオン2: Firestoreの監査ログをテストしやすくする

次に、権限が強い処理ほどアプリ側の監査ログを残します。OrderDockでは、管理者が受注ステータスを変更したらFirestoreのauditLogsへ記録します。

// src/app/api/orders/[orderId]/approve/route.ts
import { initializeApp, getApps } from "firebase-admin/app";
import { FieldValue, getFirestore } from "firebase-admin/firestore";
import { NextResponse } from "next/server";

if (getApps().length === 0) {
  initializeApp();
}

const db = getFirestore();

type Params = {
  params: Promise<{ orderId: string }>;
};

export async function POST(request: Request, { params }: Params) {
  const { orderId } = await params;
  const { reviewerId } = (await request.json()) as { reviewerId: string };

  if (!reviewerId) {
    return NextResponse.json({ error: "reviewerId is required" }, { status: 400 });
  }

  await db.collection("auditLogs").add({
    action: "order.approve",
    orderId,
    reviewerId,
    createdAt: FieldValue.serverTimestamp(),
  });

  return NextResponse.json({ ok: true });
}

このコード自体をCIで本番Firestoreに接続して試す必要はありません。PRでは型チェックとビルド、必要ならFirestoreアクセス部分をモックした単体テストまでに留めます。本番データへ触れる検証は、Maintainerが起動できるworkflow_dispatchや、ステージング専用の環境に分けます。

ハンズオン3: 危険なイベントを棚卸しする

Workflow execution protectionsを入れる前に、既存workflowをイベント別に棚卸しします。

rg "pull_request_target|workflow_dispatch|repository_dispatch|secrets\\." .github/workflows

見つかったworkflowは、次の観点で分類します。

観点確認内容
pull_request_targetフォークPRのコードをチェックアウトして実行していないか
workflow_dispatch誰が手動実行できるべきか
SecretsPRから到達できるjobに秘密情報を渡していないか
Checkoutgithub.event.pull_request.head.shaなど未レビューのコードを特権jobで取っていないか

特にpull_request_targetは慎重に扱います。GitHubの6月18日の発表では、actions/checkout v7が一般的な危険チェックアウトを拒否すると説明されていますが、runブロックでgitghを使って未信頼コードを取得するような別経路は残ります。プラットフォーム側の保護に任せきらず、workflowレビューも続けるべきです。

導入チェックリスト

OrderDockのようなNext.js + Firestore構成なら、まず次の順で進めるのが現実的です。

  1. .github/workflowsを棚卸しし、pull_request_targetworkflow_dispatchを洗い出す
  2. PR用CIのpermissionscontents: read中心に整理する
  3. デプロイ、Firestore本番接続、秘密情報を使うjobをPR用CIから分離する
  4. Workflow execution protectionsをevaluate modeで作成する
  5. Actor rulesでMaintainer、GitHub Apps、Dependabot、Copilotなどの扱いを決める
  6. Event rulesでpull_request_targetworkflow_dispatchの実行範囲を絞る
  7. 1週間ほどログを見て、問題なければactiveに切り替える

AIエージェントがPRを作る開発では、CIを止めることが目的ではありません。信頼できる入口からだけCIを動かし、レビュー前のコードに強い権限を渡さないことが目的です。

参考リンク

まとめ

GitHub ActionsのWorkflow execution protectionsは、CI/CDを「workflowファイルに書かれた通りに動かす」段階から、「組織のポリシーで実行前に判断する」段階へ進める機能です。

Next.jsやFirestoreを使う業務アプリでは、PR用CI、ステージング検証、本番デプロイを分け、Actor/Event単位で実行権限を設計する価値があります。特にAIやbotがPRを量産する開発では、速さと安全性を両立するための基盤になります。