Dependabot auto-triage rules の書き方と挙動を検証してみた

Dependabot auto-triage rules の書き方と挙動を検証してみた

Dependabot alerts はパスで除外できないので、auto-triage rules による自動 dismiss を検証しました。書き方の落とし穴と適用タイミングの仕様をまとめています。
2026.08.26

こんにちは!製造ビジネステクノロジー部の石井です。

Dependabot alerts、便利なんですが、たまに「そこは見なくていいんだよ」というディレクトリからもアラートが飛んできます。

私が担当している案件のリポジトリにも、焼き直し元の旧システムのソースコードを参考用に置いているだけのディレクトリがあります。
参照用なので、依存関係を新たにインストールすることもないですが composer.lockpackage-lock.json が置いてある以上、Dependabot は真面目にスキャンしてアラートを上げてきます。

手動で dismiss すればいい話ですが、毎回となると手間です。

そこで今回は Dependabot auto-triage rules を使用してこの手間を解消できないか、検証用のサンドボックスリポジトリを作って確かめてみました。

なお本記事は、Dependabot alerts がどういうものかはある程度ご存じの前提で進めます。「そもそも Dependabot って何をしてくれるの?」というところから知りたい方は、先にこちらの記事を読んでいただくとスムーズです。

https://dev.classmethod.jp/articles/using-dependabot-alertssecurity-updates-on-github/

Dependabot alerts は特定のパスを除外できない

まず前提として、Dependabot alerts には「このフォルダパスは見ないで」という設定がありません。

dependabot.ymlignore は「このパッケージのこのバージョン範囲を無視する」という指定なので、パス単位の除外には使えません。
directory の指定はバージョンアップ PR を作る対象を決めるもので、アラートの発生自体には効きません。
アラートの元になっている Dependency graph 側にも、スキャン対象パスを絞る設定は用意されていません。
つまり、manifest ファイルがリポジトリに存在する限り、アラートは出ます。
本ブログで扱う Dependabot auto-triage rules は、「出さない」ではなく「出たものを自動で片付ける」ためのものです。

Dependabot auto-triage rules とは

アラートに対して「条件に合うものを自動で dismiss する」「条件に合うものは PR を作る」といったルールを設定できる機能です。

管理画面は Settings → Advanced Security → Dependabot rules。ここに2種類のルールが並びます。

Settings の Advanced Security にある Dependabot rules の項目

Dependabot rules の画面。GitHub presets が2本並んでいる

GitHub presets は GitHub があらかじめ用意しているルールです。

  1. Dismiss low-impact alerts for development-scoped dependencies
    開発用の依存(npm でいう devDependencies)に出た、影響の小さいアラートを自動 dismiss するもの。public リポジトリではデフォルトで有効になっています。

  2. Dismiss package malware alerts
    「全バージョンが悪意あるパッケージとしてフラグされた」タイプのアラートを自動 dismiss するもの。社内パッケージが公開の悪性パッケージと名前衝突した場合の誤検知対策で、デフォルトは無効。

もう1つが Repository rules。こちらが自分で作るカスタムルールです。

カスタムルールに書けること

カスタムルールは「Target alerts(どのアラートを対象にするか)」と「Rules(何をするか)」の2つを決めます。

Dependabot rules 画面の右上にある New rule ボタン

New rule の作成画面。Rule name、State、Target alerts、Rules を指定する

Target alerts に指定できるメタデータは、ドキュメント上10種類。

条件 補足
Manifest path リポジトリレベルのルール限定。組織レベルでは指定不可
Severity critical / high / moderate / low
Package name
Ecosystem npm / pip など
Dependency scope development か runtime か
Patch availability 修正版が出ているかどうか
CVE ID
GHSA ID
CWE
EPSS Score 悪用されうる確率のスコア

アクションは2種類。

  1. Dismiss alerts
    Indefinitely(無期限)か Until a patch is available(修正版が出るまで)のどちらかを選びます。

  2. Open a pull request
    Dependabot に修正 PR を作らせます。Dismiss indefinitely を選んでいる場合や、Dependabot security updates が有効な場合は選べません。

使う前に知っておきたい制約

