
Un assistant local d'installation de paquets faisait trop confiance aux noms de paquets fournis par l'appelant. Dans yeoman-environment, des generators manquants pouvaient être installés sans confirmation de l'utilisateur, transformant des métadonnées de projet contrôlées par l'attaquant en une voie d'installation de paquets et d'exécution de code.
Un assistant d'installation de paquets local faisait trop confiance aux noms de paquets fournis par l'appelant. Dans yeoman-environment, les générateurs manquants pouvaient être installés sans confirmation de l'utilisateur, transformant des métadonnées de projet contrôlées par un attaquant en un vecteur d'installation de paquets et d'exécution de code.
J'ai découvert ce problème en examinant generator-jhipster avec une simple question de sécurité en tête :
Des métadonnées de projet contrôlées par un attaquant peuvent-elles amener un outil de développement à récupérer et exécuter du code tiers avant que l'utilisateur ne l'ait explicitement demandé ?
Dans ce cas, la réponse était oui.
Ce qui ressemblait initialement à un problème JHipster s'est avéré avoir une cause racine plus profonde dans yeoman-environment.
Le comportement vulnérable se trouvait dans le flux d'installation locale de générateurs de Yeoman, où les paquets manquants fournis par l'appelant étaient installés automatiquement sans confirmation de l'utilisateur. Dans un consommateur aval qui transmettait des noms de paquets contrôlés par l'attaquant dans ce chemin, cela suffisait à créer une véritable chaîne d'installation de paquets et d'exécution de code.
Ce problème a donné lieu à CVE-2026-42089.
yeoman-environment : yeoman-environment sur GitHub
Paquet : yeoman-environment (npm)
CVE : CVE-2026-42089
Cela affectait yeoman-environment, la couche d'exécution derrière le flux de chargement et d'amorçage des générateurs de Yeoman. Le projet officiel le décrit comme le composant qui gère le cycle de vie et la découverte des générateurs, et au , la page du paquet npm indiquait , ce qui en fait un paquet largement déployé dans l'écosystème des outils JavaScript.

