tl;dr;
自分が管理してるOSSで Dependabot Security Alert で作られたPRに対して自動でタイトルにCVE IDをつけたりsecurityラベルをつけられるようにしました


モチベーション
PRのタイトルにCVE IDを書いておくとリリースノートでオートリンクされて便利です。

今まではDependabot Security Alertが作ったPRに対して手でタイトルを修正をしてたのですが、自動化の機運が高まったのでやりました。
技術的なこと
dependabot/fetch-metadata で alert-lookup: true をつければGHSA ID( https://github.com/advisories 上のID)がとれるので、GHSA IDからさらにCVE IDを取得してます。
あとラベルをつけておくと .github/release.yml でいい感じに分類できるようになるのでsecurityラベルをつけるようにしました。
workflow抜粋
- uses: actions/create-github-app-token@v2 id: app-token with: app-id: ${{ secrets.app-id }} private-key: ${{ secrets.private-key }} permission-contents: write permission-issues: write # to create security label permission-pull-requests: write permission-vulnerability-alerts: read - name: Dependabot metadata id: metadata uses: dependabot/fetch-metadata@v2 with: github-token: ${{ steps.app-token.outputs.token }} alert-lookup: true - name: Create `security` label if it doesn't exist if: steps.metadata.outputs.ghsa-id != '' run: | if ! gh label list --json name --jq '.[].name' | grep -qx "security"; then gh label create "security" --color "ffc107" --description "Security related issues or PRs" echo "[INFO] Created security label" fi env: GH_TOKEN: ${{ steps.app-token.outputs.token }} - name: Add security label and GHSA-ID (or CVE-ID if possible) to PR if: steps.metadata.outputs.ghsa-id != '' run: | # Only prepend GHSA-ID or CVE-ID if the title does not already start with a GHSA or CVE prefix if [[ ! "${PR_TITLE}" =~ ^\[(GHSA|CVE)-[0-9A-Za-z-]+\] ]]; then # When multiple CVE identifiers exist for a GHSA advisory, we intentionally use a single CVE ID for the PR title prefix. CVE_ID=$(gh api "/advisories/$GHSA_ID" --jq '.identifiers[] | select(.type=="CVE") | .value' 2>/dev/null || echo "") if [ -n "${CVE_ID}" ]; then PR_TITLE="[${CVE_ID}] ${PR_TITLE}" else PR_TITLE="[${GHSA_ID}] ${PR_TITLE}" fi fi gh pr edit "$PR_URL" --title "${PR_TITLE}" --add-label "security" env: PR_URL: ${{ github.event.pull_request.html_url }} PR_TITLE: ${{ github.event.pull_request.title }} GH_TOKEN: ${{ steps.app-token.outputs.token }} GHSA_ID: ${{ steps.metadata.outputs.ghsa-id }}
細かいけど、 actions/create-github-app-token で permision-xxxx みたいな引数を明示的に渡すことで必要な権限をyaml上で宣言できて便利ですね。
workflow全文は https://github.com/sue445/workflows/blob/main/.github/workflows/dependabot-security-alert.yml を見てください。
注意点
Security advisoryの情報は secret.GITHUB_TOKENでは取得できません。
https://github.com/dependabot/fetch-metadata のREADMEには alert-lookup: true を使うにはPAT(Personal Access Token)を使えって書いてたけど調べたらGitHub AppでDependabot alertsのRead-onlyをつけることで取得できたのでPATを使うよりはこっちの方がセキュアかと思います。*1
あとGitHub Appを利用する場合にはGitHub Appの証明書をsecretにセットしてから利用することになると思いますが、Dependabotが参照できる必要があるのでAction Secretではなく Dependabot Secrets にセットする必要があります。


苦労したポイント
Dependabot security alertによるPR作成がworkflowのトリガになっているため、動作確認が非常に大変でした。(自分がメンテしてるOSSで使ってるライブラリで脆弱性が発生しないと動作確認ができない)
One more thing
privateリポジトリも含めると僕は100個以上のリポジトリをメンテしてるんですが、全てのリポジトリにさっきのような長大なworkflowファイルを置きたくなかったのでworkflow本体は https://github.com/sue445/workflows に置きつつ他のリポジトリからはそれをincludeする方式にしました。(最近のマイブーム)
各リポジトリでは下記のようなyamlを書くだけで使えるようにしてます。
name: dependabot-security-alert on: pull_request: types: - opened - synchronize # PR branch is rebased jobs: auto-merge: uses: sue445/workflows/.github/workflows/dependabot-security-alert.yml@main secrets: # TODO: Set secrets to Dependabot secrets app-id: ${{ secrets.GH_APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} # slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
https://github.com/sue445/workflows?tab=readme-ov-file#dependabot-security-alert
1つのworkflowファイルから複数の Reuse workflows をincludeすることができるので、実際には下記のように DependabotのPRをいい感じに自動マージするworkflow もセットで使っています。
name: dependabot-manager on: pull_request: types: - opened - synchronize # PR branch is rebased jobs: dependabot-auto-merge: uses: sue445/workflows/.github/workflows/dependabot-auto-merge.yml@main secrets: # TODO: Set secrets to Dependabot secrets app-id: ${{ secrets.GH_APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} slack-webhook: ${{ secrets.SLACK_WEBHOOK }} dependabot-security-alert: uses: sue445/workflows/.github/workflows/dependabot-security-alert.yml@main secrets: # TODO: Set secrets to Dependabot secrets app-id: ${{ secrets.GH_APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
2026/2/20 23:50追記
https://github.com/dependabot/fetch-metadata の alert-lookup でApp Tokenも使えることを追記するパッチを投げた
*1:追記にも書いたけどApp Tokenと一緒に使う例をパッチで投げました