GitHub Actionsのワークフローにおいて、サードパーティ製アクションを利用する際のバージョン指定は「コミットハッシュ(SHA)指定」がセキュリティ上のベストプラクティスとされています。しかし、コミットハッシュ指定を行うと可読性が下がるため、行末にバージョン情報をコメントとして付与する運用が一般的です。
このとき、コメントの書き方次第でDependabotが「SHAとコメントの両方を更新してくれる」場合と、「SHAだけ更新してコメントが古いまま放置される」場合があります。
この記事では、コミットハッシュ指定の重要性をおさらいしたうえで、Dependabotの内部実装(dependabot-core)の観点から、どのような書き方が更新対象になるのか・ならないのかを技術的に解説します。
1. コミットハッシュ指定の重要性
GitHub Actionsの uses ステートメントで actions/checkout@v4 のような「タグ指定」を用いると、その参照は可変(Mutable)となります。
過去に発生した tj-actions/changed-files の侵害インシデントのように、攻撃者がリポジトリを乗っ取り、既存のタグを悪意のあるコードを含む別のコミットに付け替えた場合(タグのハイジャック)、利用者のCI/CDパイプラインで自動的に不正なコードが実行されるサプライチェーン攻撃のリスクがあります。
一方、40文字のコミットハッシュ(例: actions/checkout@8e5e7e5cb813f0ea429bcaf7f9e8a58742fb63b8)は不変(Immutable)です。監査した時点のコードから改ざんされることはありません。
しかし、これでは人間が「どのバージョンを使っているのか」判別できなくなるため、以下のようにコメントを併記する運用が推奨されています。
uses: actions/checkout@8e5e7e5cb813f0ea429bcaf7f9e8a58742fb63b8 # v4.2.2
Dependabotは、適切に記述されていれば新しいリリースが出た際にハッシュとコメントの両方を一括でアップデートするPRを自動生成してくれます。
2. Dependabotの更新対象になる書き方・ならない書き方
Dependabotは特定のフォーマットに従ったコメントのみをバージョン文字列として認識します。
更新対象になる書き方
ハッシュの後ろに # を置き、シンプルにバージョン(タグ名)のみを記載する形式です。
# Dependabotがハッシュとコメントの両方を更新する
uses: actions/checkout@<SHA> # v4.2.2
uses: actions/checkout@<SHA> # v4
uses: actions/checkout@<SHA> # 4.2.2
更新対象にならない(ハッシュのみ更新される)書き方
コメント内にバージョン以外の余分な文字列が含まれていたり、元々の記述が間違っていたりすると、Dependabotはコメントを更新できず、SHAのみが更新される(=コメントのバージョンが古いまま取り残される)という事態に陥ります。
# パターン1: 余計なプレフィックスや文字列が含まれる
uses: actions/checkout@<SHA> # pin @v2.4.0
uses: actions/checkout@<SHA> # version 2.4.0
# パターン2: コミットハッシュとコメントのバージョンが元から不一致
uses: actions/checkout@<古いSHA> # v99.9.9 (間違った記載)
3. dependabot-core の実装から読み解く挙動の違い
なぜこのような挙動の違いが生じるのかは、Dependabotのコアロジックである dependabot/dependabot-core リポジトリの GitHub Actions エコシステム向け実装(github_actions/lib/dependabot/github_actions/ 配下)を見ることで理解できます。
FileParser による依存関係の抽出
Dependabotの FileParser クラス(file_parser.rb)は、YAMLファイルから正規表現(Regex)を用いて uses: の行を抽出します。
40文字のSHAが指定されている場合、パーサーはその後ろにあるコメント(# 以降の文字列)を先読みし、それがセマンティックバージョニング(SemVer)や有効なタグフォーマットに合致するかを評価します。
この際、正規表現はシンプルにバージョン名として解釈できる文字列を抽出するよう設計されているため、# pin @v2.4.0 などの人間のための自然言語が混ざると、パーサーはそれを「現在利用している有効なバージョン文字列」として正確に認識・保持できなくなります。
FileUpdater における置換ロジックのフェイルセーフ
アップデートが検知され新しいプルリクエストを作成する際、FileUpdater クラス(file_updater.rb)が実際のファイルの書き換えを行います。
SHA文字列の更新は一意な40文字のハッシュを置換するだけなので確実ですが、コメントの書き換えにおいては、Rubyの文字列置換処理を用いて「Parserが認識していた以前のバージョン文字列」と「ファイル上の文字列」の厳密なマッチングを試みます。
-
推奨フォーマットの場合: Parserが認識した
v1.0.0とファイル上の# v1.0.0が合致するため、安全に# v2.0.0に置換されます。 -
非推奨フォーマットの場合: 文字列にノイズが含まれていて一致しない、あるいはパース段階でバージョンとして認識されていない場合、
FileUpdaterは**「誤検知による意図しないコメントの破壊」を防ぐフェイルセーフとして、インラインコメントの置換をスキップします**(Issue #5314 などの原因)。
また、**既にSHAとコメントのバージョンが不一致の場合(Issue #7912)**についても、Dependabotは「現在の正確な状態(タグとSHAが正しく紐づいていること)」を前提として差分を生成するため、前提が壊れているコメントを推測で無理に上書きすることはしません。その結果、ハッシュのみが最新化され、不一致なコメントがそのまま残ることになります。
4. まとめとベストプラクティス
サードパーティ製アクションのサプライチェーン攻撃を防ぐためにコミットハッシュ指定は必須と言えますが、Dependabotを活用して継続的に運用するためには、コメントのフォーマットを遵守する必要があります。
ベストプラクティス:
余計な装飾(pin や version などの文字)は一切含めず、純粋なバージョン番号のみをコメントします。
uses: 組織名/リポジトリ名@<40文字のSHA> # vX.Y.Z