
Dependabot alertsのREST APIで、リポジトリ内の依存バージョンを直接取得できるようになりました
どうも!オペ部の西村祐二です!
Dependabot alertsをREST APIで取得して、脆弱性管理ツールやチケットに同期している方もいると思います。これまでのアラートには「脆弱なバージョンの範囲」は入っていましたが、「リポジトリで実際に使っているバージョン」は入っていませんでした。そのため、lockfileを別途読むか、GraphQLのvulnerableRequirementsを追加で問い合わせる必要がありました。
2026年10月20日のGitHub Changelogで、REST APIのレスポンスに version_requirement が加わりました。この記事では、自分のリポジトリのアラートを読み取り、lockfileとマニフェストで値がどう変わるか、GraphQLの値と一致するかを確かめました。あわせて、同期処理で扱うときの注意点をまとめます。
確認したのは、github.com上の自分のリポジトリ8件にあるアラート330件(オープン・修正済み・却下済み)を、GitHub CLIのgh apiで読み取った範囲です。
今回の変更点
- 対象: github.com と GitHub Enterprise Cloud with data residency(GitHub Enterprise Serverは3.24で対応予定)
- 追加された項目: 各アラートの
dependency.version_requirement - 返る場所: 個別アラート、repository / organization / enterpriseのアラート一覧、
dependabot_alertwebhookのpayload - 公式リンク: GitHub Changelog、REST API endpoints for Dependabot alerts
入るのは、Dependabotがそのアラートについて最後に記録した値です。値の中身は、Dependabotが依存関係をどこから検出したかで変わります。Changelogによると次の通りです。
| 検出元 | version_requirement に入る値 |
|---|---|
| lockfile | 解決済みのバージョン |
| lockfileのないマニフェスト | マニフェストに宣言したバージョンまたはバージョン範囲(~1.8.1 など) |
| Dependency submission API | ビルドツールが送信した解決済みのバージョン |
| 値がない場合 | null |
REST APIのOpenAPI定義には、空文字、64バイトでの切り詰め、1アラートに1値という注意書きもあります。同期処理への影響と合わせて後半で整理します。
手元のリポジトリで試してみる
環境
- macOS(arm64)
- GitHub CLI 2.102.0(
gh apiと組み込みの--jqを使用)。gh auth login済みで、対象リポジトリのDependabotアラートを閲覧できるアカウント - 対象: 自分のリポジトリのうち、Dependabot alertsが有効でアラートがある8件(Cargo.lock、Cargo.toml、package.json、package-lock.json、pnpm-lock.yaml、uv.lock、Gemfile.lock、packages.config)
操作はすべて読み取りだけで、アラートの状態は変更していません。
1. 個別アラートで項目を確認する
まずアラート番号(ここでは22)を指定して個別アラートを1件取得し、dependency を見ます。
gh api repos/<owner>/<repo>/dependabot/alerts/22 --jq '.dependency'
{
"manifest_path": "wasm/mandelbrot/Cargo.toml",
"package": { "ecosystem": "rust", "name": "wee_alloc" },
"relationship": "unknown",
"scope": "runtime",
"version_requirement": ">= 0.4.5,< 0.5.0"
}
manifest_path や relationship と並んで version_requirement が入っていました。リクエストヘッダー X-GitHub-Api-Version を 2022-11-28 にしても 2026-03-10 にしても、同じ値が返りました。OpenAPI定義でも、両方のAPIバージョンに記載があります。
2. lockfileとマニフェストで値がどう変わるか
version_requirement を、脆弱なバージョンの範囲(security_vulnerability.vulnerable_version_range)や最初の修正版(first_patched_version)と並べて一覧にするスクリプトを用意しました。対象はオープンなアラートだけです。
#!/bin/bash
# 使い方: alerts.sh <owner/repo> オープンなDependabotアラートの依存関係とバージョンを一覧にする
gh api --paginate "repos/$1/dependabot/alerts?state=open&per_page=100" \
--jq '.[] | [.dependency.package.name, .dependency.manifest_path,
(.dependency.version_requirement // "null"),
.security_vulnerability.vulnerable_version_range,
(.security_vulnerability.first_patched_version.identifier // "-")] | @tsv'
Rustの練習用リポジトリで実行し、出力からlockfile由来の行とlockfileのない Cargo.toml の行を抜き出して、見出し行を付けて列をそろえたのが次の画像です(画面の show.sh はこの抜き出しと整形を行う小さなスクリプトです)。

Cargo.lock 由来の rand は = 0.7.3 で、lockfileに書かれた解決済みバージョン 0.7.3 と一致しました。lockfileのない Cargo.toml の wee_alloc は >= 0.4.5,< 0.5.0 です。Cargo.toml の宣言は version = "0.4.5" です。Cargoはこの書き方をキャレット指定(^0.4.5)として扱うため、それを範囲に展開した値が記録されていました。
tokio が3行あるのは、同じ = 0.2.25 に対して別々のアドバイザリが3件出ているためです。脆弱な範囲と修正版はアラートごとに違い、3件すべてを解消するには一番大きい修正版(この例では 1.38.2)まで上げる必要があります。
もう1つ、lockfileを置いていないNode.jsのサンプルアプリでは、package.json の宣言どおり ~1.8.1 や ~1.17.1 が入っていました。
3. エコシステムごとの表記の違い
8リポジトリのアラートを、状態を絞らず(state=open,fixed,dismissed,auto_dismissed)取得し、マニフェストの種類ごとに値を集計しました。値の書き方はエコシステムによって違いました。
| マニフェスト / lockfile | 観測した値の例 |
|---|---|
| Cargo.lock、Gemfile.lock、uv.lock、packages.config | = 0.7.3、= 3.8.1(先頭に = が付く) |
| pnpm-lock.yaml、package-lock.json | 4.1.11、1.49.1(数字だけ) |
| package.json(lockfileなし) | ~1.8.1、~ 4.15.2 |
| Cargo.toml(lockfileなし) | >= 0.4.5,< 0.5.0 |
NuGetの packages.config はマニフェストですが、バージョンを固定で宣言するため、lockfileと同じ = x.y.z の形になっていました。
package.json の例では、同じ morgan でも古いアラートは ~ 1.8.1、新しいアラートは ~1.8.1 と、演算子の後の空白の有無が分かれていました。値はアラートごとに最後に記録されたものなので、記録された時期の表記がそのまま残っているように見えます。この表記ゆれは公式ドキュメントには書かれていないため、手元の観測として紹介します。
修正済み(fixed)と却下済み(dismissed)のアラートにも値が残っていました。
4. GraphQLの vulnerableRequirements と比べる
導入で触れたGraphQLの vulnerableRequirements と比べました。比べたリポジトリはアラートが100件以下なので、GraphQL側は first: 100 の1ページで全件が取れます。100件を超える場合はGraphQL側もページングが必要です。
gh api graphql -f query='
query {
repository(owner: "<owner>", name: "<repo>") {
vulnerabilityAlerts(first: 100) {
nodes { number vulnerableManifestPath vulnerableRequirements }
}
}
}' --jq '.data.repository.vulnerabilityAlerts.nodes[] | [.number, .vulnerableManifestPath, .vulnerableRequirements] | @tsv' | sort > gql.tsv
gh api --paginate 'repos/<owner>/<repo>/dependabot/alerts?per_page=100' \
--jq '.[] | [.number, .dependency.manifest_path, .dependency.version_requirement] | @tsv' | sort > rest.tsv
diff gql.tsv rest.tsv
Rustリポジトリの41件は、番号・マニフェスト・値がすべて一致し、diff は何も出力しませんでした。Node.jsのサンプルアプリの8件も、~ 1.8.1 の空白まで同じ値でした。比べた2リポジトリ49件の範囲では、GraphQLへ別に問い合わせなくても、REST APIの一覧だけで同じ値が取れます。
同期処理に組み込むときの注意点
次の項目は、手元の観測とOpenAPI定義の説明に基づきます。
- バージョン番号とは限らない: lockfileがなければ
~1.8.1や>= 0.4.5,< 0.5.0のようなバージョン範囲が入ります。lockfileやマニフェストから検出したアラートでは、manifest_pathがlockfileを指すかどうかで、解決済みバージョンかバージョン範囲かを見分けられます - 表記はエコシステムごとに異なる:
=の有無や空白の違いがあります。修正版との大小比較に使う場合は、エコシステムごとのバージョン比較ライブラリに渡す前に=や空白を取り除く正規化が必要です - 同じ値が複数のアラートに出る: アドバイザリごとに別のアラートになるため、依存関係単位でまとめるときは修正版の最大値を取ります
nullと空文字を区別する: 今回の330件では出ませんでしたが、OpenAPI定義では値がなければnull、バージョンを指定していない依存関係では空文字になりうると説明されています- 64バイトで切り詰められる: 長いバージョン範囲は途中で切れ、範囲として正しく読めないことがあります
- 1アラートに1値: 1つのアラートに複数の脆弱なバージョンが対応する場合、記録されるのは1つで、すべての脆弱なバージョンを表すとは限りません。全バージョンを把握したい場合は、lockfileやSBOMの確認がまだ必要です
まとめ
Dependabot alertsのREST APIに version_requirement が加わり、GraphQLへ別に問い合わせなくても、アラート一覧からリポジトリ内の依存バージョン(lockfileがなければ宣言したバージョン範囲)が取れるようになりました。手元のリポジトリでは、lockfile由来なら解決済みバージョン、lockfileのないマニフェストなら宣言したバージョン範囲が入っていました。値はGraphQLの vulnerableRequirements とも一致しました。
一方で、値はバージョン範囲の場合があり、表記もエコシステムごとに異なります。同期処理に入れるときは、lockfile由来かどうかを manifest_path で見分け、比較に使う前に = や空白を取り除いておきます。organization / enterpriseの一覧APIとwebhookは今回試していないため、使う前に同じ値が入るか確認するのがよさそうです。
誰かの参考になれば幸いです。
参考リンク:






