GitHub Secret Scanningのcustom patternをREST APIで一括管理する方法|階層別endpointとdry run制約

多数のOrganizationやRepositoryへGitHub secret scanningのcustom patternを配布する作業は、REST APIで自動化できます。2026年7月13日から、Enterprise、Organization、Repositoryの各階層で、custom patternの取得・作成・更新・削除を行うREST APIが正式提供されています。

ただし、APIだけで運用を完結させることはできません。パターンが実際のコードにどう一致するかを確認するdry runと、パターンを有効化する最終publishは、引き続きGitHub UIで行う必要があります。そのため、実務では「REST APIでドラフトを一括同期し、GitHub UIでdry runとpublishを行う」という二段階の運用にするのが現実的です。(The GitHub Blog)

この記事では、階層別endpointの選び方、認証権限、POST・PATCH・DELETEの具体例、多数のOrganizationやRepositoryへ安全に配布する設計、dry run制約への対処方法まで解説します。

目次

secret scanning custom patternをREST APIで管理できる範囲

custom pattern向けREST APIでは、基本的なCRUD操作を実行できます。一方、実際のRepositoryを検索するGitHubのdry runと、パターンのpublishはAPIの対象外です。

操作REST API補足
パターン一覧の取得可能GETを使用
パターンの新規作成可能POSTで複数件をまとめて作成可能
パターンの更新可能PATCHで1パターンずつ更新
パターンの削除可能DELETEで複数件をまとめて削除可能
定義差分の確認独自実装で可能現在値と管理ファイルを比較する
Repositoryに対するdry run不可GitHub UIで実行
最終publish不可GitHub UIで実行

ここで注意したいのは、「独自実装のplan」と「GitHubのdry run」は別物だという点です。

独自のplan処理では、APIから取得した現在の定義と、Gitなどで管理している期待値を比較できます。しかし、正規表現が実際のコードに何件一致するか、誤検知が発生するかまでは確認できません。実データへの一致確認は、GitHub UIのdry runが必要です。

階層別endpointと使い分け

custom pattern APIは、Enterprise、Organization、Repositoryの3階層に用意されています。

階層ベースendpoint適した用途
Enterprise/enterprises/{enterprise}/secret-scanning/custom-patterns複数Organizationへ共通ルールを適用する
Organization/orgs/{org}/secret-scanning/custom-patternsOrganizationごとの社内ルールを管理する
Repository/repos/{owner}/{repo}/secret-scanning/custom-patterns特定Repositoryだけの例外ルールを管理する

各階層で使用するHTTPメソッドは共通です。

操作メソッドendpoint
一覧取得GETベースendpoint
新規作成POSTベースendpoint
更新PATCHベースendpoint+/{pattern_id}
削除DELETEベースendpoint

PATCHではURLにpattern_idを含めます。一方、DELETEではURLにIDを付けず、削除対象をリクエストボディのpatterns配列で指定します。この違いは実装時に間違えやすいポイントです。(GitHub Docs)

Enterprise階層を選ぶ場合

複数のOrganizationに同じパターンを適用したい場合、Organizationごとに同じ定義を複製するより、Enterprise階層で一元管理した方が管理対象を減らせます。

ただし、Enterprise endpointには次の制約があります。

  • classic personal access tokenが必要
  • admin:enterpriseスコープが必要
  • fine-grained personal access tokenは利用できない
  • GitHub Appのinstallation access tokenやuser access tokenは利用できない
  • Enterpriseレベルのパターンは、作成者だけが編集やdry runを実行できる

特に最後の作成者制約は重要です。自動化専用アカウントでPOSTした後、別の管理者がUIでdry runできない構成になる可能性があります。Enterprise階層で自動化する場合は、誰の認証情報で作成し、誰がUIでdry runとpublishを行うのかを先に決めてください。(GitHub Docs)

Organization階層を選ぶ場合

Organizationごとに異なる命名規則やトークン形式を検知したい場合は、Organization endpointが適しています。

