GitHub ActionsでDependabotの脆弱性修正PRのMerge処理を自動化する

2022.04.30

こんにちは、CX事業本部 IoT事業部の若槻です。

前回のエントリでは、Dependabotにより脆弱性を修正するPull Requestが自動でOpenされる所まで確認してみました。

この脆弱性修正を、プロダクトへすぐに反映させるために自動化したい場合もあると思います。

今回は、GitHub ActionsでDependabotのOpenしたPull RequestのMerge処理を自動化してみました。

やってみた

こちらのドキュメントを参考にやってみます。

準備

対象のRepositoryの[Dependabot security updates]を有効化します。

Repositoryにdependabot config fileを作成します。Dependabot security updatesの作成するPull Requestに対する処理の自動化を行いたいので、open-pull-requests-limitプロパティを0とします。(もしくは不要であればconfig fileごと削除しても良いです。)

.github/dependabot.yml

version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "daily"
    open-pull-requests-limit: 0

RepositoryをローカルにCloneし、必要に応じてnpm initをしたら、あえてDependabot alertで検出される脆弱性を含むDependencyをインストールします。

$ npm install moment@2.24.0

$ npm ls --depth=0
test-220501@1.0.0 /Users/wakatsuki.ryuta/projects/cm-rwakatsuki/test-220501
└── moment@2.24.0

脆弱性がちゃんと含まれていますね。

$ npm audit
# npm audit report

moment  <2.29.2
Severity: high

また、まだの場合は.gitignorenode_modulesを指定してCommit対象から除外します。

Workflow Configの作成

次のようなWorkflow Config fileを作成します。DependabotによりOpenされたPull Requestのメタデータ取得と、自動Mergeを行っています。

.github/workflows/automate_dependabot.yml

name: Dependabot auto-merge
on: pull_request

permissions:
  pull-requests: write
  contents: write

jobs:
  dependabot:
    runs-on: ubuntu-latest
    if: ${{ github.actor == 'dependabot[bot]' }}
    steps:
      - name: Dependabot metadata
        id: metadata
        uses: dependabot/fetch-metadata@v1.1.1
        with:
          github-token: "${{ secrets.GITHUB_TOKEN }}"

      - name: Enable auto-merge for Dependabot PRs
        if: ${{ steps.metadata.outputs.dependency-type == 'direct:production' }}
        run: gh pr merge --auto --merge "$PR_URL"
        env:
          PR_URL: ${{github.event.pull_request.html_url}}
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
  • GitHub Actionsではsecrets.GITHUB_TOKENに既定でReadonly権限のトークンが設定されますが、それをpermissionsで記載した内容で拡大しています。
  • if: ${{ github.actor == 'dependabot[bot]' }}により、Pull RequestがDependabotにより自動Openされた時のみjobを実行するよにしています。
  • dependabot/fetch-metadata actionにより、WorkflowをトリガーしたPull Requestに関するDependabotのメタデータを取得できます。
  • 取得したメタデータでは次の情報が含まれています。
    • dependency-names:アップデートされるDependencyの名前。
    • dependency-type:アップデートされるDependencyのタイプはdevelopmentproductionのいずれかであるか。
    • update-type:アップデートのされ方。セマンティックバージョニングの範囲内のアップデートであればversion-update:semver-patchという値が入る。
  • if: ${{ steps.metadata.outputs.dependency-type == 'direct:production' }}のようにメタデータを使用した判定処理を入れることが可能です。ここではProdectionのDependencyの場合にのみApproveをするようにしています。

動作確認

ここまでの変更をCommitしてリモートのデフォルトブランチにpushします。(実際にはfeatureにpush→デフォルトブランチにmergeという流れだと思いますが簡単のため省略)

するとDependabotによりalertが作成され、Pull RequestがOpenされました。

そしてそれをトリガーにしてWorkflowも実行され、正常に完了しました。

Pull Requestを見ると、Pull RequestがGitHub ActionsのBotによりMergeされています。

ハマった箇所

dependabot/fetch-metadataで必要な権限

最初次のようにWorkflow ConfigでPermissionをcontents: writeのみとして試しました。

.github/workflows/automate_dependabot.yml

name: Dependabot auto-merge
on: pull_request

permissions:
  contents: write

jobs:
  dependabot:
    runs-on: ubuntu-latest
    if: ${{ github.actor == 'dependabot[bot]' }}
    steps:
      - name: Dependabot metadata
        id: metadata
        uses: dependabot/fetch-metadata@v1.1.1
        with:
          github-token: "${{ secrets.GITHUB_TOKEN }}"

しかしdependabot/fetch-metadataで403エラーとなりました。

そこでPermissionにpull-requests: writeを追加するとエラー無く動作するようになりました。メタデータを取得するだけなのでReadonlyのPermissionだけでも十分かと思いきやwrite権限も必要でした。

おわりに

GitHub ActionsでDependabotのOpenしたPull RequestのMerge処理を自動化してみました。

Dependencyのアップデートはプロダクトの動作に影響が出る可能性があるため本来なら検証環境での動作確認や対人レビューをきちんと経た上で行いたいところですが、ゼロデイ攻撃の恐れのある脆弱性が検知された場合はそんな悠長なことはして要られません。そのような場合に脆弱性への対処を最優先とするために、今回紹介した方法で脆弱性が検知されてから本番環境(あるいはステージング環境)へ反映されるまでのリードタイムを短縮することができます。Apache Log4jの件もあった昨今ですので一考に値するかと思います。

以上