[速報] pnpmに深刻度HighのPath Traversal脆弱性 (CVE-2026-82392) - フロントエンド開発者のための対策
はじめに
pnpmは、その高速性とディスク効率の良さから、多くのフロントエンド開発プロジェクトで採用されている人気のパッケージマネージャーです。しかし、この度、pnpmの仮想ストアリンカーに深刻度HighのPath Traversal脆弱性(GHSA-c59q-g84q-2gj5 / CVE-2026-82392)が発見されました。この脆弱性は、細工されたpnpm-lock.yamlファイルを通じて、開発者のシステム上で任意の場所にファイルを書き込むことを可能にし、最悪の場合、リモートからのコード実行(RCE)につながる可能性があります。本記事では、この脆弱性の詳細、影響、そして日本のフロントエンドエンジニアが取るべき対策について解説します。
Path Traversal (ディレクトリトラバーサル) とは?
Path Traversal、またはディレクトリトラバーサルとは、ファイルパス内の「../」(親ディレクトリへの移動)などの特殊なシーケンスを利用して、本来アクセスしてはならないファイルやディレクトリに不正にアクセスする攻撃手法です。今回の脆弱性も、この原理を悪用して、pnpmの管理する仮想ストアの外部にあるファイルシステムにパッケージ内容を書き込んでしまいます。
脆弱性の詳細:なぜ発生したのか
この脆弱性の根本原因は、pnpmがロックファイル(pnpm-lock.yaml)内のパッケージ名を処理する方法にあります。具体的には、ロックファイルの`packages`キーからパッケージ名(`pkgName`)を抽出する際に、`depPath`(依存関係のパス)に含まれる「../../../tmp/pwned@1.0.0」のようなトラバーサルシーケンスが適切に検証されていなかったことに起因します。
pnpmは、`dp.parse(depPath).name`という処理でパッケージ名を抽出しますが、この処理は`../../../tmp/pwned`のような不正なパスをそのままパッケージ名として返してしまいます。この不正なパッケージ名が、仮想ストア内のパッケージインストールディレクトリを構築する`path.join(modules, pkgName)`に渡されることで、意図せず仮想ストアの外部(例: `/tmp/pwned`)にパッケージの内容が書き込まれてしまうのです。
以前にも類似の脆弱性(GHSA-fr4h-3cph-29xv)が修正されましたが、今回の脆弱性はこの修正が不完全であったために発生しました。具体的には、仮想ストアリンカーの重要なコードパス(`lockfileToDepGraph.ts:233`)に、安全なパス結合のためのヘルパー関数`safeJoinModulesDir`が適用されていなかったことが原因です。また、以下の既存の防御策も、この特定の攻撃経路を防ぐことはできませんでした。
<ul><li>`depPathToFilename()`: 仮想ストア内のパス(`dirInVirtualStore`)は変換しますが、`dp.parse()`から抽出される`pkgName`には適用されません。</li><li>`verifyLockfileResolutions()`: 依存関係のエイリアスは検証しますが、`depPath`キー自体は検証しません。</li><li>ロックファイルパーサー: `packages`キーにスキーマ検証がありません。</li><li>`importPackage()`: パッケージ内容の書き込み先パスに対する検証がありません。</li><li>Integrity検証: パッケージの内容は検証しますが、書き込み先のパスは検証しません。</li></ul>
デフォルト設定では、Path Traversalによる影響は任意ファイル書き込みに留まります。しかし、`dangerouslyAllowAllBuilds: true`が設定されている場合や、攻撃者が指定したパッケージ名が`allowBuilds`リストに含まれている場合、同じ不正なパスがパッケージのビルドフェーズで使用されます。これにより、攻撃者が用意した`postinstall`スクリプトが、被害者のシェル権限で実行され、リモートからのコード実行(RCE)につながる可能性があります。
`nodeLinker: pnp`が設定されている場合も、同様に未検証の`dp.parse().name`が`.pnp.cjs`の解決マップで使用され、仮想ストア外を指すパッケージロケーションが生成される可能性があります。ただし、これはデフォルトのリンカーではないため、影響は限定的とされています。
想定される影響
この脆弱性の最も直接的な影響は、**任意ファイル書き込み**です。攻撃者が細工された`pnpm-lock.yaml`をリポジトリにコミットしたり、悪意のあるパッケージを供給したりすることで、`pnpm install`を実行したユーザーのシステム上で、攻撃者が指定した任意の場所にファイルを書き込むことが可能になります。具体的には、以下のような被害が想定されます。
<ul><li>`.git/hooks/pre-commit`: Git操作のフックに悪意のあるスクリプトを注入し、次回のコミット時にコードを実行させる。</li><li>`~/.local/bin/`や`/usr/local/bin/`: 既存のシステムコマンドを上書きまたは追加し、バイナリハイジャックを行う。</li><li>プロジェクトのソースファイル: アプリケーションのコードベースに悪意のあるコードを注入し、サプライチェーン攻撃に発展させる。</li></ul>
さらに、前述の通り、特定の設定下ではRCEに発展し、開発者のマシンが完全に侵害されるリスクもあります。
再現方法
以下のような内容で`pnpm-lock.yaml`を作成します。`sha512-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`の部分は、任意の有効なパッケージ(例: `is-odd`など)の`sha512`ハッシュ値に置き換えてください。
```yaml lockfileVersion: '9.0' packages: ../../../../../../../tmp/pwned@1.0.0: resolution: {integrity: sha512-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} engines: {node: '>=14'} snapshots: ../../../../../../../tmp/pwned@1.0.0: {} importers: .: dependencies: legitimate-name: specifier: ^1.0.0 version: ../../../../../../../tmp/pwned@1.0.0 ```
この`pnpm-lock.yaml`が存在するディレクトリで`pnpm install`を実行すると、通常であれば仮想ストア内にインストールされるパッケージの内容が、`/tmp/pwned/`ディレクトリ(macOS/Linuxの場合)に書き込まれることを確認できます。
対策:フロントエンドエンジニアとして今すぐできること
最も重要かつ直接的な対策は、pnpmを速やかに最新バージョンにアップデートすることです。脆弱性が修正されたバージョンがリリースされているはずですので、プロジェクトで使用しているpnpmのバージョンを確認し、すぐにアップデートしてください。また、以下の点にも注意を払いましょう。
脆弱性が修正された最新バージョンのpnpmを使用するようにしてください。グローバルインストールされているpnpmだけでなく、プロジェクトローカルで使用しているpnpmのバージョンも確認が必要です。
コードレビュー時に`pnpm-lock.yaml`の変更にも注意を払いましょう。特に、`packages`セクションに「`../`」などのPath Traversalシーケンスを含む、見慣れないキーが存在しないか確認してください。悪意のある変更は、通常とは異なる`depPath`のパターンを含んでいる可能性が高いです。
インターネット上の見慣れないリポジトリや、信頼できないソースから取得したプロジェクトの`pnpm-lock.yaml`を安易に利用しないようにしましょう。可能であれば、自身で`pnpm install`を実行してロックファイルを再生成するか、内容を徹底的に精査してください。
`dangerouslyAllowAllBuilds: true`の設定は、深刻なリスクを伴います。この設定は、必要な場合のみ、そのリスクを十分に理解した上で慎重に利用し、可能な限り最小限の`allowBuilds`リストを使用するように検討してください。
まとめ
pnpmのPath Traversal脆弱性(CVE-2026-82392)は、フロントエンド開発環境に深刻な影響を及ぼす可能性のある重要なセキュリティ問題です。この記事で解説した内容を参考に、ご自身の開発環境とプロジェクトにおけるpnpmのバージョンを速やかに確認し、適切な対策を講じてください。最新のセキュリティ情報を常にチェックし、安全な開発を心がけることが、私たちフロントエンドエンジニアにとって非常に重要です。