たとえば、事業部ごとにOrganizationが分かれていて、内部サービスのトークン形式も異なる場合です。Organization単位なら、共通ルールを保ちつつ、対象範囲を適切に限定できます。

GitHub Appやfine-grained personal access tokenにも対応しているため、多数のOrganizationを継続的に管理する自動化では、Enterprise階層より扱いやすいケースがあります。

Repository階層を選ぶ場合

Repository固有の秘密情報を検知したい場合は、Repository endpointを使用します。

たとえば、旧システムだけで使われている独自APIキーや、特定製品のライセンスキー形式を検知する場合です。

共通パターンまでRepositoryごとに複製すると、更新漏れや削除漏れが起きやすくなります。Repository階層は、全体ルールの配布先ではなく、局所的な例外に限定するのが安全です。

APIに必要な認証と権限

必要なトークンと権限は階層ごとに異なります。

階層classic PATfine-grained PAT/GitHub App
Enterpriseadmin:enterprise非対応
OrganizationGETはread:org、変更操作はwrite:orgOrganizationのAdministration権限。GETはread、POST・PATCH・DELETEはwrite
Repositoryrepoまたはsecurity_events。公開Repositoryのみならpublic_repoも利用可能RepositoryのSecret scanning alerts権限。GETはread、変更操作はwrite

OrganizationやRepositoryを大量に扱う場合は、可能であればGitHub Appを使い、対象OrganizationやRepositoryだけに権限を付与する方が管理しやすくなります。

一方、Enterprise endpointではGitHub Appやfine-grained PATを利用できません。Enterprise全体をAPI管理する場合は、classic PATの保管、ローテーション、利用者監査を別途設計する必要があります。(GitHub Docs)

REST APIリクエストの基本形

GitHub REST APIでは、APIバージョンを明示して呼び出すことが推奨されます。2026年7月時点のドキュメントでは、2026-03-10が最新のAPIバージョンとして案内されています。(GitHub Docs)

Organization階層から一覧を取得する例は次のとおりです。

export GH_TOKEN='<YOUR_TOKEN>'
export ORG='example-org'

curl --fail-with-body \
  --silent \
  --show-error \
  --location \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer ${GH_TOKEN}" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  "https://api.github.com/orgs/${ORG}/secret-scanning/custom-patterns?per_page=100"

トークンをスクリプトやRepositoryへ直接記述してはいけません。CI/CDで実行する場合は、GitHub ActionsのSecrets、外部シークレット管理サービス、GitHub Appの短期トークンなどを利用します。

GETで利用できる主な絞り込み条件

一覧取得では、状態やpush protectionの設定状態を指定できます。

パラメーター主な値用途
statepublished、unpublished公開済みか未公開かを絞り込む
push_protectionenabled、disabledpush protectionの状態で絞り込む
sortcreated、updated、name並び替え項目
directionasc、desc昇順・降順
per_page最大1001ページの取得件数
page1以上取得ページ

管理対象が100件を超える場合は、1回のGETだけでは全件を取得できません。レスポンスのLinkヘッダーを処理し、次のページがなくなるまで取得してください。(GitHub Docs)

POSTでcustom patternを一括作成する

新しいパターンは、ベースendpointへPOSTします。リクエストボディにはpatterns配列を指定できるため、複数のパターンをまとめて作成できます。

Organization階層へ作成する例は次のとおりです。

{
  "patterns": [
    {
      "name": "Internal service token",
      "pattern": "svc_[A-Z0-9]{32}",
      "start_delimiter": "\\A|[^0-9A-Za-z]",
      "end_delimiter": "\\z|[^0-9A-Za-z]",
      "must_match": [
        "svc_"
      ],
      "must_not_match": [
        "svc_TEST_"
      ]
    }
  ]
}
curl --fail-with-body \
  --silent \
  --show-error \
  --location \
  -X POST \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer ${GH_TOKEN}" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  -H "Content-Type: application/json" \
  "https://api.github.com/orgs/${ORG}/secret-scanning/custom-patterns" \
  --data-binary @create-patterns.json

各フィールドの役割は次のとおりです。

