pnpm のパッチ機能に潜むパストラバーサル - CVE-2026-50015 とは何か

pnpm のパッチ機能に潜むパストラバーサル - CVE-2026-50015 とは何か

作成日:
読了:16
更新日:

2026年6月下旬、pnpm のパッチ適用機能(patchedDependencies と内部の@pnpm/patch-package)に関するパストラバーサル脆弱性がまとめて公開されました。中心となるのがCVE-2026-50015です。悪意ある.patchファイルのdiff --gitヘッダに../を仕込んでおくと、pnpm installを実行しただけでパッケージディレクトリの外へ任意のファイルを書き込めてしまう、という問題です。

これまで当ブログではaxios乗っ取りpnpm 10のライフサイクルスクリプト防御など、依存パッケージ側からの攻撃を追ってきました。今回はパッケージそのものではなく、パッケージマネージャ(pnpm)の機能が攻撃面になっている点が新しいところです。この記事では、何が起きたのか、どういう仕組みなのか、自分のプロジェクトは大丈夫か、どう直すかを、GHSA とNVD の一次情報をもとに整理します。

何が起きたのか - 概要

pnpm には、依存パッケージにローカルで修正を当てるpnpm patchという機能があります。pnpm patch <パッケージ名>で一時ディレクトリにパッケージを展開して編集し、pnpm patch-commitすると差分が.patchファイルとして保存され、pnpm-workspace.yamlpatchedDependenciesに登録されます。以降はpnpm installのたびに、その.patchが自動で対象パッケージに適用されます。npm のpatch-packageに相当する、地味だが実務でよく使われる機能です。

CVE-2026-50015 は、このパッチ適用パイプラインがファイルパスを検証していなかったことに起因します。GHSA-rxhj-4m44-96r4(一次情報)によると、pnpm は登録された.patchを内部の@pnpm/patch-packageで適用しますが、その際に.patch内から取り出したパスをそのまま使ってファイルの書き込み・削除を行っていました。パスに../が含まれていても弾かれないため、対象パッケージのディレクトリを抜け出して任意の場所に書き込めてしまいます。

項目内容
CVE-IDCVE-2026-50015
GHSAGHSA-rxhj-4m44-96r4
種別パストラバーサル(CWE-22)
CVSS v3.17.3(High)
CVSS ベクタCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H
影響範囲影響と修正のバージョン表を参照
修正版pnpm 10.34.0 / 11.4.0
発火条件patchedDependenciesを使うリポジトリでpnpm install

NOTE

深刻度は、CVE-2026-50015 の GHSA(GHSA-rxhj-4m44-96r4)と NVD のいずれも CVSS v3.1 で 7.3(High)と記載しています。一部のアグリゲータサイトが「Critical」と表記していますが、一次情報の評価は High です。本記事は一次情報の値を採用します。

攻撃の仕組み - パッチ機構とパストラバーサル

.patchファイル(unified diff 形式)の中身は、おおむね次のような見た目をしています。

正常な .patch の例(イメージ)
diff --git a/lib/index.js b/lib/index.js
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,3 +1,3 @@
-const x = 1
+const x = 2

pnpm はdiff --git a/(.*?) b/(.*?)のような正規表現でヘッダから対象ファイルのパスを取り出し、そのパスに対して書き込みや削除を行います。問題は、このパスに対して「パッケージのルート配下に収まっているか」の検証がなかった点です。攻撃者は次のように、ヘッダのパスに大量の../を仕込めます。

悪意ある .patch(パストラバーサルの例・イメージ)
diff --git a/../../../../../../../../home/user/.ssh/authorized_keys b/../../../../../../../../home/user/.ssh/authorized_keys

このパスはnode_modules内の対象パッケージから何階層も上に遡り、ホームディレクトリの~/.ssh/authorized_keysを指します。検証がないため、pnpm はこの位置に攻撃者の指定した内容を書き込んでしまいます。GHSA-rxhj-4m44-96r4 は、書き込み先の例として~/.ssh/authorized_keys(SSH のバックドア化)、~/.bashrc~/.zshrc(シェル設定)、CI/CD の設定ファイルなどを挙げています。書き込みだけでなく削除も可能です。

ここで重要なのは発火条件です。GHSA によれば、この攻撃はpnpm-workspace.yamlpatchedDependenciesエントリが存在するリポジトリで、誰かがpnpm installを実行した時点で自動的にトリガされます。ライフサイクルスクリプト(postinstall)のように「スクリプトが走る」必要すらなく、パッチ適用というインストールの一工程の中で発火します。動作するのはpnpm installを実行したユーザーの権限です。

攻撃の現実的な経路として GHSA が想定しているのがプルリクエストです。OSS リポジトリや社内リポジトリに、悪意ある.patchファイルとpatchedDependenciesエントリを足すPR を送る。package.jsonの変更はレビューで警戒されますが、.patchdiff --gitヘッダにある../は一見ただのパッチのメタデータに見えるため、レビューをすり抜けやすい、と GHSA は指摘しています。マージされて誰かがpnpm installした瞬間、あるいはCI が回った瞬間に発火します。

