LoginSignup
20
7

More than 1 year has passed since last update.

license-checkerでnpmの依存ライブラリのライセンスを確認する

Last updated at Posted at 2021-10-04

みなさんは日々の開発でライブラリなどを利用する際に、きちんとライセンスを確認していますか?

OSS(に限らないですが)を利用する際には、ライセンスに従う必要があります。
ライセンスによっては著作権表示を求められたり、それらを組み込んだソースコードもまた公開しなければならないものもあります。

今回はlicense-checkerを用いて、Node.jsプロジェクト内で利用しているパッケージのライセンスを確認してみたいと思います。

license-checkerを使ってみる

license-checker
https://www.npmjs.com/package/license-checker

ツールなのでグローバルにインストールしてもいいと思いますが、個人的にはdevDependenciesに足したほうがチームで共有できたりCIなどでも利用できるのでよいかと思います。1

ここでは適当に作った空プロジェクトで試してみましょう。
license-checkerもまたnpmパッケージなので、色々な依存関係のあるパッケージが入ります。試すのにちょうど良いですね。

# 適当にプロジェクトを作る
mkdir hello
cd hello
yarn init -y

# license-checkerを入れる
yarn add -D license-checker

package.jsonの中身はこんな感じです。
devDependencieslicense-checkerがいます。

package.json
{
  "name": "hello",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "license-checker": "^25.0.1"
  }
}

準備はできました。
では早速実行してみましょう。

yarn license-checker
実行結果
├─ abbrev@1.1.1
│  ├─ licenses: ISC
│  ├─ repository: https://github.com/isaacs/abbrev-js
│  ├─ publisher: Isaac Z. Schlueter
│  ├─ email: i@izs.me
│  ├─ path: /Users/ynakamura/projects/sandbox/hello/node_modules/abbrev
│  └─ licenseFile: /Users/ynakamura/projects/sandbox/hello/node_modules/abbrev/LICENSE
├─ ansi-styles@3.2.1
│  ├─ licenses: MIT
│  ├─ repository: https://github.com/chalk/ansi-styles
│  ├─ publisher: Sindre Sorhus
│  ├─ email: sindresorhus@gmail.com
│  ├─ url: sindresorhus.com
│  ├─ path: /Users/ynakamura/projects/sandbox/hello/node_modules/ansi-styles
│  └─ licenseFile: /Users/ynakamura/projects/sandbox/hello/node_modules/ansi-styles/license
├─ array-find-index@1.0.2
│  ├─ licenses: MIT
│  ├─ repository: https://github.com/sindresorhus/array-find-index
│  ├─ publisher: Sindre Sorhus
│  ├─ email: sindresorhus@gmail.com
│  ├─ url: sindresorhus.com
│  ├─ path: /Users/ynakamura/projects/sandbox/hello/node_modules/array-find-index
│  └─ licenseFile: /Users/ynakamura/projects/sandbox/hello/node_modules/array-find-index/license
├─ asap@2.0.6
│  ├─ licenses: MIT
│  ├─ repository: https://github.com/kriskowal/asap
│  ├─ path: /Users/ynakamura/projects/sandbox/hello/node_modules/asap
│  └─ licenseFile: /Users/ynakamura/projects/sandbox/hello/node_modules/asap/LICENSE.md
            :
            :

めちゃくちゃいっぱい出てきました。

プロジェクトの直接の依存パッケージであるlicense-checkerだけでなく、その依存ツリーのパッケージ全てが出力されます。

--directという直接の依存パッケージのみに絞るオプションもあるのですが、v25.0.1の時点ではうまく機能しませんでした。2

色々な出力形式

CSVやJSON

--csv--jsonオプションを指定すると、CSVやJSONといった形式で出力してくれます。

yarn license-checker --csv
実行結果
"module name","license","repository"
"abbrev@1.1.1","ISC","https://github.com/isaacs/abbrev-js"
"ansi-styles@3.2.1","MIT","https://github.com/chalk/ansi-styles"
"array-find-index@1.0.2","MIT","https://github.com/sindresorhus/array-find-index"
"asap@2.0.6","MIT","https://github.com/kriskowal/asap"
"balanced-match@1.0.2","MIT","https://github.com/juliangruber/balanced-match"
"brace-expansion@1.1.11","MIT","https://github.com/juliangruber/brace-expansion"
"chalk@2.4.2","MIT","https://github.com/chalk/chalk"
"color-convert@1.9.3","MIT","https://github.com/Qix-/color-convert"
"color-name@1.1.3","MIT","https://github.com/dfcreative/color-name"
"concat-map@0.0.1","MIT","https://github.com/substack/node-concat-map"
            :
            :

ライセンス毎のサマリー

--summaryを指定するとライセンス毎のパッケージ数が出力されます。
ざっと把握するにはよいでしょう。

yarn license-checker --summary
実行結果
├─ MIT: 32
├─ ISC: 19
├─ Apache-2.0: 2
├─ BSD-3-Clause: 1
├─ BSD-2-Clause: 1
├─ CC-BY-3.0: 1
├─ CC0-1.0: 1
└─ (MIT AND CC-BY-3.0): 1

その他のオプションなど

他にも特定のパッケージを除外したり、dependenciesのみを対象としたりするオプションがあります。

便利そうなオプションだけピックアップしておきます。
完全なオプション一覧はREADMEを見るか、--helpで確認してください。

