コンテンツにスキップ

依存元からの再解決の開発

  • 状態: 実装済み(本番運用環境で PR の作成を確認)
  • 開始日: 2026年10月15日
  • 最終更新: 2026年10月18日

目的

依存元の古い版指定のために直せない脆弱性を、依存元ごとプロジェクトの指定範囲内で上げて直せるようにする。 その際に脆弱性が増えていないかを、依存関係の位置ごとに確かめる。

依存元ごと上げて直す仕組み

修正 PR の運用で、@nestjs/platform-express 11.1.27 が multer を「2.1.1 ちょうど」に指定していたため、multer だけの更新では脆弱性を直せなかった。

更新前: プロジェクト → @nestjs/platform-express 11.1.27 → multer 2.1.1(脆弱)
更新後: プロジェクト → @nestjs/platform-express 11.2.7 → multer 2.4.0

依存元の @nestjs/platform-express を、プロジェクトの package.json の指定範囲内で新しい版に上げることで、multer も修正版になる。 これが依存元からの再解決である。

版はパッケージマネージャーが指定範囲内で選び直し、KestreLynx は範囲を広げない。

以前は依存元を通知に出す案も検討したが、依存元が分かるなら依存元ごと上げればよいと判断した。

方針

  • 通常の更新を先に行い、依存元の指定が原因と証明できる場合だけ再解決する
    • 通常の更新で直せるものまで、依存元を上げる対象に含めないため
  • 一番上の直接依存からプロジェクトの指定範囲内で解決し直し、範囲は広げず、overrides は使わない
    • プロジェクトが許している版の条件を守るため
  • 新しく入る版は公開から72時間待つ
    • 公開直後の版をすぐに取り込まないため

仕組みは依存元からの再解決、対応範囲と上限は再解決の対応範囲と上限にまとめている。

決めたこと

脆弱性を増やさないための検査

一番上の直接依存を上げると、その下にあるほかのパッケージも動くため、脆弱性が増える懸念があった。 そのため、次の条件を採用した。

  • どの依存経路でも版が下がらない
  • 通常更新の対象と再解決の起点を除き、ほかの直接依存は変わらない
  • 更新によって脆弱性が増えない

持ち越しの扱い

パッケージ名・版・脆弱性 ID を厳しく比べると、依存元が指定範囲内で直せない別の脆弱性を持っているだけで、ほとんど不合格になる。 そこで、同じ位置で版だけが変わり、同じ脆弱性が残る場合は「持ち越し」として認めることにした。

更新前: A 1.0 → Y 1.0(CVE-2 あり)
更新後: A 2.0 → Y 1.1(CVE-2 あり)

この例では、Y に残る CVE-2 を持ち越しとして扱う。 持ち越した脆弱性は直らず、今回直したい脆弱性を持ち越して合格にはできない。

位置ごとの検査

更新前: プロジェクト → A → X 1.0(脆弱性なし)
        プロジェクト → B → X 2.0(CVE-9 あり)

更新後: プロジェクト → A → X 2.0(CVE-9 あり)
        プロジェクト → B(X を使わなくなった)

「CVE-9 を持つ X」は更新前も更新後も1つで、件数は増えていない。 しかし、A の位置では脆弱性のなかった X が CVE-9 を持つ X に変わっているため、不合格になる。

  • 更新後に変わった依存関係それぞれについて、プロジェクトから同じ道筋をたどった更新前の位置を探す
  • その位置の旧版と比べて、新しい脆弱性がないかを確かめる
  • 更新前の位置が見つからない場合や、一つに決まらない場合は採用しない

位置ごとの検査では、次の変化があると、件数が増えていなくても更新を採用しない。 次の表では、→ は依存関係、⇒ は更新前から更新後への変化を表す。

変化 例(更新前 ⇒ 更新後) 採用しない理由
脆弱な新しいパッケージが入る A(X なし) ⇒ A → X(脆弱) 更新前の同じ位置に X がなく、脆弱性の持ち越しとして扱えない
脆弱なパッケージを使う依存元が増える A → X(脆弱) ⇒ A → X(脆弱), B → X(脆弱) B の位置に脆弱な X が新しく入る
新しい版に旧版にない脆弱性がある A → X 1.0(CVE-2) ⇒ A → X 2.0(CVE-2, CVE-9) 同じ位置の旧版になかった CVE-9 が加わる
1つの版が複数の脆弱な版に分かれる A → X 1.0, B → X 1.0 ⇒ A → X 1.1(脆弱), B → X 1.2(脆弱) 旧版から複数の脆弱な版への分岐は、持ち越しとして扱えない
複数の経路が1つに合流する A → X 1.0, B → X 2.0 ⇒ A → X 3.0, B → X 3.0 合流後の X と比べる旧版を一つに決められない

詳しい条件は再解決の検査条件にまとめている。

検証方法と結果

実際のパッケージマネージャーを動かす結合テスト

npm 10、pnpm 9.15.9・10.34.4・12.11.2、uv 0.13 を使い、再解決と検査の結果を確認した。

  • npm・pnpm では、mkdirp 0.5.1 → minimist 0.0.8 の例で、直接依存は mkdirp だけが上がり、minimist が修正版になることを確認した
  • rc 1.2.8 → minimist ^1.2.0 の経路は通常の更新で直せるため、rc の版を変えないことを確認した
  • 依存元を上げても対象の脆弱性を直せない場合に、不合格になることを確認した
  • uv では、pyopenssl の脆弱性は持ち越しに当たるが、cryptography に新しい脆弱性が入るため、不合格になることを確認した

本番運用環境での確認

2026年10月18日、反映後の最初の処理で、@nestjs/platform-express を 11.1.27 から 11.2.7 に上げ、multer を 2.1.1 から 2.4.0 にする PR が作られた。

位置ごとの検査も通った。 次の回は同じ内容の更新を再実行せず、既存の PR に対応付けた。

未検証・次の作業

  • 本番運用環境で作られた PR のマージ後の確認
  • 持ち越しを認めて採用する例は、結合テストの深刻度設定(HIGH・CRITICAL)では作れず、単体テストだけで確認
  • pip は依存元からの再解決の対象外
  • 上限の既定値を裏付ける実測

更新履歴

2026年10月18日

  • 依存元からの再解決の方針と検査
  • 持ち越しと位置ごとの検査
  • 結合テストと本番運用環境での確認

この記事は、KestreLynxの開発記録です。KestreLynxについて