「半年前に納品したシステムで使っているライブラリに、深刻な脆弱性が見つかった」——保守契約を結んでいる受託開発では、こうした連絡が突然飛んできます。問題は、それをこちらから先に気づけているかです。顧客やニュースで指摘されてから慌てて調べるのと、CI が毎回チェックしていて検知済みなのとでは、信頼がまるで変わります。Python のプロジェクトで「使っている依存に既知の脆弱性がないか」を継続的に見張る仕組みは、保守の質を直接左右します。
gihyo.jp が報じたとおり、高速パッケージマネージャーとして広く使われるようになった uv に、脆弱性確認コマンド uv audit がプレビューとして加わりました。これまで pip-audit など別ツールを併用していた部分を、依存管理と同じ uv の中で完結できるようになります。本記事では、受託保守で「依存の脆弱性を取りこぼさない」仕組みを uv audit でどう作るか、そして運用上どこに気をつけるかを、開発の立場から整理します。
なぜ「依存の脆弱性」が後手に回るのか
依存ライブラリの脆弱性対応が後手に回るのは、たいてい仕組みがないからです。三つのパターンに分かれます。
ひとつは、そもそも監視していない。納品時点では最新でも、ライブラリは日々アップデートされ、脆弱性も日々公開されます。一度納品したきり誰もチェックしていなければ、危険な状態に何ヶ月も気づけません。
ふたつめは、手動チェックで形骸化する。「リリース前に手で確認する」運用は、忙しくなると飛ばされます。人の善意に頼った確認は、いずれ抜けます。
みっつめは、間接依存が見えていない。直接書いた依存だけでなく、その依存が引き込む孫依存にも脆弱性は潜みます。ロックファイル全体を見ないと、本当のリスクは把握できません。
uv audit は、この三つに対して「ロックファイルに固定された依存ツリー全体を、機械的に・継続的にチェックする」という答えを出します。人の記憶ではなく、CI のジョブとして回せるのが肝です。依存のバージョンを揃え続ける運用そのものの重要性については、モノレポで依存のズレを断つ記事でも扱いました。脆弱性監査は、その「揃えた依存」が安全かを見張る次のレイヤーです。
uv audit の基本 — ロックファイルを脆弱性データベースに突き合わせる
uv audit は、プロジェクトのロックファイル(uv.lock)に記録された依存を、既知脆弱性のデータベースと突き合わせて、該当するものを報告します。直接依存だけでなく、ロックされた依存ツリー全体が対象になるのがポイントです。
# プロジェクトの依存に既知脆弱性がないかチェックする
uv audit
脆弱性が見つかると、どのパッケージのどのバージョンが、どの脆弱性(CVE 等)に該当し、どのバージョンで修正されているかが報告されます。あとは該当パッケージを修正版に上げ、ロックファイルを更新する流れです。
# 該当パッケージを修正版へ更新し、ロックを取り直す
uv lock --upgrade-package <パッケージ名>
プレビュー段階のコマンドのため、オプションや出力形式は今後変わる可能性があります。本番運用に組み込むときは、uv のバージョンを固定し、出力の解釈を自分たちで一度検証してから自動化に乗せるのが安全です。
受託保守でCIに組み込む
uv audit の価値は、手で叩くことではなく、CI で自動的に・定期的に回すことで出ます。受託保守では、次の二系統で走らせるのが定石です。
| トリガー | 目的 |
|---|---|
| Pull Request ごと | 新たに入れた依存が脆弱性を持ち込んでいないかを、マージ前に止める |
| 日次/週次の定期実行 | コード変更がなくても、新しく公開された脆弱性を検知する |
二つめが特に重要です。脆弱性は「自分のコードが変わったとき」ではなく「世の中で新しく公開されたとき」に発生します。コードが半年動いていなくても、その間に依存が危険になることはある。だからこそ、定期実行で「コードは変わっていないが、依存のリスクが変わった」を拾う必要があります。
GitHub Actions に組むなら、定期実行のワークフローでこう走らせるイメージです。
# .github/workflows/audit.yml(抜粋)
on:
schedule:
- cron: '0 0 * * 1' # 毎週月曜に実行
pull_request:
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pipx install uv
- run: uv audit
検知したときに「誰が・いつまでに対応するか」まで決めておかないと、アラートだけが溜まります。重大度に応じて、PR を自動で止めるのか、通知だけ出して保守の定例で扱うのかを切り分けるのが現実的です。ソースコードやシークレットの監査と同じく、検知の仕組みと運用フローはセットで設計する必要があります。この「検知して終わりにしない」考え方は、ソースコードの機密情報を監査する記事で扱った観点と共通します。
受託で仕組み化したときの実例
弊社が保守を引き継いだある社内業務システム(社名は伏せます)は、Python 製で稼働して二年が経っており、依存の脆弱性チェックはリリース時に担当者が思い出したときだけ手で行う運用でした。引き継ぎ時に一度棚卸ししたところ、すでに修正版が出ている既知脆弱性を持つライブラリが複数残っていました。コードは安定して動いていたぶん、誰も依存の劣化に気づいていなかったのです。
私たちは uv への移行と同時に uv audit を CI に組み込み、PR ごとのチェックと週次の定期実行の二系統を設定しました。検知時は重大度で振り分け、深刻なものは即時の更新 PR を起票し、軽微なものは月次保守でまとめて対応する運用にしました。やったのは、属人的な「思い出したらやる」を、機械が毎週やる仕組みに置き換えただけです。これで「気づいたら何ヶ月も危険な依存を抱えていた」という状態が構造的に起きなくなりました。
この案件で一番効いたのは、定期実行を入れたことでした。PR ごとのチェックは「新しく持ち込む脆弱性」しか止められません。すでに動いているシステムのリスクは、定期実行でしか拾えない——ここを最初から二系統で組んだことが、保守の安心感に直結しました。
どこから着手するか
まず、いま保守しているPythonプロジェクトに「依存の脆弱性を継続的にチェックする仕組みがあるか」を確認するのが出発点です。なければ、ローカルで一度 uv audit を走らせ、現状のリスクを把握する。そのうえで、PR ごとと定期実行の二系統を CI に組み、検知時の対応フロー(誰が・いつまでに)を決めれば、脆弱性対応は「思い出したらやる」から「仕組みが見張る」状態になります。
納品したシステムの脆弱性対応が後手に回っている、保守契約で「依存の安全」をどう担保するか整理したい、CI に監査を組み込みたい——そうしたお悩みがあれば、グリームハブのお問い合わせからご相談ください。現行プロジェクトの依存と CI を拝見し、脆弱性を取りこぼさない保守の仕組みづくりをお手伝いします。