脆弱性のあるJavaScriptライブラリを検出するRetire.jsを紹介

「[Webには時代遅れで脆弱性のあるJavaScirptライブラリで溢れていることが研究により明らかに](https://www.infoq.com/jp/news/2017/04/a-web-of-vulnerable-js-libs)」という記事のとおり、世の中には脆弱性のあるJavaScriptライブラリを使った多くのウェブサイトが存在します。

IPAが公開している「[安全なウェブサイトの作り方](https://www.ipa.go.jp/security/vuln/websecurity.html)」には`脆弱性が修正されたバージョンのライブラリを使用する。`と記述があったり、「[OWASP TOP10 2017 (日本語版)](https://www.owasp.org/images/2/23/OWASP_Top_10-2017%28ja%29.pdf)」にも`既知の脆弱性のあるコンポーネントの使用`を避けるように、と脆弱性のあるライブラリを使用しないよう注意喚起されています。

脆弱性のあるライブラリを使用しているか調べることができる[Retire.js](https://retirejs.github.io/retire.js/)を紹介します。
すべての脆弱性を防げる保証は無いので、あくまで自己責任でお使いください。

# Retire.jsとは
<img width="988" alt="スクリーンショット 2018-03-22 03.08.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/93166/0c444ee0-196b-13a6-56bb-328961060f4d.png">

[Retire.js](https://retirejs.github.io/retire.js/)は上の画像のようにCLIなどで実行することができる脆弱性のあるJSライブラリを検出するツールです。

[Retire.jsのページ](https://retirejs.github.io/retire.js/)には`The goal of Retire.js is to help you detect the use of JS-library versions with known vulnerabilities.`と記述されています。
拙い訳ですが、「Retire.jsは脆弱性があることがわかっているJSライブラリを検知する補助を行うことを目的としています。」という意味です。
[Retire.jsのページ](https://retirejs.github.io/retire.js/)にRetire.jsが検知する脆弱性の一覧が掲載されています。

# Retire.jsの使い方
使用方法は[Retire.jsのGitHub](https://github.com/retirejs/retire.js/)に掲載されています。
npmでインストールしてCLIやgulp・gruntで実行することができます。
ChromeやFirefoxのエクステンションも提供されています。

## CLI
まずはnpmのretire.jsを使用しコマンドラインから実行する方法を紹介します。
gulpから実行したい方やChromeやFirefoxのエクステンションで実行したい方は読み飛ばして下さい。

### インストール
npmで[retire](https://www.npmjs.com/package/retire)というパッケージをインストールする必要があります。
Retire.jsをローカルにインストールせずに使いたい方は後述があるのでインストール手順は読み飛ばして下さい。

```bash:グローバルにインストールする場合
$ npm install -g retire
```

```bash:プロジェクトのnode_modulesにインストールする場合
$ npm install --save-dev retire
```

調べたいプロジェクトの直下に移動して実行します。

```bash:グローバルにインストールした場合
$ retire
```

```js:node_modulesにインストールした場合
// package.jsonのscriptsに以下を追記
{
  "scripts": {
    "retire": "retire"
  }
}

// CLIで実行
$ npm run retire
```

脆弱性のあるライブラリがある場合、以下のような内容がコンソールに表示されます。
以下はlodash 1.0.2に脆弱性があるのを検知した例です。

```bash
{package.jsonに記載されているname} 0.0.0
↳ lodash 1.0.2
lodash 1.0.2 has known vulnerabilities:  severity: low; summary: Prototype pollution attack; https://hackerone.com/reports/310443
```

npmのバージョン5.2以上をお使いでローカルにインストールせずに使いたい方はnpxを使って実行することができます。
npxについては「[npm 5.2.0の新機能！ 「npx」でローカルパッケージを手軽に実行しよう](https://qiita.com/tonkotsuboy_com/items/8227f5993769c3df533d)」に詳しく紹介されています。

```bash:npxで実行
$ npx retire
```

## gulp
gulpのタスクとして定義して実行することも可能です。
gulpについては「[Gulp.js入門](https://qiita.com/yoshinariiii/items/8340286ec6f3ed6ff888)」をご覧ください。
[Retire.jsのGitHub](https://github.com/RetireJS/retire.js#gulp-task)に掲載されている例を元に紹介します。
gulpで実行する場合も[retire](https://www.npmjs.com/package/retire)をnpmでインストールする必要があります。

gulpfileに以下のようなretireというタスクを書きます。タスク名は任意なのでお好きな名前に変更してください。

```js:gulpfile.js
const gulp = require('gulp');
const spawn = require('child_process').spawn;
const gutil = require('gulp-util');

gulp.task('retire', function() {
  var child = spawn('retire', [], {cwd: process.cwd()});

  child.stdout.setEncoding('utf8');
  child.stdout.on('data', function (data) {
    gutil.log(data);
  });

  child.stderr.setEncoding('utf8');
  child.stderr.on('data', function (data) {
    gutil.log(gutil.colors.red(data));
    gutil.beep();
    process.exit(1);
  });
});
```

[Retire.jsのGitHub](https://github.com/RetireJS/retire.js#gulp-task)に掲載されているコードとの違いは`process.exit(1)`で異常終了させているところです。こうすることでnpm-scriptsで複数タスクを実行する場合に異常終了したら以降の処理を行わないように制御することができます。

CLIに比べてgulpにすることのメリットは、脆弱性のあるライブラリがあった場合の処理を追加できるところです。
上記のgulpタスクの場合だと、`gutil.colors.red(data)`でコンソール上のメッセージを赤く表示します。

# Chrome・Firefoxエクステンション
ChromeやFirefoxのエクステンションとしても使うことができます。
以下からダウンロードしてお使いください。
* [Chromeエクステンション](https://chrome.google.com/webstore/detail/retirejs/moibopkbhjceeedibkbkbchbjnkadmom?hl=fi0)
* [Firefoxアドオン](https://addons.mozilla.org/ja/firefox/addon/retire-js/)


こちらは他の開発者が開発したウェブサイトを検査するときにも使えます。
例えば、Wikipediaでは以下のような結果が出ました。

<img width="616" alt="スクリーンショット 2018-03-22 02.50.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/93166/f8fc4c80-4959-d285-55f1-2ec0c3d94397.png">


# 応用例
筆者が実際行っている応用例を紹介します。

## 公開前にチェックする
[pre-push](https://www.npmjs.com/package/pre-push)や[pre-commit](https://www.npmjs.com/package/pre-commit)と組み合わせれば、`git commit`や`git push`したときに脆弱性のあるライブラリがあればコミットやpushを行わないように制御することができます。

```json:package.json
{
  "scripts": {
    "retire": "retire"
  },
  "pre-commit": ["retire"]
}
```
## package.jsonに記載されているライブラリだけチェックする
Retire.jsはデフォルトではnode_modules以下もすべて検査します。
package.jsonに記載されたライブラリだけ検査したい場合は`-p`オプションを付けて実行します。

```bash:CLIの場合
$ retire -p
```

```js:gulpの場合
gulp.task('retire', function() {
  // 第二引数の配列にオプションを指定する
  var child = spawn('retire', ['-p'], {cwd: process.cwd()});

  child.stdout.setEncoding('utf8');
  child.stdout.on('data', function (data) {
    gutil.log(data);
  });

  child.stderr.setEncoding('utf8');
  child.stderr.on('data', function (data) {
    gutil.log(gutil.colors.red(data));
    gutil.beep();
    process.exit(1);
  });
});
```

## CIで実行する
GitHubとjenkinsやCircle CIなどCIツールを連携させて、pushされたときときに実行することも可能です。
GitHubとjenkinsの連携については「[GithubからJenkinsへのServer Hook](https://qiita.com/mechamogera/items/dbeb3a540f636bfed7af)」や「[Jenkinsでプルリクエストをビルドする](https://qiita.com/quattro_4/items/6b1962909ce868f12e5a)」を参考にしてください。

# まとめ
* 簡単に脆弱性のあるライブラリの使用を検出できる
* コミット前やデプロイ前に検査することで脆弱性のあるライブラリの使用を防止することができる
* 世の中のウェブサイトの脆弱性の検査をすることができる（悪用厳禁！）
* すべての脆弱性を防げるかはわからないので、これだけに頼らないこと！

#　参考
* [Retire.js](https://retirejs.github.io/retire.js/)
* [RetireJS/retire.js](https://github.com/retirejs/retire.js/)
* [npm 5.2.0の新機能！ 「npx」でローカルパッケージを手軽に実行しよう](https://qiita.com/tonkotsuboy_com/items/8227f5993769c3df533d)
* [pre-push](https://www.npmjs.com/package/pre-push)
* [pre-commit](https://www.npmjs.com/package/pre-commit)
* [GithubからJenkinsへのServer Hook](https://qiita.com/mechamogera/items/dbeb3a540f636bfed7af)
* [Jenkinsでプルリクエストをビルドする](https://qiita.com/quattro_4/items/6b1962909ce868f12e5a)

最後までお読み頂きありがとうございました。
不備や質問はコメント欄かTwitter([@shisama_](https://twitter.com/shisama_))などでご連絡ください。