
ローカルパッケージインストールヘルパーが、呼び出し元から提供されたパッケージ名を信頼しすぎていました。yeoman-environment では、不足しているジェネレーターがユーザーの確認なしにインストールされ、攻撃者が制御するプロジェクトメタデータがパッケージインストールおよびコード実行の経路となる可能性がありました。
この問題は、generator-jhipster をレビューしている際に、単純なセキュリティ上の疑問を念頭に置いて発見しました。
攻撃者が制御するプロジェクトメタデータによって、開発者ツールがユーザーが明示的に要求する前にサードパーティのコードを取得して実行させられるか?
このケースでは、答えは「はい」でした。
当初はJHipsterの問題に見えましたが、より深い上流の根本原因が yeoman-environment にあることが判明しました。
脆弱な動作は、Yeoman のローカルジェネレーターインストールフローにありました。そこでは、呼び出し元から提供された不足しているパッケージが、ユーザーの確認なしに自動的にインストールされていました。攻撃者が制御するパッケージ名をそのパスに渡すダウンストリームコンシューマーでは、それが実際のパッケージインストールおよびコード実行チェーンを生み出すのに十分でした。
この問題は CVE-2026-42089 となりました。
yeoman-environment: yeoman-environment on GitHub
パッケージ: yeoman-environment (npm)
CVE: CVE-2026-42089
この影響を受けたのは yeoman-environment です。これは Yeoman のジェネレーター読み込みおよびブートストラップフローの背後にあるランタイムレイヤーです。公式プロジェクトは、これをジェネレーターのライフサイクルとディスカバリを処理するコンポーネントと説明しており、2026年6月26日時点で、npmパッケージページには 1,466,426 週間ダウンロード と表示されており、これはJavaScriptツールエコシステムで広くデプロイされているパッケージです。
攻撃者が制御するプロジェクト設定 -> 呼び出し元から提供されたジェネレーターパッケージ名 -> yeoman-environment が不足しているパッケージをサイレントインストール -> ダウンストリームツールがインストールされたジェネレーターコードを読み込む -> CLI ブートストラップ中にパッケージインストールとコード実行が発生
yeoman-environment は、Yeomanベースのツールの背後にあるランタイムおよびジェネレーター読み込みレイヤーです。
とりわけ、以下の処理を行います。
つまり、これは信頼の境界に直接位置しています。
関連する疑問は、Yeoman が「単なるローカルツール」であるかどうかではありません。
関連する疑問は、信頼できない入力がパッケージインストールとコード読み込みの動作に影響を与えることができるかどうかです。
このケースでは、それが可能でした。
私はここでメモリ破壊やクラッシュのみのバグを探していたわけではありません。
より重要なターゲットは、拡張とパッケージ解決の表面領域でした。
以下のようなシステムはすべて、
これらは厳密な精査に値します。
これは、ダウンストリームコンシューマーがそれらのパッケージ名をプロジェクトローカルデータから導出できる場合に特に当てはまります。
まさにこれこそ、通常の設定が静かにセキュリティ境界になる可能性がある場所です。
それが正しい調査対象でした。
最初に generator-jhipster を通じて動作を再現しました。
重要なパスは以下の通りです。
.yo-rc.json がブループリントパッケージを宣言するつまり、次のような無害なコマンドでも、
jhipster --help
要求されたコマンドが完了する前にパッケージインストールに到達する可能性がありました。
これは実際の信頼境界の失敗です。
ダウンストリームのトリガーがそれを露呈するのに役立ちましたが、安全でないデフォルトの動作は Yeoman にありました。
バグは単純でした。
yeoman-environment において、脆弱なメソッドは次の通りです。
async installLocalGenerators(packages) {
const entries = Object.entries(packages);
const specs = entries.map(([packageName, version]) => `${packageName}${version ? `@${version}` : ''}`);
const installResult = await this.repository.install(specs);
const failToInstall = installResult.find(result => !result.path);
if (failToInstall) {
throw new Error(`Fail to install ${failToInstall.pkgid}`);
}
await this.lookup({ packagePaths: installResult.map(result => result.path) });
return true;
}
このメソッドは、呼び出し元から提供されたパッケージ名を直接以下の方法でインストールしていました。
this.repository.install(specs)
ユーザーに事前に確認を求めないままです。
これが脆弱性の核心です。
パッケージ名が信頼できるソースから来る必要がないからです。
ダウンストリームコンシューマーがそれらを攻撃者が制御するプロジェクトメタデータから導出する場合、悪用チェーンは簡単です。
これは単に「パッケージインストールが発生した」という話ではありません。
これは、明示的な同意の境界なしに、信頼できない入力がパッケージインストールシンクに渡されるということです。
重要な区別は、信頼できない入力からのサイレントインストールです。
以下の間には実際に違いがあります。
この違いは、パッケージがその後すぐに読み込み可能になる場合にさらに重要になります。
問題はサードパーティのジェネレーターが存在することではありません。
問題は、Yeoman が呼び出し元から提供されたパッケージ名を、ユーザーの確認なしにデフォルトでインストール可能として扱ったことです。
これにより、安全でないダウンストリームの信頼前提が事実上悪化します。
修正が確認ゲートを追加したのは、まさにそのためです。
私は2層の証明を使用しました。なぜなら、それらが根本原因と実際のダウンストリーム影響の両方を示したからです。
最初の証明では、未修正の generator-jhipster を使用しました。
まだインストールされていないブループリントパッケージを参照するルート .yo-rc.json を持つプロジェクトを作成し、次を実行しました。
jhipster --help
これにより、JHipster は不足しているブループリントを、ヘルプが完了する前に Yeoman のローカルジェネレーターインストールフローに渡しました。
重要な結果は以下の通りです。
これにより、実際のトリガー条件が明確に確立されました。
2番目の証明では、制御されたローカルレジストリと、インポート時の副作用を安全に示すように設計されたパッケージを使用しました。
これが重要だったのは、より強力なストーリーを示したかったからです。
これは最も強力な証拠チェーンでした。なぜなら、問題を以下の範囲から
「予期しないインストール試行」
以下の範囲へと移行させたからです。
「インストールとダウンストリームコード読み込みパスが実際に到達可能である」
これこそが、信頼境界の失敗がはるかに否定しにくくなるポイントです。
最初のPoCはサイレントインストール動作を証明します。
2番目のPoCは、その動作がなぜ重要かを証明します。
この分割は重要でした。
以下のように留まる報告は、
「パッケージがインストールされる可能性がある」
以下のように示す報告よりも弱いです。
それが完全なストーリーです。
アドバイザリの処理とローカルレビュー中に、この動作は installLocalGenerators() の導入まで遡って調査されました。導入は以下のバージョンです。
yeoman-environment 2.9.0
したがって、影響範囲は次の通りです。
>= 2.9.0 かつ < 6.0.1
修正バージョンは次の通りです。
6.0.1
修正は正確で最小限でした。
6.0.1 では、installLocalGenerators() が変更され、強制インストールが明示的に要求されない限り、インストール前に確認ステップが追加されました。
修正後の形状は次のようになりました。
async installLocalGenerators(packages, forceInstall = false) {
そして次のようになります。
const { aproveInstall } = await this.adapter.prompt({
message: `次のパッケージをローカルリポジトリにインストールする必要があります: ${specs.join(', ')}。続行しますか?`,
type: 'confirm',
name: 'aproveInstall',
default: false,
});
ユーザーが拒否した場合、インストールは中断されます。
これは正しい修正です。なぜなら、不足していた信頼境界を復元するからです。
この修正は次のコミットで導入されました。
78d2af7
次のPRを通じて。
PR #753
これは、このようなセキュリティ問題に求めるまさにその種類の修復です。
この問題は、外観上の問題や驚くべき動作以上の影響があるため、合理的に真剣に受け止められました。
脆弱な動作は以下につながる可能性があります。
この問題に関連付けられたCVSSベクトルは次の通りです。
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
これはダウンストリームの悪用ストーリーとして理にかなっています。
この問題は、最初は generator-jhipster に対する非公開報告として始まりました。なぜなら、それが私が最初に検証した実際のトリガーパスだったからです。
トリアージ中に、JHipster メンテナーから、自動インストール動作自体が yeoman-environment にあると指摘され、上流の修正が参照されました。
それにより、正しい方向転換が行われました。
Yeoman メンテナーは問題をレビューし、影響範囲を確認し、非公開アドバイザリを通じて追跡しました。
その後、報告には次のCVEが割り当てられました。
CVE-2026-42089
そのアドバイザリは、generator-jhipster を通じた実際のダウンストリームトリガーパスも文書化していました。
これは、調整された開示が時々もう1ステップ必要とする理由の良い例です。
ここでは、ダウンストリームでの再現は有用でしたが、CVE の適切な場所は上流パッケージでした。
ここでの重要な教訓は単純です。
パッケージインストールは、ローカル開発ツールであってもセキュリティ境界である
多くの人は、これがCLIツールで発生するという理由で、このような問題を本能的に軽視します。
それは間違いです。
本当の疑問は、ツールがローカルかどうかではありません。
本当の疑問は次の通りです。
信頼できない入力が、明示的なユーザーの決定なしにツールにコードを取得させ、信頼させることができるか?
このケースでは、可能でした。
それが本当の教訓です。
この問題は、優れた脆弱性研究に関する重要な点も強化します。
それがまさにこのCVEの形状でした。
この脆弱性は派手なペイロードに関するものではありませんでした。
それは、正しい信頼境界の質問をすることに関するものでした。
ダウンストリームツールがプロジェクトローカルデータにパッケージ選択に影響を与えさせました。 Yeoman は不足しているパッケージを確認なしでインストールしました。 残りのコード読み込みチェーンが残りを行いました。
それが、これが CVE-2026-42089 になった理由です。
yeoman-environment 6.0.1 で修正されました。