Skip to main content

npmのfakerパッケージと、オープンソースライブラリに訪れた予期せぬ終焉

著者
blog feature snyk open source blue

2022年9月2日

0 分で読めます

オープンソースの依存関係はどこで終わりを迎え、なぜ終わるのでしょうか? npmのfakerモジュールには何が起きたのでしょうか? 同じことが再び起こる可能性はあるのでしょうか? オープンソースソフトウェアのライブラリがどのように人気を博し、そしてライフサイクルの終わりを迎えるのかを一緒に見ていきましょう。開発者や運用エンジニア向けの教訓も紹介します。

JavaScriptとPHPのエコシステムで起きた、いくつかの突然のソフトウェアサプライチェーンの混乱についてお話しします。気候や環境への懸念、政治や人道的な活動をめぐる法的紛争が、その原因となった事例です。

今回の事例に共通するテーマは、オープンソースの持続可能性、気候変動、オープンソースソフトウェアの自由をめぐるさまざまな問題をきっかけに生まれた、オープンソースの抗議活動としてのソフトウェア(プロテストウェア)です。

npmのプロテストウェアに関するもう一つの興味深い事例は、node-ipc npmパッケージのメンテナーが、政治危機を受けて行動を起こしたことです。

JavaScriptライブラリのleft-padとnpmメンテナーのAzer Koçulu

これは、ある企業によるオープンソース開発者への法的措置が、JavaScriptエコシステムにおける数千ものプロジェクト(Node.jsやBabelなど)を機能不全に陥らせた経緯を示す事例です。 €‹ €‹

left-pad npmパッケージのメンテナーであるAzer Koçuluは、Kikという別のnpmモジュールもメンテナンスしていました。どうやら、同名のインスタントメッセージアプリがあったため、Kikモジュールが企業の弁護士の目に留まったようです。弁護士から連絡を受け、npmから要請されても、Azerはパッケージ名の変更を拒みました。

訴訟を起こすと脅され(またnpm組織が双方の合意を取り付けられなかったこともあり)、Azerは抗議の意思表示として、250を超えるnpmパッケージをnpmレジストリから非公開にしました。

残念ながら、その中にはleft-padも含まれていました。left-padとは何でしょうか? left-padは、left-pad@1.0.0を例にすると、文字列をスペースで埋めるためのわずか17行のコードからなる小さなライブラリです。しかし、文字列を左側から埋めるだけでも、いくつかのエッジケースへの対応が必要です。最新バージョンの1.3.0は、2018年4月9日に次のように公開されました。

/* This program is free software. It comes without any warranty, to
     * the extent permitted by applicable law. You can redistribute it     * and/or modify it under the terms of the Do What The Fuck You Want
     * To Public License, Version 2, as published by Sam Hocevar. See
     * http://www.wtfpl.net/ for more details. */'use strict';
module.exports = leftPad;

var cache = [
  '',
  ' ',
  '  ',
  '   ',
  '    ',
  '     ',
  '      ',
  '       ',
  '        ',
  '         '
];

function leftPad (str, len, ch) {
  // convert `str` to a `string`
  str = str + '';
  // `len` is the `pad`'s length now
  len = len - str.length;
  // doesn't need to pad
  if (len <= 0) return str;
  // `ch` defaults to `' '`
  if (!ch && ch !== 0) ch = ' ';
  // convert `ch` to a `string` cuz it could be a number
  ch = ch + '';
  // cache common use cases
  if (ch === ' ' && len < 10) return cache[len] + str;
  // `pad` starts with an empty string
  var pad = '';
  // loop
  while (true) {
    // add `ch` to `pad` if `len` is odd
    if (len & 1) pad += ch;
    // divide `len` by 2, ditch the remainder
    len >>= 1;
    // "double" the `ch` so this operation count grows logarithmically on `len`
    // each time `ch` is "doubled", the `len` would need to be "doubled" too
    // similar to finding a value in binary search tree, hence O(log(n))
    if (len) ch += ch;
    // `len` is 0, exit the loop
    else break;
  }
  // pad `str`!
  return pad + str;
}