WARNING

この脆弱性は「悪意あるnpm パッケージを入れてしまう」タイプではなく、「リポジトリにコミットされた.patchファイル」がトリガになります。フォーク&PR を受け付けるOSS や、外部コントリビュータのいるプロジェクトは、レビューの観点を一段増やす必要があります。

影響範囲とバージョン

CVE-2026-50015 の影響・修正バージョンは、GHSA-rxhj-4m44-96r4 と NVD で次のとおりです。

区分バージョン範囲
影響あり(10系)10.34.0 未満
影響あり(11系)11.0.0 以上 11.4.0 未満
修正済み(10系)10.34.0 以上
修正済み(11系)11.4.0 以上

pnpm 10.34.0 のリリースノートにも、「diff --gitヘッダが対象パッケージのディレクトリ外を指す.patchファイルを拒否する」という旨が明記されており、本CVE の修正が実際にこのバージョンで取り込まれたことが確認できます。

なお、今回はパストラバーサル脆弱性が複数まとめて公開されています。混同しないよう、関連するものを一次情報の値で並べておきます。

CVE / GHSA概要CVSS修正版
CVE-2026-50015 / GHSA-rxhj-4m44-96r4.patchdiff --gitヘッダ経由で任意ファイル書き込み・削除7.3 (High)10.34.0 / 11.4.0
CVE-2026-50016 / GHSA-hwx4-2j3j-g496推移的依存のエイリアスにパストラバーサルを含め、リンク時に外部へ8.8 (High)10.34.0 / 11.4.0
GHSA-72r4-9c5j-mj57pnpm patch-removeがpatches ディレクトリ外のファイルを削除しうる7.1 (High)(要確認)10.34.4 / 11.7.0

NOTE

CVE-2026-50016 は今回の話と紛らわしいですが、こちらは.patchファイルではなく依存パッケージのエイリアス名にパストラバーサルを仕込む別経路です(CWE-23、CVSS 8.8)。同じ pnpm 10.34.0 / 11.4.0 で修正されています。pnpm patch-removeの問題(GHSA-72r4-9c5j-mj57)は本記事執筆時点でCVE 番号が確認できず、CVSS と修正版(10.34.4 / 11.7.0)はGitHub Advisory の値です。番号と数値は要確認として扱ってください。

自分が影響を受けるか確認する

まず使っている pnpm のバージョンを確認します。

pnpm のバージョン確認
pnpm --version
# Corepack 経由なら
corepack pnpm --version

10.34.0未満、または11.0.0以上11.4.0未満なら、本体は脆弱性のあるバージョンです。

次に、そもそもパッチ機能を使っているかを確認します。patchedDependenciesを使っていなければ、CVE-2026-50015 の発火条件は成立しません。

patchedDependencies と .patch を使っているか確認
# pnpm 10 以降は pnpm-workspace.yaml に書かれることが多い
grep -n "patchedDependencies" pnpm-workspace.yaml package.json 2>/dev/null
 
# patches ディレクトリの .patch を一覧
ls -la patches/ 2>/dev/null

patchedDependenciesがあり、patches/配下に.patchがある場合は、その.patchの中身をレビューしておくと安心です。diff --git--- / +++ヘッダのパスに..が混じっていないかを確認します。

.patch の中に ../ を含むパスが無いか確認
grep -rnE '^(diff --git|---|\+\+\+) .*\.\./' patches/ 2>/dev/null

出力が空であれば、少なくとも手元の.patchに明らかなパストラバーサルは含まれていません。出力があれば、そのパッチが意図したものか(自分でcommit したものか、外部から入ってきたものか)を必ず確認してください。

NOTE

パッチ機能を使っていなくても、pnpm 本体が脆弱バージョンなら更新を推奨します。同時に公開された CVE-2026-50016 のように、patchedDependenciesを使っていなくても影響しうる別経路の脆弱性も同じ修正版に含まれているためです。

対処 - アップデートと運用の注意

pnpm 本体を修正版へ更新する

最優先は pnpm 本体を修正版に上げることです。10系なら10.34.0以上、11系なら11.4.0以上にします。

pnpm 本体の更新
# Corepack を使っている場合(推奨)
corepack use pnpm@10.34.0
# あるいは 11 系
corepack use pnpm@11.4.0
 
# npm でグローバルに入れている場合
npm install -g pnpm@latest
 
# package.json の packageManager フィールドを固定しているなら更新する
# "packageManager": "pnpm@10.34.0"

packageManagerフィールドや CI のセットアップで pnpm のバージョンを固定している場合は、そちらも忘れずに更新します。固定値が古いままだと、ローカルだけ更新してもCI では脆弱版が使われ続けます。バージョン指定の考え方はセマンティックバージョニングの記事も参考にしてください。

.patch とPR レビューの運用