オプション 内容
--summary ライセンス毎のサマリー表示る
--production dependenciesのみ出力する
--development devDependenciesのみ出力する
--unknown 推測したライセンスをunknownとして扱う
--onlyunknown unknownと推測のみ出力する
--json JSON形式で出力する
--csv CSV形式で出力する
--exclude [list] 指定のライセンスのパッケージを除外する(セミコロン区切り)
--excludePackages [list] 指定のパッケージを除外する(セミコロン区切り)
--packages [list] 指定のパッケージのみ出力する(セミコロン区切り)
--excludePrivatePackages プラベートパッケージを除外する
--failOn [list] 指定のライセンスがあったらエラーにする(セミコロン区切り)
--onlyAllow [list] 指定のライセンス以外があったらエラーにする(セミコロン区切り)
--direct 直接の依存パッケージについてのみ出力する(v25.0.1現在うまく機能しない)

どうやってライセンスを検出しているのか

ところでlicense-checkerはどうやってパッケージのライセンスを判定しているのでしょうか。

ドキュメントによると、node_modulesの中を順番に見ていき、各package.jsonlicenseから拾うようです。
package.jsonから拾うのに失敗した場合は、LICENSE, LICENCE, COPYING, READMEといったファイルの中身を見て、ライセンス固有の文字列が含まれているかどうかで判定するようです。

応用編: CIやコミットフックなどで自動検出する

さて、ここからはCIなどに組み込んで、特定のライセンスが含まれていたら自動的に検出するような仕組みを考えてみたいと思います。

パッと思いつくのは--failOnオプションですね。
これを使えば特定のライセンスが含まれていた場合に、コマンドの終了ステータスが1になるので、これで検出することができそうです。

試してみましょう。

yarn license-checker --failOn MIT
実行結果
Found license defined by the --failOn flag: "MIT". Exiting.
error Command failed with exit code 1.

うまくいったようです。

これをCIやGitのpre-commitフックなどに組み込んでおくことで、いつのまにかGPLなライブラリが入り込んでいた、といった事態を避けることができます。

Gitのpre-commitに設定してみる

下記のような感じのスクリプトを.git/hooks/pre-commitに作ります(chmod +xするのを忘れずに)。

$EDITOR .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
#!/bin/sh

# 許容しないライセンス
DISALLOW_LICENSES="GPL;GPL-2.0;GPL-3.0;LGPL;LGPL-3.0"

# package.json, package-lock.json, yarn.lock に変更があったときだけチェックする
if git diff --cached --name-only $against | grep --quiet --word-regexp -E 'package(-lock)?\.json|yarn\.lock'
then
    result=0
    yarn license-checker --failOn "$DISALLOW_LICENSES" >/dev/null || result=$?
    if [[ ! "$result" = "0" ]]; then
        yarn license-checker --csv
        exit 1
    fi
fi

exit 0

これでpackage.json, package-lock.json, yarn.lockに変更があったときにライセンスチェックが走って、許容しないライセンスが含まれていた場合は、コミットに失敗するようになります。

コミットのたびに走ると重いので、パッケージ情報に変更があったときだけ走るようにしてみました。

CIでも同じようなスクリプトを書いて、最初に実行しておけばよいでしょう。

注意点

--exclude--failOn等に指定するライセンス名ですが、完全一致でしかマッチしないようなので注意が必要です。

例えばGPL-3.0なパッケージがあったとして、--failOn GPLとか--failOn GPL3.0とか--failOn gpl-3.0としても引っかかりません。--failOn GPL-3.0でなければいけません。

また、(MIT OR GPL-3.0)のような表記(デュアルライセンス)もMITGPL-3.0(GPL-3.0 OR MIT)(逆順)では引っかかりません。--failOn "(MIT OR GPL-3.0)"などと指定しないといけません。

このあたりをもう少し柔軟に判定しようとすると、grepを駆使するとか、JSON形式をパースするといった対応が必要になってきます。

安全側に倒すなら--csvの結果をgrep -i gplとかでチェックして、デュアルライセンスなど問題なさそうなものを随時--excludePackagesに足していくとかでもいいかもしれません。

ただし、--excludePackagesの場合、バージョンが上がってライセンスが変更されている場合に、気づかずに除外してしまうという、別の問題はありそうです。

応用編: ライブラリとして使う

license-checkerはライブラリとしても使えるインターフェースを提供しているので、前項のような複雑な条件に対応するスクリプトを書くとか、パッケージとライセンスの一覧をまとめた画面コードを生成するといった用途にも使えるかと思います。

以下のコードはREADMEからの引用。

var checker = require('license-checker');
 
checker.init({
    start: '/path/to/start/looking'
}, function(err, packages) {
    if (err) {
        //Handle error
    } else {
        //The sorted package data
        //as an Object
    }
});

packagesが各パッケージ情報の配列になっていて、その中にパッケージ名やバージョン、ライセンスなどの情報が入っているようです。

その他のライセンスチェッカー

  • microsoft/license-checker-webpack-plugin
    • Microsoft製
    • JavaScript製
    • Webpack pluginとして動く
    • ビルド結果に含まれるものしかチェックできない
      • devDependenciesとかチェックできない
  • github/licensed
    • GitHub製
    • Ruby製
    • 色々な言語のプロジェクトに対応している
  • franciscop/legally
    • JavaScript製
    • Node.jsプロジェクトのみ
    • きれいなテーブルで表示してくれる
    • package, License, READMEを別々に表示してくれる
  • go-enry/go-license-detector
    • Go製
    • 特定ディレクトリに対してチェックできる
    • LICENSEREADMEから判定
    • 依存ツリーのチェックはできない
  1. 難点はlicense-checker自身の依存ツリーも結果に出てきてしまうこと。

  2. https://github.com/davglass/license-checker/issues/191

20
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
7