configuration de projet contrôlée par l'attaquant -> noms de paquets de générateurs fournis par l'appelant -> yeoman-environment installe silencieusement les paquets manquants -> l'outil aval charge le code du générateur installé -> installation de paquets et exécution de code pendant l'amorçage de l'interface en ligne de commande
yeoman-environment est la couche d'exécution et de chargement des générateurs derrière les outils basés sur Yeoman.
Entre autres choses, il gère :
Cela signifie qu'il se trouve directement sur une frontière de confiance.
La question pertinente n'est pas de savoir si Yeoman est « simplement un outil local ».
La question pertinente est de savoir si une entrée non fiable peut influencer le comportement d'installation de paquets et de chargement de code.
Dans ce cas, c'était possible.
Je ne recherchais pas ici des corruption de mémoire ou des bogues de simple plantage.
La cible la plus pertinente était la surface d'extension et de résolution de paquets.
Tout système qui :
mérite un examen attentif.
C'est particulièrement vrai lorsque le consommateur aval peut dériver ces noms de paquets à partir de données locales au projet.
C'est exactement le genre d'endroit où une configuration ordinaire peut silencieusement devenir une frontière de sécurité.
C'était le bon endroit pour chercher.
J'ai d'abord reproduit le comportement via generator-jhipster.
Le chemin important était :
.yo-rc.json local au projet déclare un paquet de blueprintCela signifiait que même une commande anodine telle que :
jhipster --help
pouvait atteindre l'installation de paquets avant la fin de la commande demandée.
C'est une véritable défaillance de la frontière de confiance.
Le déclencheur aval a aidé à l'exposer, mais le comportement par défaut dangereux se trouvait dans Yeoman.
Le bogue était simple.
Dans yeoman-environment, la méthode vulnérable était :
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;
}
Cette méthode installait directement les noms de paquets fournis par l'appelant via :
this.repository.install(specs)
sans demander d'abord confirmation à l'utilisateur.
C'est la vulnérabilité principale.
Parce que les noms de paquets n'ont pas besoin de provenir d'une source fiable.
Si un consommateur aval les dérive à partir de métadonnées de projet contrôlées par l'attaquant, alors la chaîne d'exploitation est simple :
Ce n'est pas seulement « l'installation de paquet a eu lieu ».
C'est une entrée non fiable qui franchit un puits d'installation de paquets sans frontière de consentement explicite.
La distinction importante est l'installation silencieuse à partir d'une entrée non fiable.
Il y a une vraie différence entre :
Cette distinction compte encore plus lorsque le paquet devient chargeable immédiatement après.
Le problème n'était pas que des générateurs tiers existent.
Le problème était que Yeoman traitait les noms de paquets fournis par l'appelant comme installables par défaut, sans confirmation de l'utilisateur.
Cela rend les hypothèses de confiance aval dangereuses matériellement pires.
C'est exactement pourquoi le correctif a ajouté une porte de confirmation.
J'ai utilisé deux niveaux de preuve car ils démontraient à la fois la cause racine et l'impact réel aval.
La première preuve utilisait generator-jhipster non modifié.
J'ai créé un projet avec un fichier .yo-rc.json racine qui référençait un paquet de blueprint non encore installé, puis j'ai exécuté :
jhipster --help
Cela a amené JHipster à transmettre le blueprint manquant dans le flux d'installation locale de générateurs de Yeoman avant la fin de l'aide.
Le résultat important était :
Cela a clairement établi la condition de déclenchement réelle.
La deuxième preuve utilisait un registre local contrôlé et un paquet conçu pour démontrer en toute sécurité les effets secondaires au moment de l'importation.
C'était important car je voulais montrer l'histoire plus forte :
C'était la chaîne de preuve la plus solide car elle faisait passer le problème de :
« tentative d'installation inattendue »
à :
« l'installation plus le chemin de chargement de code aval est effectivement accessible »
C'est le point où la défaillance de la frontière de confiance devient beaucoup plus difficile à écarter.
La première PoC prouve le comportement d'installation silencieuse.
La deuxième PoC prouve pourquoi ce comportement est important.
Cette séparation était importante.
Un rapport qui s'arrête à :
« un paquet peut être installé »
est plus faible qu'un rapport qui montre :
C'est l'histoire complète.
Lors du traitement de l'avis et de l'examen local, le comportement a été retracé jusqu'à l'introduction de installLocalGenerators() dans :
yeoman-environment 2.9.0
La plage affectée était donc :
>= 2.9.0 et < 6.0.1
La version corrigée était :
6.0.1
Le correctif était correct et minimal.
Dans la version 6.0.1, installLocalGenerators() a été modifié pour ajouter une étape de confirmation avant l'installation, sauf si l'installation forcée est explicitement demandée.
La forme corrigée ressemblait à ceci :
async installLocalGenerators(packages, forceInstall = false) {
puis :
const { aproveInstall } = await this.adapter.prompt({
message: `Les paquets suivants doivent être installés dans le dépôt local : ${specs.join(', ')}. Voulez-vous continuer ?`,
type: 'confirm',
name: 'aproveInstall',
default: false,
});
Si l'utilisateur refuse, l'installation est abandonnée.
C'est le bon correctif car il rétablit la frontière de confiance manquante :
Ce correctif a été intégré dans :
78d2af7
via :
PR #753
C'est exactement le genre de remédiation que l'on souhaite pour un problème de sécurité comme celui-ci :
Ce problème a été pris raisonnablement au sérieux car l'impact est plus que cosmétique ou surprenant.
Le comportement vulnérable peut entraîner :
Le vecteur CVSS associé au problème était :
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
Cela a du sens pour l'histoire d'exploitation aval :
Ce problème a commencé comme un rapport privé contre generator-jhipster, car c'était le chemin de déclenchement réel que j'avais initialement validé.
Lors du triage, les mainteneurs de JHipster ont souligné que le comportement d'auto-installation se trouvait dans yeoman-environment et ont référencé le correctif amont.
Cela a conduit au pivot correct :
Les mainteneurs de Yeoman ont examiné le problème, confirmé la plage affectée et l'ont suivi via un avis privé.
Le rapport a ensuite été attribué :
CVE-2026-42089
Cet avis documentait également le chemin de déclenchement aval réel via generator-jhipster.
C'était un bon exemple de pourquoi la divulgation coordonnée nécessite parfois une étape supplémentaire :
Ici, la reproduction aval était utile, mais le paquet amont était le bon endroit pour le CVE.
La leçon clé ici est simple :
l'installation de paquets est une frontière de sécurité, même dans les outils de développement locaux
Beaucoup de gens minimisent instinctivement ce genre de problèmes parce qu'ils se produisent dans des outils en ligne de commande.
C'est une erreur.
La vraie question n'est pas de savoir si l'outil est local.
La vraie question est :
Une entrée non fiable peut-elle amener l'outil à récupérer et à faire confiance à du code sans une décision explicite de l'utilisateur ?
Dans ce cas, oui.
C'est la véritable leçon à retenir.
Ce problème renforce également quelque chose d'important dans la bonne recherche de vulnérabilités :
C'était exactement la forme de ce CVE.
Cette vulnérabilité ne concernait pas une charge utile flashy.
Elle concernait le fait de poser la bonne question sur la frontière de confiance.
Un outil aval a permis à des données locales au projet d'influencer la sélection de paquets. Yeoman a installé le paquet manquant sans confirmation. Le reste de la chaîne de chargement de code a fait le reste.
C'est pourquoi cela est devenu CVE-2026-42089.
Corrigé dans yeoman-environment 6.0.1.