| フィールド | 必須 | 内容 |
| —————– | -: | ——————— |
| name | 必須 | GitHub UIなどに表示するパターン名 |
| pattern | 必須 | 秘密情報本体を検出する正規表現 |
| start_delimiter | 任意 | パターン直前に許容する境界 |
| end_delimiter | 任意 | パターン直後に許容する境界 |
| must_match | 任意 | 追加で一致を必要とする条件 |
| must_not_match | 任意 | 一致対象から除外する条件 |

作成に成功するとHTTPステータス201が返ります。APIで作成した直後のパターンはunpublishedであり、push protectionも無効な状態です。その後、GitHub UIでdry runとpublishを行います。(GitHub Docs)

正規表現のJSONエスケープに注意する

正規表現中のバックスラッシュは、JSON上で二重にエスケープする必要があります。

たとえば、正規表現として\Aを渡したい場合、JSONファイルには\\Aと記述します。

{
  "start_delimiter": "\\A|[^0-9A-Za-z]"
}

シェルスクリプト内でJSON文字列を直接組み立てると、シェルとJSONの両方でエスケープが必要になり、意図しない正規表現になりやすくなります。固定のJSONファイルを使用するか、jqなどのJSON生成ツールを使う方が安全です。

PATCHでcustom patternを更新する

パターンの更新は、pattern_idを含むendpointへPATCHします。

/orgs/{org}/secret-scanning/custom-patterns/{pattern_id}

リクエスト例は次のとおりです。

{
  "pattern": "svc_[A-Z0-9]{40}",
  "start_delimiter": "\\A|[^0-9A-Za-z]",
  "end_delimiter": "\\z|[^0-9A-Za-z]",
  "must_match": [
    "svc_"
  ],
  "must_not_match": [
    "svc_TEST_",
    "svc_EXAMPLE_"
  ],
  "custom_pattern_version": "<CURRENT_VERSION>"
}
export PATTERN_ID='123'

curl --fail-with-body \
  --silent \
  --show-error \
  --location \
  -X PATCH \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer ${GH_TOKEN}" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  -H "Content-Type: application/json" \
  "https://api.github.com/orgs/${ORG}/secret-scanning/custom-patterns/${PATTERN_ID}" \
  --data-binary @update-pattern.json

custom_pattern_versionは必須

PATCHでは、現在のcustom_pattern_versionを送信する必要があります。これは、別の管理者や自動処理によってパターンが変更された後、古い情報で上書きすることを防ぐための仕組みです。

更新前に現在の状態を取得し、差分計算に使用したバージョンをPATCHへ含めます。古いバージョンを送信すると、412 Precondition Failedになる可能性があります。

412が返った場合、同じリクエストをそのまま再送してはいけません。最新状態を取得し直し、期待値との差分を再計算してから更新します。(GitHub Docs)

APIでは名前変更を前提にしない

公開されているPATCH仕様には、更新可能フィールドとしてnameが含まれていません。したがって、自動化ではパターン名を後から変更できる前提にしない方が安全です。

名称変更が必要な場合は、次のような段階移行にします。

  1. 新しい名前でパターンを作成する
  2. GitHub UIでdry runする
  3. 新しいパターンをpublishする
  4. 動作を確認する
  5. 古いパターンを削除する

管理ファイルでは、表示名とは別に変更しない内部キーを持たせると、名称変更による対応関係の崩れを防げます。

patterns:
  - key: internal-service-token
    name: Internal service token
    pattern: svc_[A-Z0-9]{32}

このkeyはGitHubへ送信する項目ではなく、自動化スクリプト内でパターンを識別するための独自項目です。

DELETEでcustom patternを一括削除する

削除では、URLにpattern_idを含めません。ベースendpointへDELETEし、リクエストボディのpatterns配列で対象を指定します。

