とことんDevOps | 日本仮想化技術のDevOps技術情報メディア

DevOpsに関連する技術情報を幅広く提供していきます。

日本仮想化技術がお届けする「とことんDevOps」では、DevOpsに関する技術情報や、日々のDevOps業務の中での検証結果、TipsなどDevOpsのお役立ち情報をお届けします。
主なテーマ: DevOps、CI/CD、コンテナ開発、IaCなど
読者登録と各種SNSのフォローもよろしくお願いいたします。

npmパッケージを狙った大規模攻撃がまた発生したようなので代替を検討した話

npmパッケージが悪意のあるコードに置き換えられる「サプライチェーン攻撃」が頻繁にITニュースの話題になっています。 つい最近の記事はこんな感じです。

atmarkit.itmedia.co.jp

npmってなんぞやって思った人もいると思いますが、Node.jsというクロスプラットフォームに対応したフリーでオープンソースのJavaScript実行環境でJavaScriptのパッケージ(再利用可能なコードのまとまり)を管理するツールです。 Node.jsは色々なところで使われていて、気がついたら自分の環境にも入っていたなんてことはあるかもしれません。

nodejs.org

試しにターミナルを開いてnode -vとか実行してみてください。開発に使っている端末であれば、Node.jsはインストール済みなんてことは多いと思います。

% node -v
v25.2.1

さて、たびたびnpmが狙われることが発生しています*1

ちょっと調べたら見つかった情報

npmの代替としてYarnがあるのは知っていましたが、これを機会に比較しようと思い調べていました。 はじめに見つけたのがこれです。

qiita.com

上記はよくまとまっているので見てほしいとして、ざっくりまとめると次のようになるようです。

ツール 利点 欠点
npm デフォルトで使える node_modules構造が柔軟すぎて、意図しない依存関係へのアクセスを許容しやすい
pnpm 高速、ディスク効率が良い シンボリックリンクの扱いに慣れが必要。特にWindowsとの相性に課題
Yarn npmより高速、安定動作 PnPモードはnode_modulesの脆弱性を排除するが、一部互換性に課題

npmからyarnに切り替え

npmでは次のようにセットアップしていました。yarnに置き換える場合は、どのように実施すればいいでしょうか? 基本的にはコマンドを置き換えるだけで済みます。

まず比較のために、textlintをnpmコマンドを使ってこういう感じでインストールした構成を想定します。

% cd ~/projects
% npm init -y  # なければ package.json を作成
% npm install --save-dev textlint
% npm install --save-dev textlint-rule-preset-ja-technical-writing

去年、こういうブログ記事を書いたのを思い出しました。

devops-blog.virtualtech.jp

これを、Yarnに置き換えてみたいと思います。

まず、何はともあれYarnをインストールします。Yarnのインストール方法はOSによって色々選択肢があるのですが、この方法を使う方が最も簡単でしょう。 ところで、yarnをインストールするのにnpmコマンドを使うのはなんとも複雑です。

% npm install -g yarn

次にyarnを使って、必要なパッケージをインストールするだけです。

% cd ~/projects
% rm -r node_modules       //必要ないので消す
% rm package-lock.json  //必要ないので消す  

% yarn init -y
% yarn add --dev textlint
% yarn add --dev textlint-rule-preset-ja-technical-writing

あれ?またnode_modulesディレクトリーが作られていますね?

% ls
blog2.txt   package-lock.json       textlint-fix.sh     yarn.lock
node_modules    package.json    textlint.sh

調べたところ、次のような理由からでした。

  • 次のことから、デフォルトでは有効化されていない
    • Yarn v1とv2+で体験が大きく異なり、v2+のPnPは学習コストがかかる
    • 一部ツールとの互換性に問題が出やすい
  • もし試したい場合はプロジェクトルートでコマンドを実行して、プロジェクトをアップグレードする必要がある

早速やってみることにしました。

# プロジェクト内で Yarn のバージョンを最新(Berry)に設定する
% yarn set version stable
# PnP モードを有効にする(デフォルトで有効化されるはずですが念のため)
% yarn config set nodeLinker pnp

% rm -r node_modules  //いったん消す
% rm yarn.lock     //いったん消す

% yarn init -y
% yarn add --dev textlint
% yarn add --dev textlint-rule-preset-ja-technical-writing

実行後確認すると、node_modulesディレクトリーの代わりに.pnpから始まるディレクトリーと.yarnディレクトリーが作られていました。だからなにが違うかというと、ディレクトリーサイズです。 npmを使ってインストールしたときは120MBくらいと出ていましたが、この方法でインストールした場合のディレクトリーサイズは4.4MBでした。この差は大きいですね*2

この理由の詳細は後述しますが、yarnはパッケージのキャッシュはグローバルにインストールするのがデフォルトだからです。そのため、プロジェクトディスレクトリーは膨れません。つまり、共通するパッケージが重複はしないってことですね。

% ls -a 
.           .gitattributes      .yarn           textlint-fix.sh
..          .gitignore      .yarnrc.yml     textlint.sh
.DS_Store       .pnp.cjs        blog2.txt       yarn.lock
.editorconfig       .pnp.loader.mjs     package.json
.git            .textlintrc.json    README.md

移行後の使い方

先ほどインストールしたtextlintですが、yarnでインストールした場合はどう使えばいいでしょうか。 簡単です。ここでもコマンドを置き換えるだけです。

npx textlint test.md
↓
yarn textlint test.md

実行例です。textlintはPnPモードでも動作するようです。

% yarn textlint blog2.md

/Users/ytooyama/working/docwrite/blog2.md
  1:16  error  文末が"。"で終わっていません。
理由: 句点は文の境界を明確にし、読み手の理解を助けます
修正: 適切な文末表現で文を完結させ、句点を追加してください
例: 「〜です。」「〜ます。」「〜でした。」など  ja-technical-writing/ja-no-mixed-period

✖ 1 problem (1 error, 0 warnings, 0 infos)

キャッシュの話

ちなみにキャッシュは$USER/.yarn/berry/cacheに展開されるようです。 全部zip圧縮されていました。ここがストレージの消費サイズの差として出てくるのかな?

% yarn config get cacheFolder
/Users/ytooyama/.yarn/berry/cache

% tree /Users/ytooyama/.yarn/berry/cache
/Users/ytooyama/.yarn/berry/cache
├── @azu-format-text-npm-1.0.2-e8edacd073-10c0.zip
├── @azu-style-format-npm-1.0.1-c450b7c945-10c0.zip
├── @babel-code-frame-npm-7.27.1-4dbcabb137-10c0.zip
├── @babel-helper-string-parser-npm-7.27.1-d1471e0598-10c0.zip
├── @babel-helper-validator-identifier-npm-7.28.5-1953d49d2b-10c0.zip
├── @babel-parser-npm-7.28.5-f2345a6b62-10c0.zip
├── @babel-types-npm-7.28.5-582d7cca8a-10c0.zip
...

今後は都度、npmからyarnに置き換えていきたいと思います。 PnPモードを使うのは都度検討ですね。動くなら使っていきたいです。

ここら辺にnpmとyarnの比較が載っていました。 ざっくりいうと依存関係の解決が厳格になったり、すでにインストールされたパッケージの改ざんを防ぐ措置が有効になるようです。良いですね。

*1:npmだけではないけど

*2:チリも積もればというやつです