検証前の調査で引っかかったポイントが3つあります。

  1. private リポジトリでは GitHub Code Security のライセンスが必要
    public リポジトリなら無料で使えます。

  2. カスタムルールを操作する API が無い
    REST も GraphQL も提供されていません。UI から手で作るしかなく、Terraform などでのコード管理もできません。

  3. 1リポジトリあたり10ルールまで
    実際に10本作ってみたところ、11本目は作れませんでした。エラーが出るのではなく New rule ボタンが非活性になります。GitHub presets の2本は別枠で、この10本にはカウントされません。なおドキュメントには「public preview 中は10ルールまで」とありますが、機能自体は2024年6月に GA 済み。preview 期の記述が残ったまま、上限だけ生きている状態です。

    ここで注意したいのが、Disabled にしたルールも枠を消費することです。10本埋まった状態で1本を無効化してみましたが、ボタンは非活性のまま。使わなくなったルールを無効化して保管しておく、という運用はできません。空けるには削除するしかありません。

10本に達すると New rule ボタンが非活性になる

検証用のサンドボックスを作った

今回は意図的に脆弱な依存を詰め込んだ public リポジトリを別に用意し、そこで検証しました。

サンドボックスの構成。6つのディレクトリに合計41件のアラート

構成はこんな感じです。

ディレクトリ パッケージ ecosystem scope アラート
docs/legacy-app/.../pkg-a/ lodash@4.17.11 npm runtime 7件
docs/legacy-app/.../pkg-b/ minimist@0.0.8 npm runtime 2件
packages/app/ node-forge@0.9.0 npm runtime 15件
packages/dev-tools/ qs@6.5.1 npm development 2件
packages/unpatched/ request@2.88.2(推移依存含む) npm runtime 6件
services/api/requirements.txt PyYAML / Jinja2 pip runtime 9件

合計41件。critical 6 / high 15 / medium 17 / low 3 という内訳になりました。
なお packages/unpatched/ の6件のうち、request 本体のアラートは1件だけです。残り5件は request が引き込む推移依存(form-data / tough-cookie / uuid / qs)に出ています。この後の検証で form-data や uuid が出てきたら、ここの住人だと思ってください。

ひとつだけ注意点

検証を始める前に、GitHub presets を無効にしておく必要がありました。

プリセットの「Dismiss low-impact alerts for development-scoped dependencies」は、public リポジトリでデフォルト有効です。
これが動いていると、開発依存の低リスクなアラートがプリセット側で勝手に閉じられます。自分の作ったルールが効いたのか、プリセットが消したのか、区別がつかなくなる。

サンドボックスに限った話ではありません。本番でも「ルールを作ったら思ったより減った」ときは、プリセットの仕業かもしれない。
切り分けるなら、まずプリセットの状態を確認するのが早いです。

構成の詳しい意図は、リポジトリの README に書いてあります。

ルールの条件はどう書くのか

ここからが実際の設定です。Target alerts 欄に何をどう書けるのか、1つずつ試しました。

カンマ区切りは使えない

複数の値を並べたいとき、まずカンマを試したくなります。severity:critical,high のように。

severity has an invalid value: critical,high

弾かれました。

紛らわしいのはここからで、弾かれるかどうかがフィールドによって違います。

manifest: にワイルドカードを入れた場合も、同じようにエラーになります。

Target alerts にワイルドカードを入れると manifest has an invalid value と表示される