left-padは月間250万回以上ダウンロードされるほど人気がありました。そのため、left-padが取り下げられると、Node.jsやBabelなど、これに依存するプロジェクトが機能しなくなりました(詳細はこちら:http://left-pad.io/)。

npmコミュニティでは、依存パッケージが取り下げられたことによる影響が、npmレジストリにおける前例となりました。誰かがこのようなことをするとは、誰も思っていなかったのです。

こうして、ある開発者がたった17行のコードでJavaScriptエコシステムを壊した、という話が知られるようになりました。

JavaScriptで文字列を左側から埋めるには?

StringプロトタイプにはpadStart()とpadEnd()メソッドがあり、2つの引数を取ります。埋める文字数と、埋めるために使う文字列です。戻り値は、埋め込み後の新しい文字列です。このAPIはNode.js 8以降で利用でき、現在はすべての最新ブラウザーでサポートされています。

以下は、JavaScriptで文字列を左側から埋めるコード例です。

const someString = ‘h’;
console.log(someString.padStart(5, 'h'));

// output will be:
####

この新しいECMAScript仕様が追加されたのは、より豊かな言語APIをめぐる議論を生んだnpmのleft-padパッケージのおかげだと考える人は少なくありません。

PHP、Laravelなどのプロジェクトで偽データを生成するFakerライブラリ

環境への負荷を減らせるなら、成功したプロジェクトを手放せるでしょうか? 次に紹介するのは、地球環境の改善につながるなら自らの成功を脇に置く覚悟を持ったメンテナーの話です。

Françoisを紹介しましょう。2011年、François ZaninottoはFakerを公開しました。これは、データベースへの初期データ投入やテストに役立つ、偽データを作成するためのPHPライブラリです。

Fakerは成長を続け、Pythonなど他の言語向けのSDKも登場し、70以上のロケールをサポートする人気ライブラリとなりました。しかし、Fakerは単一の巨大なライブラリです。テストでユーザー名を偽装するために一部のデータセットだけ必要な場合でも、3MB分のデータセット全体をダウンロードする必要があります。

とはいえ、実際に使うロケールは1つだけという人がほとんどです。ライブラリのサイズはどれほどだったのでしょうか? Fakerのサイズは約3MBでした。開発ツール用のライブラリとして小さいとは言えませんが、非常に大きいわけでもありません(E2Eテスト用のChromeなどを考えてみてください)。ところが2019年までに、Fakerは生涯ダウンロード数が1億2,100万回を超えるほどの人気を集めていました。

少し考えてみてください。ライブラリのサイズを気にしたことはどれくらいありますか(明らかなフロントエンドのパフォーマンス最適化を除いて)? 長年にわたり、Fakerはおそらく11トンを超えるCO2換算排出量を生み出してきました。

Françoisはそのことを良心の呵責として抱えたくなかったため、このプロジェクトは現在終了しています。しかし、Françoisは誰かに引き継ぐこともできたのではないでしょうか? Françoisは、以前開発したORMのPHPライブラリProperlで苦い経験をしていました。彼にとってうまくいかなかったのです。

PHPライブラリのFakerについては、これでおしまいです。GitHub上のプロジェクトリポジトリはアーカイブされ、新しいリリースもプルリクエストもありません。とはいえ、オープンソースではプロジェクトのソースコードをフォークできます。開発者たちは、Fakerを新しいメンテナーとともに新たな組織へ移すことを提案しました。しかし、それではGitHub Starsの数を削除するなどして、プロジェクトが築いてきたオープンソースとしての評判を失うことになります。そのため、この案は実現しませんでした。

Françoisは、新しいメンテナーや開発者に向けて最後にこう書き残しました。「興味があれば、まずFakerをフォークしてみてください。Fakerよりずっと良いものになるでしょう。ただし、数年後にはきっと後悔するでしょう ;)」

npmのFakerライブラリ

このFaker.jsライブラリも、テストで使える大量のランダムデータを生成するのに役立ちますが、その経緯は先ほどのPHP版とは大きく異なります。

「もう無償では働かない」と、Marak Squiresは2020年11月、自身のリポジトリに投稿したGitHub Issueで述べました。