修正版に上げれば pnpm がパストラバーサルを含む.patchを拒否するようになりますが、運用面でも次を意識しておくと多層防御になります。

  • 外部からのPR でpatches/配下の.patchが追加・変更されたら、ヘッダのパスを必ず確認する
  • patchedDependenciesの追加もレビュー対象に含める(package.jsonの依存追加と同じ重みで見る)
  • CI ではロックファイルを固定し、想定外の変更が混入しないようにする
CI ではロックファイルを固定して install
pnpm install --frozen-lockfile

--frozen-lockfileはロックファイルと一致しない状態を拒否しますが、これは依存の改変を防ぐもので、リポジトリにコミットされた.patchそのものは別途レビューが必要です。.patchはソースコードと同じく「人間がレビューする変更」だと捉え直すのが安全です。

CI で被害を受けた可能性がある場合

もし脆弱バージョンで、信頼できない.patchを含むブランチをCI でビルドしていた場合、CI ランナー上で任意ファイルが書き換えられた可能性があります。その場合はaxios 事件の記事でも触れたとおり、トークンや認証情報のローテーション、ランナーの再構築を検討してください。.envの扱いについては環境変数とdotenv の記事も参考になります。

教訓 - パッケージマネージャの機能も攻撃面になる

今回の一連の脆弱性が示すのは、攻撃面はパッケージそのものだけではないということです。patchedDependencies、依存のエイリアス、patch-remove、tarball 展開——pnpm が「便利のために」提供している機能のあちこちに、パス検証の漏れがありました。いずれも CWE-22 / CWE-23(パストラバーサル)という古典的な分類で、入力(パッチのパス、エイリアス名、アーカイブ内のパス)を信頼してファイルシステム操作に渡してしまった、という共通構造です。

実務的な教訓は次のとおりです。

  • パッケージマネージャ自体を最新に保つ。依存パッケージだけでなくツールチェーンもサプライチェーンの一部
  • .patchファイルはコードと同じレビュー対象。diff --gitヘッダのパスまで見る
  • 外部PR を受け付けるリポジトリでは、patches/patchedDependenciesの変更に追加の警戒を払う
  • 「インストールするだけ」「パッチを当てるだけ」の工程でも、ファイル書き込みが起きうる以上は攻撃面になる

OSS のサプライチェーンを取り巻く事故は2026年に入って続発しています。依存を疑うのと同じ目で、自分の使うツールの更新履歴とセキュリティアドバイザリも追う習慣をつけたいところです。

まとめ

  • CVE-2026-50015 は、pnpm のパッチ適用パイプラインがパスを検証しておらず、悪意ある.patchdiff --gitヘッダに../を仕込むとpnpm install時にパッケージ外へ任意ファイルを書き込み・削除できる脆弱性です(CWE-22、CVSS 7.3 High)
  • 発火条件は「patchedDependenciesを使うリポジトリでpnpm install」。postinstall すら不要で、PR でレビューをすり抜けやすい点が厄介です
  • 影響は10.34.0未満および11.0.0以上11.4.0未満。修正版は pnpm 10.34.0 / 11.4.0です
  • まずpnpm --versionで確認し、修正版へ更新する。packageManagerフィールドやCI の固定値も忘れずに上げる
  • 同時に CVE-2026-50016(エイリアス経由)など複数のパストラバーサルが公開されているため、パッチ機能を使っていなくても更新を推奨します

便利なpnpm patchも、入力検証が欠けるとファイル書き込みの抜け穴になります。手元の pnpm を確認し、まずは修正版へ上げることから始めてください。

参考リンク

pnpm 10 のサプライチェーン防御 - postinstall を止めて依存を「承認」する

pnpm 10 のサプライチェーン防御 - postinstall を止めて依存を「承認」する

8

pnpm 10 はインストール時の依存のビルドスクリプト(postinstall 等)をデフォルトでブロックし、明示的に承認した依存だけ実行します。onlyBuiltDependencies や approve-builds、minimumReleaseAge を使った現実的なサプライチェーン防御設定を、手元の pnpm 10.12.4 の挙動を確認しながら整理します。

【サプライチェーン攻撃】2026年3月31日、npmパッケージ「axios」乗っ取り事件の全容と対策

【サプライチェーン攻撃】2026年3月31日、npmパッケージ「axios」乗っ取り事件の全容と対策

17

週1億ダウンロードを誇るnpmパッケージ「axios」が、北朝鮮系脅威アクターによって乗っ取られた事件を解説。攻撃の手口、影響を受けたバージョン、自分のプロジェクトの確認方法、今後の防御策をまとめます。

【緊急】npm パッケージ「axios」が乗っ取られた事件まとめ — 2026年3月31日のサプライチェーン攻撃

【緊急】npm パッケージ「axios」が乗っ取られた事件まとめ — 2026年3月31日のサプライチェーン攻撃

13

2026年3月31日、週間1億ダウンロードを超える人気パッケージ「axios」のnpmアカウントが乗っ取られ、悪意あるバージョン1.14.1と0.30.4が公開されました。攻撃の全容、影響を受けるバージョンの確認方法、今後の防御策をまとめます。