依存ライブラリはメンテナンスされていますか？dep-doctorによるメンテナンスチェクのススメ

# はじめに

最近のソフトウェア開発では、ほとんどの場合、外部のライブラリを利用しています。これにより、開発者は本来解決したい課題の解決に専念できます。

外部ライブラリを使用することには利点がありますが、同時に新たな運用リスクも発生します。たとえば、依存している外部ライブラリにセキュリティの問題がある場合、影響を受ける可能性があります。

一般的なセキュリティの対応手順では、まず、ソフトウェアの脆弱性情報がデータベースに登録されます。次に開発者はその情報を入手し、分析してから、対策済みのバージョンに更新するか、パッチを適用するなどの対応を行います。しかし、メンテナンスされていないライブラリの場合、問題があっても対応されないことが多いはずです。

![vuls_flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36122/0caa98b0-3543-e821-e459-ddaea458ea2e.png)

また、言語のバージョンアップで破壊的な変更がある場合、依存しているライブラリが動作しなくなる可能性もあります。OSS であっても、送信したパッチやプルリクエストはメンテナンスが行われていないと取り込まれず、リリースされることはありません。

# dep-doctor とは

[dep-doctor](https://github.com/kyoshidajp/dep-doctor) は、ソフトウェアの依存ライブラリがメンテナンスされている事を確認する、Go 言語で作成した OSS です。

![dep-doctor_overview.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36122/76626d15-1773-277a-048b-55c72c3d7c47.png)


dep-doctor は次の流れで依存ライブラリのメンテナンス状況を確認します。

![dep-doctor_flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36122/159a10dd-4dd1-9dbb-e857-6dc129970f5e.png)


対応しているパッケージマネージャーは次のとおりです。

| 言語 | パッケージマネージャー | 依存ファイルの例 |
| -------- | ------------- | -- |
| Dart | pub | pubspec.lock |
| Erlang/Elixir | mix | mix.lock |
| Go | golang | go.mod |
| JavaScript | npm | package-lock.json |
| JavaScript | yarn | yarn.lock |
| PHP | composer | composer.lock |
| Python | pip | requirements.txt |
| Python | pipenv | Pipfile.lock |
| Python | poetry | poetry.lock |
| Ruby | bundler | Gemfile.lock |
| Rust | cargo | Cargo.lock |
| Swift | cocoapods | Podfile.lock |

対応しているソースコードリポジトリは GitHub のみです。

# dep-doctor のインストールと使い方

macOS や Linux では homebrew でインストールできます。

```
$ brew tap kyoshidajp/dep-doctor
$ brew install kyoshidajp/dep-doctor/dep-doctor
```

また、[バイナリファイル](https://github.com/kyoshidajp/dep-doctor/releases)を取得して、実行パスに追加して利用する事もできます。

実行前には環境変数 `GITHUB_TOKEN` に GitHub のアクセストークンを指定する必要があります。

次は、[bundler](https://bundler.io/) で管理している `/path/to/Gemfile.lock` のライブラリをチェックする例です。

```
$ dep-doctor diagnose --package bundler --file /path/to/Gemfile.lock
```

# dep-doctor の結果の見方

実行すると、例えば次のような出力が行われます。

```
concurrent-ruby
dotenv
faker
i18n
method_source
paperclip
......
[error] paperclip (archived): https://github.com/thoughtbot/paperclip
Diagnosis completed! 6 dependencies.
1 error, 0 warn (0 unknown), 0 info (0 ignored)
```

6つのライブラリがあり、[paperclip](https://github.com/thoughtbot/paperclip) のリポジトリがアーカイブされている事を表しています。

レポートのレベルは次を表しています。

| レベル | 内容 |
| :---: | :---------- |
| **error** | ソースコードリポジトリがアーカイブされている |
| **warn** | ソースコードリポジトリがメンテナンスされていない、あるいは情報が取得できない |
| **info** | その他 | |

# dep-doctor を GitHub Actions で動作させる方法

GitHub Actions を使って、定期的にチェックすることも可能です。

```yml
jobs:
  diagnose:
    name: dep-doctor
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: kyoshidajp/dep-doctor@v1
        with:
          version: v1.0.0 # or latest
      - run: dep-doctor diagnose --package golang --file go.mod
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

# Q&A

## メンテナンスの判定基準を変更できますか？

デフォルトでは、ソースコードリポジトリがアーカイブされているか、最終コミット日時が5年を経過していればメンテナンスされていないと判断します。

この期間を例えば7年に変更したい場合は `--year` を指定します。

```
$ dep-doctor diagnose --package bundler --file /path/to/Gemfile.lock --year 7
```

また、特定のライブラリは問題なしと判断する場合は、`--ignores` で、それらのライブラリを指定します。

```
$ dep-doctor diagnose --package bundler --file /path/to/Gemfile.lock \
  --ignores "lib1 lib2 lib3"
```

## 実行が遅くないですか？

パッケージレジストリに対してライブラリのソースコードリポジトリを取得するのに時間がかかっています。特に、対象のライブラリが多いと遅くなります。

この課題に対処するため、キャッシュの仕組みを導入しています。

dep-doctor を一度実行すると、カレントディレクトリに `.dep-doctor.yml` が作成されます。このファイルには、各ライブラリとそのソースコードリポジトリがパッケージマネージャーごとに記載されます。2回目以降の実行ではこの情報を読み取り、パッケージレジストリへの問合せを省略するため、処理が高速化されます。

```.dep-doctor.yml
package_managers:
    - name: yarn
      repositories:
        - name: node-combined-stream
          source_url: https://github.com/felixge/node-combined-stream
        - name: Object.getOwnPropertyDescriptors
          source_url: https://github.com/es-shims/Object.getOwnPropertyDescriptors
        - name: '@webassemblyjs/helper-module-context'
          source_url: https://github.com/xtuc/webassemblyjs
        - name: svg-arc-to-cubic-bezier
          source_url: https://github.com/colinmeinke/svg-arc-to-cubic-bezier
```

ライブラリのソースコードリポジトリはめったに変更されない想定なので、例えば CI で実行する際には `.dep-doctor.yml` をソースコード管理に含めると良いでしょう。

もし、この仕組みを利用したくない場合は次のように `--disable-cache` を指定します。

```
$ dep-doctor diagnose --package bundler --file /path/to/Gemfile.lock \
  --disable-cache
```

## ライバルとなるソフトウェアやサービスはありますか？

脆弱性の観点では脆弱性スキャンツールの [Depandabot](https://docs.github.com/ja/code-security/dependabot) や [Vuls](https://github.com/future-architect/vuls)、[Trivy](https://github.com/aquasecurity/trivy) などが競合しています。しかし、メンテナンス状況のチェックに焦点を当てると、一般的な OSS では競合するものが見当たりませんでした。

OSS を除くと、[yamory](https://yamory.io/) の [EOL管理機能](https://yamory.io/blog/start-eol/)は目的が近いかもしれませんが、詳細は不明です。dep-doctor ではライブラリが EOL を宣言していなくても、リポジトリがアーカイブされていたり、最終コミット日時が一定期間空いていないか、をメンテナンスのアクティブ判定に利用します。

また、[Black Duck](https://www.synopsys.com/ja-jp/software-integrity/security-testing/software-composition-analysis.html) にも dep-doctor と同様のリスク検出機能があるようです。こちらも詳細は不明です。

> Black Duckは各OSSプロジェクトのコミュニティ活性度に関する情報を保持・追跡しています。アップデート状況、コミット アクティビティ、コントリビュータ数などの情報に基づいて、古くなった・活動的でなくなったOSSとそのバージョンを独自の指標で評価します。
> [OSS管理＆SBOM管理ツール Black Duck｜日立ソリューションズ『Black Duck』とは？価格・システム・サービス概要や、解決できる課題についてご紹介](https://www.hitachi-solutions.co.jp/blackduck/) より

## Go 言語で書いてみてどうでしたか？

普段は Ruby のコードを書く事が多く、Go 言語だったらどう書くのがよいのか悩みながら進めました。

しかし、Go 言語では CLI を作成して実行するまでの環境が整備されていて、とても良い開発体験でした。これは、以前 GitHub CLI の作成で感じた事でもあります。

https://zenn.dev/kyoshida/articles/f4d451c8b32249

## 今後の展望はありますか？

Java の主要なパッケージマネージャーである gradle と maven に対応したいと考えています。ただし、これらのソースコードリポジトリまでたどれるパッケージレジストリが不明なので、調査中です。

また、パッケージレジストリは現在固定されていますが、将来的にユーザが指定できるようにする対応も検討中です。

現在の出力形式は標準出力と標準エラー出力のみですが、[SARIF](https://sarifweb.azurewebsites.net/) や SBOM 関連のフォーマットに対応する事を検討しています。

# 終わりに

外部ライブラリを利用するソフトウェアにおいて、依存ライブラリのメンテナンス状況を確認するツールとして dep-doctor を紹介しました。

12月といえば年末の大掃除のタイミングでもあります。普段開発しているソフトウェアの依存ライブラリがメンテナンスされているか、確認してみてください。