オープンソースソフトウェアのメンテナンスは、大きな負担です。コードを書くことから、ドキュメント作成を含む定期的なプロジェクトの保守、リリースの公開、変更履歴ファイルの更新、新しい開発者やコントリビューターを歓迎する環境づくり、質問への回答、報告された問題の調査、プルリクエストのレビューまで、作業は多岐にわたります。健全なオープンソースプロジェクトを維持するために必要な作業の一部を挙げただけでも、これだけあります。

私たち開発者の多くも、営利企業も、多くのオープンソースソフトウェアを利用しています。しかし、何かを還元できているでしょうか?

Marakはオープンソース開発者の一人であり、いくつかのオープンソースプロジェクトのメンテナーです。ある時点で月間1,200万回以上ダウンロードされた、人気のnpmパッケージfaker.jsをメンテナンスしています。ただし、先ほどのPHPライブラリFakerの話と混同しないでください。別々のプロジェクトであり、メンテナーも経緯も異なります。

Marakの仕事は、Fortune 500企業の開発者を支援し、最終的には企業の成長にも貢献していました。しかし、多くのメンテナーと同じく、彼自身が得るものはあまりありませんでした。メンテナーの仕事には、資金も評価もほとんど与えられていません。

2020年11月、Marakはオープンソースの持続可能性、あるいはその欠如について、意思を示すことにしました。メンテナーはGitHubリポジトリにIssueを立て、今後は無償で作業をしないと表明しました。

「Marakからの無償作業はもう終わり—報酬を払うか、このプロジェクトをフォークしてくれ #1046」と題されたGitHubのIssue。コメントには『フューチュラマ』の抗議シーンが表示されている。

このGitHub Issueに寄せられた絵文字の反応を見る限り、Marakの主張はコミュニティに広く受け入れられ、共感を得ていました。Marakの声は届きましたが、その行動は目立った長期的な印象を残しませんでした。

2022年1月5日、メンテナーはセマンティックバージョニングで6.6.6となる新バージョンのFaker.js npmパッケージを公開しました。これにより、faker@6.6.6はnpmレジストリからダウンロードできる有効なバージョンとなりました。

新しい6.6.6では、Faker.js npmパッケージの元のソースコードがすべて削除されていました。この新しいメジャーバージョンをインストールすると、機能が壊れてしまいます。

新たに公開したfaker@6.6.6での声明に合わせ、Faker.jsのGitHubリポジトリにもコミットを強制的にプッシュし、ソースコード全体を削除しました。

プロジェクトファイルと「What really happened with Aaron Swartz?」という見出しのREADMEが表示されたGitHubリポジトリページ
masterブランチと、Marakによる「endgame」コミットが表示された、ダークテーマのGitHubコミット履歴。コミットハッシュは2c4f82f。

壊れたnpmパッケージは、利用者にどれほどの影響を与えるのでしょうか? それを評価するには、npmパッケージのバージョン管理と、npmパッケージマネージャーの一般的な動作を理解する必要があります。

簡単に言うと、プロジェクトですでにfakerを使っている場合、ロックファイルで依存関係を固定しているかどうかにかかわらず、npmパッケージマネージャーは通常、マイナーまたはパッチバージョンの範囲で最新バージョンを取得します。ただし、現在使っているFaker.jsのメジャーバージョン内にとどまるため、「安全圏」は保たれます。これは、faker@5.5.3が最後の安全なメジャーバージョンであり、壊れたメジャーバージョンfaker@6.6.6を使うには、明示的なアップグレードが必要だからです。エンドユーザーに自動的に提供されることは、通常ありません。

では、このメンテナーは意思表示をしたものの、実際に困った人はいなかったのでしょうか? 話は続きます。Marakは、月間約1億ダウンロードに影響するDenial of Serviceの脆弱性をcolors npmパッケージに仕込みました。

オープンソースライブラリを責任を持って使うためのポイント

以下では、npmパッケージにおけるプロテストウェアやメンテナンスされていないソフトウェアによるセキュリティ上の影響を避けるために、オープンソースソフトウェアを管理する実践的な方法を4つ紹介します。

1. オープンソースライブラリのインフラを効率的に管理する

オープンソースライブラリの利用可能性に依存する開発者として、何ができるでしょうか? レジストリからパッケージを再インストールするのは非効率で時間がかかり、再現性のない結果を招く可能性もあります。