入力 結果
severity:critical,high エラー。保存できない
manifest:docs/legacy-app/** エラー。保存できない
package:uuid,qs 保存できる。ただし該当0件
manifest:パスA,パスB 保存できる。ただし該当0件

severity はカンマをエラーとして弾いてくれます。manifest もワイルドカードは弾きます。値の検証自体はしています。

ところが manifest のカンマ区切りは、その検証をすり抜けます。
しかも該当0件。ルール一覧には Matches manifest:パスA,パスB としっかり表示されるので、設定できたようにしか見えません。

これがいちばん間違えやすい組み合わせです。複数のディレクトリを1本のルールでカバーしようとしてカンマでつなぐと、エラーも出ず、一覧にも正しく表示され、それでいて何ひとつ dismiss されない。

複数の値を指定したいなら、キーごと繰り返す

ではどう書けばいいのかですが、答えは、カンマではなくキーごと並べることでした。

package:uuid package:qs

これで両方のアラートが同時に閉じられました。同じキーを繰り返すと OR になります。

manifest: でも同じです。2つのディレクトリを繰り返しで指定したところ、両方のアラートがまとめて閉じられました。カンマでつないで0件だったのと同じ2パスです。

つまりスペース区切りの意味は、キーが同じかどうかで変わります。

書き方 意味
package:uuid package:qs OR。どちらかに当てはまれば対象
package:qs scope:development AND。両方に当てはまるものだけ対象
package:uuid,qs 使えない

「メタデータの種類ごとに AND、同じ種類の中では OR」という動きです。SQL で書くなら WHERE package IN ('uuid', 'qs') AND scope = 'development' の形。

入力欄の下には Rules will be applied for alerts matching all included metadata. と書かれていますが、この all は種類単位で読む必要があります。値単位で読むと誤解します。

これが分かれば、複数のディレクトリを1本のルールでカバーできます。ルールを分ける必要はありません。10ルールの上限も気にしなくて済みます。

ただし、ここで注意点がひとつ。ルール一覧の表示は、スペース区切りで入力してもカンマ区切りに揃えられます。

ルール一覧。カンマ区切りで作ったルールとスペース区切りで作ったルールが同じ表示になっている

上が0件、下が8件のルールです。表示上はまったく同じ文字列なのに、片方は何も閉じません。一覧を眺めてルールの正しさを判断することはできない、ということになります。

入力した記法 一覧の表示 実際の対象
カンマ区切り manifest:パスA,パスB 0件
スペース区切り manifest:パスA,パスB 8件

編集画面を開けば入力どおりの文字列が出てきます。manifest: が1つならカンマ区切り、2つ並んでいればキーの繰り返しです。

編集画面。カンマ区切りで保存されたルールは manifest が1つだけ

編集画面。キーの繰り返しで保存されたルールは manifest が2つ並んでいる

既存のルールが効いているか怪しいときは、一覧ではなく編集画面を開く。
もしくは、閉じた件数を数えることです。

条件の書き方3パターン。同じキーの繰り返しは OR、違うキーは AND、カンマは使えない

なお GitHub の発表ブログに載っているルール作成画面のスクリーンショットには、severity: low,medium scope: development manifest: otter/package-lock.json という作例が写っています。この記法は現在の UI ではエラーになります。公開当時から仕様が変わったのか、画像がモックだったのかは分かりませんが、そのまま真似すると動きません。

severity にカンマ区切りを入れると severity has an invalid value と表示される

指定できるキーの早見表

Target alerts の入力欄をクリックすると候補が出てきます。これが実際に使えるキーの一覧で、11個ありました。ドキュメントの表記と食い違うものや、ここまでで見た落とし穴もあるので、1枚にまとめておきます。

キー 値の形式 記述例 注意点
severity: critical / high / moderate / low severity:critical medium ではなく moderate。API とは名前が違う
package: パッケージ名 package:node-forge カンマ区切りは黙って0件。キーの繰り返しで OR
ecosystem: npm / pip など ecosystem:pip
scope: runtime / development scope:development
manifest: manifest ファイルのフルパス manifest:packages/app/package-lock.json ワイルドカード不可。カンマ区切りは黙って0件
cwe: 数字のみ cwe:1321 CWE-1321 ではなく 1321。ドキュメントに記載なし
cve_id: CVE ID cve_id:CVE-2021-44906 ドキュメントの表記は CVE-ID
ghsa_id: GHSA ID ghsa_id:GHSA-p8p7-x288-28g6 ドキュメントの表記は GHSA-ID
epss: >n <n >=n <=n epss:>0.1 ドキュメントの表記は epss_percentage
malware: package, version 未検証 候補にだけ現れる。ドキュメントに記載なし
classification: malware, vulnerability classification:vulnerability 候補にだけ現れる。ドキュメントに記載なし

記述例は malware: を除き、実際にルールを作って動作を確認したものです。malware: だけは、このリポジトリにマルウェア判定のアラートが無いため試せませんでした。

severity の moderate は特に引っかかりやすいところです。API のほうは medium で通るので、API で下見した文字列をそのまま貼ると弾かれます。

10ルール上限の記述と同じで、ドキュメントが実装に追いついていない部分がまだ残っているようです。実際どう書けるかは入力欄の候補が教えてくれるので、迷ったらそこを見るのが早いです。

逆に、条件として書けそうで書けないものが2つあります。

条件 状況
Patch availability 対応するキーが無い。アクション側の Until patch is available として選ぶ
relationship:(直接依存か推移依存か) ルールでは指定できない。alerts API 専用のフィルタ

Patch availability はドキュメントが「指定できるメタデータ」として挙げているのに、条件としては書けません。条件として書けるものとアクションとして選ぶものが同じ一覧に並んでいるので、最初は条件のつもりで探してしまいました。

ルールは実際どう効くのか

ここからが本題です。ルールを1本ずつ作っては消して、41件のアラートがどう動くかを観測しました。

既存のアラートにも遡って効く

まず manifest: を1つ指定したルールを作ってみます。

対象ディレクトリには7件のアラートがありました。ルールを作った瞬間、そのうち6件が auto_dismissed に変わりました。タイムスタンプは全件同一。体感では作成ボタンを押してリロードしたらもう終わっている、という速さです。

残る1件は、実験のため事前に手動 dismiss しておいたもの。こちらは手動の状態のまま据え置かれました。ルールが手動の判断を上書きすることはありません。

対象外の5ディレクトリ34件は open のまま。誤爆もありませんでした。

is:open で絞ったアラート一覧

resolution:auto-dismissed で絞り込むと、ルールが閉じたものだけを一覧できます。この検索構文は公式ドキュメントに記載がなく、is:auto-dismissed では引っかかりません。地味にハマりました。

resolution:auto-dismissed で絞ると、ルールが閉じたアラートだけが並ぶ

手で触ったアラートは、ルールの対象にならない

ここがいちばんの発見でした。

さきほど手動 dismiss していた1件を open に戻してみます。ルールの条件には合致しているので、また閉じられるはず。

閉じられませんでした。25分待っても open のまま。

決め手になったのは、そのあと別の条件(cwe:1321)でルールを新規作成したときの挙動です。この条件には問題の1件も合致します。にもかかわらず、対象になったのは他の2件だけ。

アラート ルール作成前 条件に合致 結果
#8 open する 閉じられた
#28 open する 閉じられた
#1 open(手動で reopen 済み) する 閉じられない

同じルール、同じ瞬間の判定です。違いは「人間が state を触ったかどうか」だけ。

つまり一度でも手動で dismiss や reopen をしたアラートは、以降どんなルールを作っても auto-triage の対象外になります。ドキュメントに明記はありませんが、不具合ではなく人の判断を尊重する仕様と読むのが自然でしょう。

実務上のポイントが2つあります。

  1. ルールの動作確認を「手動 reopen」でやろうとしても、絶対に動きません。テスト方法として成立しない

  2. 過去に手動 dismiss したアラートは、あとからルールを作っても auto_dismissed には変わらない。放置でよく、ルールは新しいアラートに対して効く

ルールを消せば元に戻る

では取り返しがつかないのかというと、そうでもありません。

ルールを削除すると、そのルールが閉じたアラートは open に戻ります。auto_dismissed_at もクリアされます。

しかもこの復帰は「システムによる変更」なので、さきほどの制約に引っかかりません。実際、削除で戻したアラートは別のルールで再び閉じることができました。

経緯 再びルールの対象になるか
手動 dismiss → 手動 reopen ならない
ルールで dismiss → ルール削除で復帰 なる

同じ open でも、そこに至った経緯で扱いが変わります。

適用範囲を間違えても消せば戻るので、思ったより気軽に試せます。検証中のリセットも、手動で state をいじるのではなくルール削除でやるべき。手動で触ると、そのアラートは以後ずっとルールの対象外になり、検証材料として使えなくなります。

Until patch is available の意味

アクションには IndefinitelyUntil patch is available の2つがあります。デフォルトは後者。

修正版の有無が異なる2つのパッケージで試したところ、こうなりました。

パッケージ 修正版 結果
request なし 閉じられた
form-data あり 閉じられない

Until patch is available は「修正版がまだ出ていないアラートを、出るまで伏せておく」という意味です。既に修正版があるものは最初から対象外。

使い分けはこうなります。

  • Indefinitely: 修正版の有無に関係なく閉じる。使っていないディレクトリを黙らせる用途
  • Until patch is available: 今すぐ直せないものだけ伏せる。修正版が出たら再び表に出てくる

なお Indefinitely を選ぶと Open a pull request to resolve alerts がグレーアウトして選べなくなります。これは仕様として明記されています。

Note that this option is unavailable if you have already selected the option to dismiss alerts indefinitely, or if Dependabot security updates are enabled in your repository settings.

Customizing auto-triage rules to prioritize Dependabot alerts

無期限で伏せると決めたアラートに PR を出しても仕方ないので、納得のいく制限です。引用の後半にあるとおり、Dependabot security updates が有効な場合も選べません。全件に PR を作る設定と、条件を絞って PR を作るルールは、同時には使えません。

Open a pull request は新しいアラートにだけ効く

もう一方の Open a pull request to resolve alerts は、dismiss とは挙動が違います。既存のアラートには効きません。

条件を満たした既存アラート15件を対象にルールを作って5時間待っても、PR もブランチも作られませんでした。一方、まだリポジトリに無いパッケージを対象にルールを先に作っておいて、あとからその依存を push すると、こうなります。

時刻 イベント
09:30:36 push
09:30:40 アラート発生
09:31:45 PR 作成

アラート発生から65秒。同じアクションが、既存アラートには5時間無反応で、新規アラートにはすぐ反応します。ドキュメントには「rules apply to both future and current alerts」とありますが、PR アクションは future のみと考えたほうがよさそうです。

既存のアラートに PR を作りたい場合は、アラート個別ページの Review security update ボタンから手動で作れます。

注意点が2つ。ひとつは、dismiss ルールのほうが先に評価されること。同じアラートに両方当たると、閉じられて PR は作られません。

Dismissal rules always act before rules which trigger Dependabot pull requests.

Customizing auto-triage rules to prioritize Dependabot alerts

もうひとつは、PR が作られてもアラートは open のままだということ。PR ルールは PR を作るだけで、アラートを閉じはしません。

閉じるのはマージした時点です。試したところ、マージから9秒でアラートが fixed になりました。dismissed でも auto_dismissed でもなく fixed。ルールで伏せたのか実際に直したのかが、state で区別される作りになっています。

どのルールが閉じたのかは、アラートのページで追える

ここまで状態の変化を追ってきましたが、実はアラートの個別ページに履歴が残っています。

dependabot dismissed this due to an alert rule
  Repository rule created and Dismiss node-forge was applied

dependabot reopened this
  Repository rule deleted: Dismiss node-forge

Repository rule created and ... was applied という書き方から、適用のきっかけがルールの作成であることが読み取れます。削除したときも Repository rule deleted として reopen が記録される。ルール名はリンクになっているので、そこから該当のルールに飛べます。

ルールが増えてきて「これはどれが閉じたんだ」となったときに使えます。

アラート個別ページのタイムライン。ルールの適用と削除による reopen が記録されている

ルールは常時動いているわけではない

ここまでを整理すると、ルールが評価されるタイミングは3つに絞られます。

  1. ルールを新規作成したとき(dismiss はここで既存アラートに遡って効く)

  2. ルールを削除したとき(そのルールが閉じたアラートが open に戻る)

  3. 新しいアラートが発生したとき(PR ルールはここでだけ発火する)

裏を返すと、それ以外のきっかけでは動きません。アラートの state を手で変えても、既存のルールは反応しない。定期的に全アラートを再スキャンしているわけでもない。ルールを直したのに反映されないときは、いったん消して作り直すのが確実です。

アラートの状態遷移。手を触れていないアラートはルールで行き来できるが、手動で操作したアラートは対象外になる

補足: 件数確認に API を使うときの注意

このあと触れる gh api は対象件数の確認に便利ですが、絞り込みの書き方はルールの Target alerts と一致しません。

severity:critical,high は API では OR として21件返しますが、ルールでは保存できません。severity:medium も API では通るのに、ルールは moderate しか受け付けない。epss_percentageepss のようにキー名が違うものもあります。

もっと勘違いしやすいのが、API は不正な値を黙って無視することです。

severity:bogus    → 41件(全件)
ecosystem:bogus   → 41件(全件)
scope:bogus       → 0件

打ち間違えたつもりが全件ヒットします。これを「この条件なら41件が対象か」と読んでルールに持っていくと、意図しない範囲に効いてしまいます。件数を数える用途では使えますが、ルールの書き方を試す場所ではありません。

まとめ

Dependabot alerts はパスで除外できないので、auto-triage rules で「出たものを自動で片付ける」のが現実的な打ち手になります。ルールを作れば既存のアラートにも数秒で遡って効きますし、範囲を間違えても消せば元に戻ります。

  1. カンマ区切りは使えない
    複数の値を指定するなら manifest:a manifest:b のようにキーごと繰り返します。

  2. 手で触ったアラートは対象外になる
    動作確認を手動 reopen でやろうとすると、いつまでも動かず悩みます。

  3. 書き間違いは静かに失敗する
    エラーで教えてくれるとは限りません。ルールを作ったら対象件数を必ず確認してください。UI なら resolution:auto-dismissed で絞り込めます。

  4. PR ルールは既存のアラートに効かない
    Open a pull request が発火するのは、ルール作成後に新しく発生したアラートだけです。既存ぶんは Review security update から手動で。

検証に使ったサンドボックスリポジトリは公開しています。構成と検証結果をまとめてあるので、同じことを試したい方の参考になれば。

https://github.com/yuta-ishii-cm/dependabot-auto-triage-rules-test

参考

おまけ

クラスメソッド名古屋オフィス(伏見駅徒歩5分)では、「なごやクラメソゆる勉強会」を月イチペースで開催しています。AWSやAIまわりの話題を、名前のとおりゆるめの雰囲気で。エンジニアでなくても楽しめる回もあるので、気軽にのぞきに来てください!

次回の開催情報はconnpassで告知しています(「なごやクラメソゆる勉強会」で検索)。
https://classmethod.connpass.com/

この記事をシェアする

関連記事