{
  "patterns": [
    {
      "pattern_id": 123,
      "custom_pattern_version": "<CURRENT_VERSION>",
      "post_delete_action": "resolve_alerts"
    },
    {
      "pattern_id": 456,
      "custom_pattern_version": "<CURRENT_VERSION>",
      "post_delete_action": "resolve_alerts"
    }
  ]
}
curl --fail-with-body \
  --silent \
  --show-error \
  --location \
  -X DELETE \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer ${GH_TOKEN}" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  -H "Content-Type: application/json" \
  "https://api.github.com/orgs/${ORG}/secret-scanning/custom-patterns" \
  --data-binary @delete-patterns.json

成功時はHTTPステータス204が返ります。

post_delete_actionを必ず明示する

post_delete_actionには、次の2種類があります。

値削除後のアラート
delete_alerts関連するアラートを完全に削除する
resolve_alerts関連するアラートを「pattern deleted」として解決済みにする

省略時の既定値はdelete_alertsです。監査履歴を残したい環境で省略すると、意図せず既存アラートを削除するおそれがあります。

通常は、過去の検出履歴を追跡できるresolve_alertsを明示し、アラート自体を消す必要がある場合だけdelete_alertsを選ぶ方が安全です。(GitHub Docs)

関連アラートが大量にある場合、パターン削除後のアラート処理には時間がかかることがあります。APIから204が返った直後に、すべての画面表示やアラート状態が更新されているとは限りません。(GitHub)

多数のOrganizationやRepositoryへ配布する設計

大量配布では、単純にPOSTを繰り返すだけでは不十分です。途中失敗、手動変更、削除漏れ、パターンIDの違いを考慮し、期待する状態と現在の状態を比較する「desired state方式」にします。

定義ファイルを正本にする

たとえば、次のようなYAMLをGitで管理します。

patterns:
  - key: internal-service-token
    name: Internal service token
    pattern: svc_[A-Z0-9]{32}
    start_delimiter: '\A|[^0-9A-Za-z]'
    end_delimiter: '\z|[^0-9A-Za-z]'
    must_match:
      - 'svc_'
    must_not_match:
      - 'svc_TEST_'

targets:
  organizations:
    - example-org-a
    - example-org-b

  repositories:
    - owner: example-org-c
      repo: legacy-api

この定義ファイルを正本とし、GitHub UI上の定義を直接修正する運用は例外扱いにします。

UIで緊急修正した場合も、そのまま放置せず、同じ変更を定義ファイルへ戻します。そうしないと、次回の自動同期で手動変更が元に戻る可能性があります。

一括管理処理の基本フロー

自動化処理は、次の順序にします。

段階処理
InventoryGETで対象階層の全パターンを取得する
NormalizeJSONの並び順や省略値を正規化する
Match独自キー、名前、保存済みpattern IDを使って対応付ける
Plan作成・更新・削除予定を表示する
Apply create不足しているパターンをPOSTする
Apply update差分があるパターンをPATCHする
Apply delete明示的に許可された場合だけDELETEする
UI reviewGitHub UIでdry runする
PublishGitHub UIでpublishする
VerifyGETで公開状態と定義を再確認する

処理イメージは次のようになります。

for each target:
    current_patterns = fetch_all_pages(target)
    desired_patterns = load_manifest(target)

    plan = calculate_diff(current_patterns, desired_patterns)

    print(plan)

    if mode == "apply":
        create_patterns(plan.creates)

        for update in plan.updates:
            update_pattern(update)

        if allow_delete:
            delete_patterns(plan.deletes)

POSTとDELETEは複数件をまとめて実行できますが、PATCHはpattern_id単位です。更新対象が多い場合は、APIのレート制限や途中失敗を考慮し、一定件数ごとに処理するか、再開可能な実装にします。(The GitHub Blog)

planとapplyを分離する

GitHubのdry runをAPIから実行できない以上、自動化側には少なくとも定義差分を確認するplanモードが必要です。

./sync-custom-patterns --plan

出力例は次のようにします。

Target: organization/example-org-a

CREATE:
  - Internal service token

UPDATE:
  - Legacy API key
    pattern: changed
    must_not_match: 1 item added

DELETE:
  - Old deployment token
    post_delete_action: resolve_alerts

実際に変更する場合だけapplyを指定します。

./sync-custom-patterns --apply

さらに削除は別のフラグで保護します。