npmやPyPIなどの公開レジストリからパッケージやオープンソースライブラリが消えたり、使われなくなったりする事態に備えるには、オープンソースのVerdaccioプロジェクトのような、ローカルのプライベートプロキシやキャッシュプロキシの利用をおすすめします。VerdaccioのメンテナーであるJuan Picadoにインタビューしました。インタビューを読んで、オープンソースライブラリの管理についてさらに詳しくご覧ください。

2. オープンソースライブラリを吟味する

新しいオープンソースパッケージを採用するときや、既存のパッケージの代替を検討するときは、プロジェクトのコミュニティ、これまでのメンテナンス実績と現在の活動状況、セキュリティ体制を時間をかけて評価しましょう。プロジェクトの品質、テストの有無、利用可能なドキュメントなども重要な要素です。

npmパッケージの品質を評価する際に役立つツールの一つが、無料で便利なリソースであるSnyk Advisorです。オープンソースパッケージのさまざまな側面を定量的に評価できます。たとえば、Snyk Advisorはnodemon npmパッケージの健全性スコアを提供しています。

nodemon v2.0.7のnpmページ。パッケージの健全性スコア90/100、類似パッケージ、ダウンロード数、コミット頻度、プロジェクト指標を表示

3. ロックファイルで依存関係を固定する

ロックファイルを使い、確認済みのバージョンに依存関係を固定することは、決定論的なインストールを実現する優れた方法です。これはJavaScriptエコシステム全般で有効であり、CIなどで予期せぬ事態を防ぐために広く知られている手法です。

ロックファイルを使い始めたばかりで、仕組みがよくわからない場合は、依存関係管理の仕様と動作を理解するために、package-lock.jsonとはを読むことをおすすめします。

npmFakerのワークフローを示す図。npmプロジェクトのpackage-lock.jsonを使って依存関係をインストールし、コントリビューター向けにyarn.lockを保持します。

4. 推移的依存関係を固定する

状況によっては、node_modulesツリーに追加される推移的依存関係のバージョンを制御できないことがあります。これは、それらの依存関係が他のメンテナーが宣言した依存関係の一部として含まれているためです。

とはいえ、npmやyarn、pnpmなどのパッケージマネージャーには、package.jsonファイルで許容するバージョン範囲を指定して、ネストされた依存関係のバージョンを上書きする方法が用意されています。

その影響を受け、非常に重要な意味を持った例を紹介します。peacenotwarモジュールの改ざんと、npmパッケージnode-ipcによるプロテストウェアが発生した際のことです。

"overrides": {
    "node-ipc@>9.2.1 <10": "9.2.1",
    "node-ipc@>10.1.0": "10.1.0
  }

最後に

オープンソースソフトウェアのセキュリティとアクティビズムは、私にとって大切なテーマです。このブログ記事は、2022年5月にバレンシアで開催されたCloud Native SecurityConでの講演をもとにしています。ご興味があれば、こちらから録画をご覧いただけます。

The Unexpected Demise of Open Source Libraries - Liran Tal, Synk

また、GitリポジトリをSnykに接続し、自動セキュリティ修正や依存関係のバージョンアップを受け取ることで、オープンソースの依存関係のセキュリティにプロアクティブに取り組むことを強くおすすめします。

CTFを始めよう

オンデマンドのバーチャル入門ワークショップを視聴して、CTFの課題の解き方を学びましょう。

続きを読む

feature insights context
Blog

自律型攻撃はすでに始まっている。防御もそのスピードに追いつかなければならない。

自律型攻撃者によって、防御に使える時間は短くなっています。継続的な検出、修復、検証、予防で、セキュリティチームが攻撃に歩調を合わせる方法をご紹介します。

feature insights context
Blog

予防は、本質的に解決済みの問題なのでしょうか?

エージェントが生成するコードの予防策はアーキテクチャ上解決されていますが、開発を遅らせることなくセキュリティを守る制御を選ぶことが、依然として課題です。

Live Stream

修正エージェントをわかりやすく解説:見つけるより直すことが重要な理由

SnykのRemediation Agentが、セキュリティインテリジェンス、破壊可能性分析、検証を活用して、脆弱性をマージ可能なプルリクエストに変える仕組みをご覧ください。