Dependabot Critical Alert の Slack 通知実装
- 公開日
- カテゴリ:Github
- タグ:GitHub

Dependabot が脆弱性を検出して PR を作成しても、見落とすリスクがある。
特に Critical な脆弱性は環境によっては即座に対応したいため、Slack 通知で見逃さない仕組みを作る。
通知される条件
- Dependabot が PR を作成したとき
- Severity が
critical(CVSS 9.0以上)のとき
Slack 通知イメージ(blocks 形式でリッチな表示に):
┌──────────────────────────────────────┐
│ CRITICAL Security Alert │
├──────────────────────────────────────┤
│ Repository: Severity: │
│ user/repo critical (9.8) │
├──────────────────────────────────────┤
│ PR: │
│ Bump immer from 9.0.5 to 10.0.3 │
├──────────────────────────────────────┤
│ Package: │
│ immer │
├──────────────────────────────────────┤
│ Links: │
│ View Alert | View Advisory(CVE-XXX) │
└──────────────────────────────────────┘
アーキテクチャ
各リポジトリには呼び出し用の薄いワークフローのみを配置し、実処理は shared-workflows リポジトリの Reusable Workflow に集約する構成。GitHub App トークンで Dependabot alerts API から脆弱性情報を取得し、Critical の場合のみ Slack に通知する。
┌─────────────────────────────────────────────────────────────┐
│ 各リポジトリ (様々なプロジェクト) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ .github/workflows/dependabot-critical-alert.yml │ │
│ │ (呼び出しワークフロー) │ │
│ └──────────────────────┬──────────────────────────────┘ │
└─────────────────────────┼───────────────────────────────────┘
│ uses: YOUR_USERNAME/shared-workflows/...
▼
┌─────────────────────────────────────────────────────────────┐
│ shared-workflows │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ .github/workflows/dependabot-critical-alert.yml │ │
│ │ (Reusable Workflow) │ │
│ └──────────────────────┬──────────────────────────────┘ │
└─────────────────────────┼───────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
GitHub App Dependabot API Slack Webhook
Token生成 アラート取得 通知送信
使用する GitHub Actions
できるだけ簡単に実装するため、以下の GitHub Actions を利用。
| Action | 用途 |
|---|---|
actions/create-github-app-token | GitHub App トークン生成 |
dependabot/fetch-metadata | PR からパッケージ名等を取得 |
slackapi/slack-github-action | Slack 通知(公式) |
Dependabot alerts API と認証
脆弱性の Severity や CVSS スコアなどの詳細情報を取得するために、Dependabot alerts API を利用する。
Dependabot alerts API へのアクセスには認証が必要だが、GITHUB_TOKEN では Dependabot alerts API にアクセスできないため、選択肢は2つ:
| 方式 | メリット | デメリット |
|---|---|---|
| PAT(Personal Access Token) | 設定がシンプル | 有効期限あり、個人に紐づく |
| GitHub App | 有効期限なし、権限を細かく制御可能 | 初期設定がやや複雑 |
今回は GitHub App を採用。
- トークンの有効期限切れを気にしなくてよい
- 必要最小限の権限(Dependabot alerts: Read-only)だけを付与できる
- 個人アカウントに依存しない
事前に必要な設定
GitHub UI 上で、事前に必要な設定を行う。
1. リポジトリの Dependabot 設定を有効化
各リポジトリで以下を有効化する:
- GitHub リポジトリの Settings > Code security を開く
- 以下の項目を有効化:
- Dependency graph: 依存関係の可視化
- Dependabot alerts: 脆弱性アラートの有効化
- Dependabot security updates: 脆弱性修正 PR の自動作成
これにより、Dependabot が脆弱性を検出した際に自動で PR を作成するようになる。
2. GitHub App の作成
- Permissions:
- Repository permissions > Dependabot alerts: Read-only
- Repository permissions > Metadata: Read-only
- Repository access: 対象リポジトリを追加
3. Dependabot Secrets の設定(各リポジトリ)
| Secret名 | 内容 |
|---|---|
SLACK_WEBHOOK_URL | Slack Incoming Webhook URL |
APP_ID | GitHub App の App ID |
APP_PRIVATE_KEY | GitHub App の Private Key |
Point: Actions secrets ではなく Dependabot secrets に設定する必要がある(Dependabot が作成する PR では Actions secrets にアクセスできないため)
GitHub Actions 実装
単一リポジトリで動作するものを実装した後、複数リポジトリでも利用できる形にしていく。
フェーズ 1: 単一リポジトリ版
1つのリポジトリだけで使う場合のシンプルな実装。
.github/workflows/dependabot-critical-alert.yml
name: Dependabot Critical Alert
on:
pull_request:
types: [opened]
permissions:
contents: read
pull-requests: read
jobs:
notify:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Fetch Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Get alert details
id: alert
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
PACKAGE: ${{ steps.metadata.outputs.dependency-names }}
run: |
ALERT=$(gh api "/repos/${{ github.repository }}/dependabot/alerts" \
--jq ".[] | select(.state == \"open\") | select(.dependency.package.name == \"${PACKAGE}\")" | head -1)
if [ -z "$ALERT" ]; then
echo "found=false" >> $GITHUB_OUTPUT
exit 0
fi
NUMBER=$(echo "$ALERT" | jq -r '.number')
SEVERITY=$(echo "$ALERT" | jq -r '.security_advisory.severity')
CVSS=$(echo "$ALERT" | jq -r '.security_advisory.cvss.score // empty')
GHSA_ID=$(echo "$ALERT" | jq -r '.security_advisory.ghsa_id')
CVE_ID=$(echo "$ALERT" | jq -r '.security_advisory.cve_id // empty')
echo "Found alert: number=${NUMBER}, severity=${SEVERITY}, cvss=${CVSS}"
echo "found=true" >> $GITHUB_OUTPUT
echo "number=${NUMBER}" >> $GITHUB_OUTPUT
echo "severity=${SEVERITY}" >> $GITHUB_OUTPUT
echo "cvss=${CVSS}" >> $GITHUB_OUTPUT
echo "ghsa_id=${GHSA_ID}" >> $GITHUB_OUTPUT
echo "url=https://github.com/${{ github.repository }}/security/dependabot/${NUMBER}" >> $GITHUB_OUTPUT
if [ -n "$CVE_ID" ]; then
echo "advisory_text=View Advisory(${CVE_ID})" >> $GITHUB_OUTPUT
else
echo "advisory_text=View Advisory" >> $GITHUB_OUTPUT
fi
- name: Send Slack notification
if: steps.alert.outputs.found == 'true' && steps.alert.outputs.severity == 'critical'
uses: slackapi/slack-github-action@v2.1.1
env:
REPOSITORY: ${{ github.repository }}
SEVERITY: "critical (${{ steps.alert.outputs.cvss }})"
PR_URL: ${{ github.event.pull_request.html_url }}
PR_TITLE: ${{ github.event.pull_request.title }}
PACKAGE: ${{ steps.metadata.outputs.dependency-names }}
ALERT_URL: ${{ steps.alert.outputs.url }}
ADVISORY_URL: "https://github.com/advisories/${{ steps.alert.outputs.ghsa_id }}"
ADVISORY_TEXT: ${{ steps.alert.outputs.advisory_text }}
with:
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
webhook-type: incoming-webhook
payload: |
blocks:
- type: header
text:
type: plain_text
text: "CRITICAL Security Alert"
emoji: true
- type: section
fields:
- type: mrkdwn
text: "*Repository:*\n${{ env.REPOSITORY }}"
- type: mrkdwn
text: "*Severity:*\n${{ env.SEVERITY }}"
- type: section
text:
type: mrkdwn
text: "*PR:*\n<${{ env.PR_URL }}|${{ env.PR_TITLE }}>"
- type: section
text:
type: mrkdwn
text: "*Package:*\n${{ env.PACKAGE }}"
- type: section
text:
type: mrkdwn
text: "*Links:*\n<${{ env.ALERT_URL }}|View Alert> | <${{ env.ADVISORY_URL }}|${{ env.ADVISORY_TEXT }}>"
フェーズ 2: 複数リポジトリ共通化版(Reusable Workflows)
複数リポジトリで同じワークフローを使いたい場合は、Reusable Workflows で共通化する。
Reusable Workflows は、GitHub Actions でワークフローを再利用する仕組み。workflow_call トリガーで定義したワークフローを、他のリポジトリから uses: で呼び出せる。
Composite Actions(通常の GitHub Actions)は Private リポジトリ間で共有できないが、Reusable Workflows ならアクセス許可を設定すれば Private でも共有できる。
Reusable Workflows の前提条件
Reusable Workflows を別リポジトリから呼び出すには、以下の条件がある:
| 環境 | Private リポジトリ | Public リポジトリ |
|---|---|---|
| Organization | 同一 Organization 内で設定すれば利用可能 | 利用可能 |
| 個人アカウント | 同一ユーザー所有で設定すれば利用可能 | 利用可能 |
個人アカウントの場合: Private リポジトリに配置した Reusable Workflows も設定次第で共有可能だが、設定が必要。試してみる程度なら Public リポジトリ に配置するのが手軽。
個人アカウントで private リポジトリに対する同一ユーザー所有の設定は以下:
shared-workflows の Settings > Actions > General > Access
「Accessible from repositories owned by 'YOUR_USERNAME' user」を選択
◯ Not accessible
Workflows in other repositories cannot access this repository.
● Accessible from repositories owned by the user 'YOUR_USERNAME'
Workflows in other repositories that are owned by the user 'YOUR_USERNAME' can access the actions and reusable workflows in this repository. Access is allowed only from private repositories.
参考:Managing GitHub Actions settings for a repository - GitHub Docs
共通ワークフロー(shared-workflows リポジトリに配置)
shared-workflows/.github/workflows/dependabot-critical-alert.yml
name: Dependabot Critical Alert
on:
workflow_call:
secrets:
SLACK_WEBHOOK_URL:
required: true
APP_ID:
required: true
APP_PRIVATE_KEY:
required: true
jobs:
notify:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Fetch Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Get alert details
id: alert
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
PACKAGE: ${{ steps.metadata.outputs.dependency-names }}
run: |
ALERT=$(gh api "/repos/${{ github.repository }}/dependabot/alerts" \
--jq ".[] | select(.state == \"open\") | select(.dependency.package.name == \"${PACKAGE}\")" | head -1)
if [ -z "$ALERT" ]; then
echo "found=false" >> $GITHUB_OUTPUT
exit 0
fi
NUMBER=$(echo "$ALERT" | jq -r '.number')
SEVERITY=$(echo "$ALERT" | jq -r '.security_advisory.severity')
CVSS=$(echo "$ALERT" | jq -r '.security_advisory.cvss.score // empty')
GHSA_ID=$(echo "$ALERT" | jq -r '.security_advisory.ghsa_id')
CVE_ID=$(echo "$ALERT" | jq -r '.security_advisory.cve_id // empty')
echo "found=true" >> $GITHUB_OUTPUT
echo "number=${NUMBER}" >> $GITHUB_OUTPUT
echo "severity=${SEVERITY}" >> $GITHUB_OUTPUT
echo "cvss=${CVSS}" >> $GITHUB_OUTPUT
echo "ghsa_id=${GHSA_ID}" >> $GITHUB_OUTPUT
echo "url=https://github.com/${{ github.repository }}/security/dependabot/${NUMBER}" >> $GITHUB_OUTPUT
if [ -n "$CVE_ID" ]; then
echo "advisory_text=View Advisory(${CVE_ID})" >> $GITHUB_OUTPUT
else
echo "advisory_text=View Advisory" >> $GITHUB_OUTPUT
fi
- name: Send Slack notification
if: steps.alert.outputs.found == 'true' && steps.alert.outputs.severity == 'critical'
uses: slackapi/slack-github-action@v2.1.1
env:
REPOSITORY: ${{ github.repository }}
SEVERITY: "critical (${{ steps.alert.outputs.cvss }})"
PR_URL: ${{ github.event.pull_request.html_url }}
PR_TITLE: ${{ github.event.pull_request.title }}
PACKAGE: ${{ steps.metadata.outputs.dependency-names }}
ALERT_URL: ${{ steps.alert.outputs.url }}
ADVISORY_URL: "https://github.com/advisories/${{ steps.alert.outputs.ghsa_id }}"
ADVISORY_TEXT: ${{ steps.alert.outputs.advisory_text }}
with:
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
webhook-type: incoming-webhook
payload: |
blocks:
- type: header
text:
type: plain_text
text: "CRITICAL Security Alert"
emoji: true
- type: section
fields:
- type: mrkdwn
text: "*Repository:*\n${{ env.REPOSITORY }}"
- type: mrkdwn
text: "*Severity:*\n${{ env.SEVERITY }}"
- type: section
text:
type: mrkdwn
text: "*PR:*\n<${{ env.PR_URL }}|${{ env.PR_TITLE }}>"
- type: section
text:
type: mrkdwn
text: "*Package:*\n${{ env.PACKAGE }}"
- type: section
text:
type: mrkdwn
text: "*Links:*\n<${{ env.ALERT_URL }}|View Alert> | <${{ env.ADVISORY_URL }}|${{ env.ADVISORY_TEXT }}>"
呼び出し側ワークフロー(各リポジトリに配置)
.github/workflows/dependabot-critical-alert.yml
name: Dependabot Critical Alert
on:
pull_request:
types: [opened]
permissions:
contents: read
pull-requests: read
jobs:
notify:
uses: YOUR_USERNAME/shared-workflows/.github/workflows/dependabot-critical-alert.yml@main
secrets: inherit
共通化のポイント
| 項目 | フェーズ 1 | フェーズ 2 |
|---|---|---|
| メンテナンス | 各リポジトリで個別 | 共通リポジトリで一括 |
| 変更の反映 | 各リポジトリで修正 | 共通リポジトリの修正のみ |
まとめ
Dependabot の Critical アラートを Slack 通知する仕組みを実装した。
GITHUB_TOKENでは Dependabot alerts API にアクセスできないため、GitHub App を作成してトークンを生成することで解決できる。- Dependabot が作成する PR では Actions secrets にアクセスできないため、Dependabot secrets に設定する必要がある点も注意が必要。
- 複数リポジトリで同じ仕組みを使いたい場合は、Reusable Workflows で共通化できる。個人アカウントでも設定次第で Private リポジトリで共有可能なので、Organization でも個人アカウントでも活用できる。