./sync-custom-patterns --apply --allow-delete

これにより、定義ファイルの読み込み失敗や対象Organizationの指定ミスによって、既存パターンが一括削除される事故を防ぎやすくなります。

dry runとpublishを運用フローへ組み込む

APIで作成または更新した後は、GitHub UIでdry runを実行します。OrganizationやEnterprise階層では、dry runの対象Repositoryを選択できます。dry runによって見つかった一致は確認用であり、この時点ではsecret scanning alertは作成されません。(GitHub)

推奨する運用フローは次のとおりです。

APIでunpublished状態まで同期する

CI/CDまたは管理スクリプトで、各階層へPOST・PATCHを実行します。

この段階では、検出ルールを本番適用したとは考えません。API処理の役割は、GitHub UIで評価できるドラフトを準備するところまでです。

変更レポートを出力する

自動処理の終了時に、少なくとも次の情報を記録します。

  • 対象Enterprise、Organization、Repository
  • 作成・更新・削除したパターン
  • pattern_id
  • API実行結果
  • dry runが必要なパターン
  • publish待ちのパターン
  • 削除時のpost_delete_action

GitHub Actionsで実行する場合は、Job SummaryやPull Requestコメントへ出力すると、レビュー担当者が確認しやすくなります。

GitHub UIでdry runする

まず少数の代表的なRepositoryで試します。

選定するRepositoryは、単に規模の小さいものではなく、次のようなコードを含むものが適しています。

  • 実際の設定ファイルが多いRepository
  • テスト用トークンやサンプル値を含むRepository
  • 生成コードや圧縮済みファイルを含むRepository
  • 過去のGit履歴が長いRepository
  • 利用言語やフレームワークが異なるRepository

誤検知が多い場合は、must_not_matchやdelimiterを調整します。検知漏れが疑われる場合は、パターン本体だけでなく、前後の境界条件も確認してください。

GitHub UIでpublishする

dry runの結果を確認し、問題がなければpublishします。

push protectionを利用する場合も、dry runとpublishを先に完了させる必要があります。最初からpushをブロックするのではなく、通常の検知で誤検知を確認してから段階的に有効化する方が安全です。(GitHub)

APIで公開状態を再確認する

publish後にGETを実行し、state=publishedになったことを確認します。

curl --fail-with-body \
  --silent \
  --show-error \
  --location \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer ${GH_TOKEN}" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  "https://api.github.com/orgs/${ORG}/secret-scanning/custom-patterns?state=published&per_page=100"

APIで定義を同期しただけで処理を成功扱いにすると、UIでpublishされていないパターンを「配布済み」と誤認します。自動化の完了条件は、APIの成功とpublish後の再確認を分けて管理してください。

更新時に既存アラートへ与える影響

custom patternを変更すると、以前のバージョンで作成されたアラートが閉じられます。そのため、正規表現の微調整であっても、単なる設定変更として扱わない方が安全です。(GitHub)

更新前には、次の情報を記録しておきます。

  • 現在のパターン定義
  • 現在のpattern IDとversion
  • 既存アラート数
  • 変更理由
  • dry run結果
  • publish実施者
  • ロールバック方針

誤検知の除外条件を1件追加するだけでも、既存アラートの状態が変化する可能性があります。セキュリティ担当者が調査中のアラートがある場合は、更新前に影響を共有してください。

よくある失敗と対処方法

症状主な確認ポイント対処
400 Bad RequestJSON形式、正規表現のエスケープ、必須フィールドjqなどでJSONを検証する
403 Forbiddenトークンの種類、scope、OrganizationやRepositoryの権限階層ごとの必要権限を確認する
404 Not FoundEnterprise名、Organization名、Repository名、endpoint階層対象名とAPIパスを再確認する
412 Precondition Failedcustom_pattern_versionが古い最新状態を取得し、差分を再計算する
422 Unprocessable Entity正規表現、delimiter、複数パターンの一部が不正ペイロードを分割して問題のパターンを特定する
POST後も検知されないunpublishedのままGitHub UIでdry runとpublishを行う
DELETE後に履歴が消えたpost_delete_actionを省略した通常はresolve_alertsを明示する
Enterpriseで別の管理者がdry runできない作成者制約API実行者とUI担当者を事前に統一する

