yarnのresolutionsで全ての依存関係をコントロールする

yarnでセキュリティパッチを確実に当てるためのアプローチ

Soudai Sasada

Soudai Sasada

yarnでauditを実行しアドバイザリーレポートに報告された脆弱性のあるパッケージを更新しようとしていたのですが、直接依存しているパッケージでのバージョンアップはともかく依存するパッケージが依存しているパッケージ、そのまたさらに依存するパッケージが依存しているパッケージなど、アプリケーションが直接依存しているわけではない深いところで脆弱性のあるバージョンが指定されていると、直接依存しているパッケージのバージョンを上げてもそのパッケージが依存しているパッケージで脆弱性のあるバージョンに依存したままだと結局は脆弱性を抱えたまま動かさなくてはなりません。

こうした問題の解決策としてyarnのSelective Dependency Resolutionsを使うことで直接/間接を問わずアプリケーションが依存している全てのパッケージをコントロールすることが可能です。

Selective Dependency Resolutions

package.jsonのルートにresolutionsというフィールドとして直接依存を指定します。これによりその指定に従い間接的に依存しているパッケージも含めた全てのバージョン管理が可能です。

package.json
{
  "name": "project",
  "version": "1.0.0",
  "dependencies": {
    "gulp": "^4.0.2",
    "gulp-iconfont": "^11.0.0"
  },
  "resolutions": {
    "gulp/**/y18n": "^3.2.2",
    "xmldom": "^0.5.0"
  }
}

ここではresolutionsで直接依存しているパッケージが間接的に依存しているパッケージも含めて全てのxmldomへの依存に対し^0.5.0と指定しています。これにより依存先での指定に関わらずこのアプリケーションで使われるxmldomは全て0.5.0以上が使われることになります。
また、gulp/**/y18nと指定することでgulpの依存先にあるy18nだけをバージョン指定することも可能です。

注意事項

頻繁にアップデートされているパッケージの場合は後方互換性などなんらかの制約によりバージョンを上げることができない場合がありそうしたケースではresolutionsを使って強制的に依存をアップデートしてしまうことで正しく動かなくなる可能性があります。

  • どこでどのように使われているか
  • パッケージが指定していたバージョンとresolutionsで指定したバージョンの差異はなにか
  • バージョンを直接指定してしまうことでどのような問題が考えられるか

これらを確認し慎重に運用する方が良さそうです。また、新しく依存関係を追加する場合も当然この指定が適用されるので上述のxmldomの例のように全てに適用するような指定は避けy18nの例のように特定のパッケージごとに指定する方が安全でしょう。

まとめ

脆弱性が報告されていてもアプリケーションに直接影響を及ぼすとは限りません。また、依存パッケージが依存しているパッケージのバージョンを上げることで正常に動かなくなるリスクもあります。
まずは脆弱性の内容を確認し対応すべきかどうかを検討し、対応が必要であれば直接依存しているパッケージですでに脆弱性に対応したバージョンがないか探しましょう。もしなければ次はIssueやPull Requestを確認します。大抵の場合は(Dependabotのようなツールもあるので)すでにPull Requestがオープンされているかもしれません。もしそれらもなければ同じように困っている方がいるはずなのでPull Requestをオープンしコントリビューションすることも検討しましょう。パッケージがメンテされていなければ同じことができる別のパッケージを検討するのも手です。

様々な選択肢を考慮したうえでなにが最適かは状況によりますがresolutionsによる依存の解決はパッケージが想定していない使い方をするということになるので様々な選択肢を考慮したうえでの最終手段として捉え常用することがないようにするべきでしょう。

ドキュメントはこちらになります。

https://classic.yarnpkg.com/en/docs/selective-version-resolutions/

この記事がお役に立てたら一杯のコーヒーを恵んで頂けると励みになります 😊

Buy Me A Coffee
© 2020, Soudai Sasada