1件の不正なパターンで一括作成が失敗する

POSTで複数パターンを送信すると、いずれかの定義に問題がある場合に422になることがあります。

大量配布の前に、ローカルでJSON構文を検証し、最初は少数のパターンでテストしてください。エラー時にはペイロードを半分ずつ分割すると、問題のある定義を特定しやすくなります。

APIレスポンスのサンプルだけで型を決める

ドキュメント上のサンプルレスポンスは、すべてのフィールドを常に掲載しているとは限りません。特にPATCHで必要なcustom_pattern_versionを扱う実装では、サンプルJSONの見た目だけでデータモデルを固定しないことが重要です。

実際のAPIレスポンスとOpenAPI schemaを確認し、不明なフィールドを破棄しない形でクライアントを実装します。

階層をまたいで同じパターンを複製する

同じルールをEnterprise、Organization、Repositoryへそれぞれ登録すると、どの階層の定義が正本なのか分からなくなります。

基本方針は次のようにします。

  • 全体共通ルールはEnterprise
  • Organization固有ルールはOrganization
  • Repository固有の例外だけRepository

Enterprise endpointの認証制約や作成者制約が運用に合わない場合は、Organization階層へ展開する設計も選択肢になります。その場合も、定義ファイルは1か所に集約し、Organizationごとのコピーを手動管理しないことが重要です。

安全に導入するための実践手順

最初から全Organizationへ展開するのではなく、次の順序で導入します。

対象階層と認証方法を決める

共通ルールならEnterprise、部門別ならOrganization、例外ならRepositoryを基本にします。

Enterpriseを選ぶ場合はclassic PATと作成者制約、OrganizationやRepositoryを選ぶ場合はGitHub Appの権限設計を確認します。

既存パターンをすべて取得する

GETを使用し、ページネーションを処理して全件を取得します。既存のpattern ID、状態、定義をバックアップします。

desired stateファイルを作る

Gitで管理するYAMLまたはJSONへ、パターン、適用対象、削除後のアラート処理方針を記録します。

planモードを実行する

作成・更新・削除の予定を確認します。削除が含まれる場合は、自動適用せずレビューを必須にします。

小規模な対象へapplyする

最初は検証用OrganizationまたはRepositoryへ適用します。APIが成功したことだけでなく、GitHub UIにunpublishedのパターンが表示されることも確認します。

GitHub UIでdry runする

実データへの一致件数と誤検知を確認します。パターン本体だけでなく、delimiterや除外条件も調整します。

publish後にAPIで再確認する

state=publishedで一覧を取得し、期待した対象に公開済みパターンが存在することを確認します。

対象を段階的に拡大する

検証用Repository、単一Organization、複数Organizationの順に展開します。各段階でdry run結果とアラート件数を確認し、一括展開による大量誤検知を防ぎます。

REST APIとUIを分担することが一括管理の要点

GitHub secret scanningのcustom patternは、REST APIによって多数のEnterprise、Organization、Repositoryへ配布・更新・削除できるようになりました。特に、POSTによる複数作成、PATCHによるバージョン管理付き更新、DELETEによる複数削除を組み合わせれば、Git管理された定義を各環境へ同期できます。

一方、APIはGitHubのdry runと最終publishを代替しません。完全無人化を目指すのではなく、次の役割分担にすることが重要です。

  • REST APIで定義の取得、差分確認、作成、更新、削除を自動化する
  • 独自のplanモードで変更内容をレビューする
  • GitHub UIで実Repositoryに対するdry runを行う
  • 誤検知を確認してからUIでpublishする
  • publish後にREST APIで状態を再確認する

まずは1つのOrganizationまたは検証用Repositoryで、GET、POST、UIでのdry run、publish、GETによる再確認までを一巡させてください。その流れを自動化の標準手順として固めてから、対象OrganizationやRepositoryを増やすのが安全です。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次