
Un asistente de instalación local de paquetes confiaba demasiado en los nombres de paquetes proporcionados por el llamante. En yeoman-environment, los generadores faltantes podían instalarse sin confirmación del usuario, convirtiendo los metadatos del proyecto controlados por el atacante en una ruta de instalación de paquetes y ejecución de código.
Un ayudante de instalación de paquetes local confiaba demasiado en los nombres de paquetes proporcionados por el llamante. En yeoman-environment, los generadores faltantes podían instalarse sin confirmación del usuario, convirtiendo metadatos de proyecto controlados por un atacante en una ruta de instalación de paquetes y ejecución de código.
Encontré este problema al revisar generator-jhipster con una simple pregunta de seguridad en mente:
¿Pueden los metadatos de proyecto controlados por un atacante hacer que una herramienta de desarrollo obtenga y ejecute código de terceros antes de que el usuario lo haya solicitado explícitamente?
En este caso, la respuesta fue sí.
Lo que inicialmente parecía un problema de JHipster resultó tener una causa raíz más profunda en yeoman-environment.
El comportamiento vulnerable estaba en el flujo de instalación de generadores locales de Yeoman, donde los paquetes faltantes suministrados por el llamante se instalaban automáticamente sin confirmación del usuario. En un consumidor downstream que pasaba nombres de paquetes controlados por un atacante a esa ruta, eso era suficiente para crear una cadena real de instalación de paquetes y ejecución de código.
Ese problema se convirtió en CVE-2026-42089.
yeoman-environment: yeoman-environment en GitHub
Paquete: yeoman-environment (npm)
CVE: CVE-2026-42089
Esto afectó a yeoman-environment, la capa de ejecución detrás del flujo de carga e inicio de generadores de Yeoman. El proyecto oficial lo describe como el componente que maneja el ciclo de vida y el descubrimiento de generadores, y al 26 de junio de 2026, la página del paquete npm mostraba 1,466,426 descargas semanales, lo que lo convierte en un paquete ampliamente desplegado en el ecosistema de herramientas JavaScript.
configuración de proyecto controlada por atacante -> nombres de paquetes generador proporcionados por el llamante -> yeoman-environment instala silenciosamente paquetes faltantes -> herramienta downstream carga el código del generador instalado -> instalación de paquetes y ejecución de código durante el arranque del CLI
yeoman-environment es la capa de ejecución y carga de generadores detrás de las herramientas basadas en Yeoman.
Entre otras cosas, maneja:
Eso significa que se encuentra directamente en un límite de confianza.
La pregunta relevante no es si Yeoman es "solo una herramienta local".
La pregunta relevante es si la entrada no confiable puede influir en el comportamiento de instalación de paquetes y carga de código.
En este caso, podía.
No estaba buscando corrupción de memoria o errores que solo causaran caídas aquí.
El objetivo más fuerte era la superficie de extensión y resolución de paquetes.
Cualquier sistema que:
merece un escrutinio cercano.
Esto es especialmente cierto cuando el consumidor downstream puede derivar esos nombres de paquetes de datos locales del proyecto.
Este es exactamente el tipo de lugar donde la configuración ordinaria puede convertirse silenciosamente en un límite de seguridad.
Ese era el lugar adecuado para mirar.
Primero reproduje el comportamiento a través de generator-jhipster.
La ruta importante era:
.yo-rc.json local del proyecto declara un paquete blueprintEso significaba que incluso un comando benigno como:
jhipster --help
podía llegar a la instalación de paquetes antes de que el comando solicitado se completara.
Eso es una falla real del límite de confianza.
El desencadenante downstream ayudó a exponerlo, pero el comportamiento predeterminado inseguro estaba en Yeoman.
El error era simple.
En yeoman-environment, el método vulnerable era:
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;
}
Ese método instalaba nombres de paquetes proporcionados por el llamante directamente a través de:
this.repository.install(specs)
sin preguntar primero al usuario.
Esa es la vulnerabilidad central.
Porque los nombres de paquetes no tienen que provenir de una fuente confiable.
Si un consumidor downstream los deriva de metadatos de proyecto controlados por un atacante, entonces la cadena de explotación es directa:
Eso no es solo "la instalación del paquete ocurrió".
Eso es entrada no confiable cruzando hacia un sumidero de instalación de paquetes sin un límite de consentimiento explícito.
La distinción importante es la instalación silenciosa desde entrada no confiable.
Hay una diferencia real entre:
Esa distinción importa aún más cuando el paquete se vuelve cargable inmediatamente después.
El problema no era que existieran generadores de terceros.
El problema era que Yeoman trataba los nombres de paquetes proporcionados por el llamante como instalables por defecto sin confirmación del usuario.
Eso empeora materialmente las suposiciones de confianza downstream inseguras.
Es exactamente por eso que la corrección añadió una puerta de confirmación.
Usé dos capas de prueba porque demostraban tanto la causa raíz como el impacto real downstream.
La primera prueba usó generator-jhipster sin modificar.
Creé un proyecto con un .yo-rc.json raíz que referenciaba un paquete blueprint que no estaba instalado, luego ejecuté:
jhipster --help
Eso hizo que JHipster pasara el blueprint faltante al flujo de instalación de generadores locales de Yeoman antes de que la ayuda se completara.
El resultado importante fue:
Eso estableció claramente la condición de activación real.
La segunda prueba usó un registro local controlado y un paquete diseñado para demostrar efectos secundarios en tiempo de importación de manera segura.
Eso importaba porque quería mostrar la historia más fuerte:
Esta era la cadena de evidencia más fuerte porque movió el problema más allá de:
"intento de instalación inesperado"
y hacia:
"la ruta de instalación más la carga de código downstream es realmente alcanzable"
Ese es el punto donde la falla del límite de confianza se vuelve mucho más difícil de desestimar.
El primer PoC prueba el comportamiento de instalación silenciosa.
El segundo PoC prueba por qué ese comportamiento importa.
Esa división era importante.
Un informe que se detiene en:
"se puede instalar un paquete"
es más débil que uno que muestra:
Esa es la historia completa.
Durante el manejo del aviso y la revisión local, el comportamiento se rastreó hasta la introducción de installLocalGenerators() en:
yeoman-environment 2.9.0
Por lo tanto, el rango afectado era:
>= 2.9.0 y < 6.0.1
La versión corregida era:
6.0.1
La corrección fue correcta y mínima.
En 6.0.1, installLocalGenerators() se cambió para agregar un paso de confirmación antes de la instalación, a menos que se solicite explícitamente la instalación forzada.
La forma corregida se veía así:
async installLocalGenerators(packages, forceInstall = false) {
y luego:
const { aproveInstall } = await this.adapter.prompt({
message: `The following packages need to be installed in the local repository: ${specs.join(', ')}. Do you want to proceed?`,
type: 'confirm',
name: 'aproveInstall',
default: false,
});
Si el usuario rechaza, la instalación se cancela.
Esa es la corrección correcta porque restaura el límite de confianza faltante:
Esta corrección se integró en:
78d2af7
a través de:
PR #753
Eso es exactamente el tipo de remediación que deseas en un problema de seguridad como este:
Este problema se tomó razonablemente en serio porque el impacto es más que cosmético o un comportamiento sorprendente.
El comportamiento vulnerable puede llevar a:
El vector CVSS asociado al problema era:
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
Eso tiene sentido para la historia de explotación downstream:
Este problema comenzó como un informe privado contra generator-jhipster, porque esa era la ruta de activación del mundo real que inicialmente validé.
Durante el triaje, los mantenedores de JHipster señalaron que el comportamiento de instalación automática estaba en yeoman-environment y mencionaron la corrección upstream.
Eso llevó al giro correcto:
Los mantenedores de Yeoman revisaron el problema, confirmaron el rango afectado y lo rastrearon a través de un aviso privado.
El informe fue asignado posteriormente:
CVE-2026-42089
Ese aviso también documentó la ruta de activación downstream real a través de generator-jhipster.
Este fue un buen ejemplo de por qué la divulgación coordinada a veces necesita un paso adicional:
Aquí, la reproducción downstream fue útil, pero el paquete upstream era el lugar correcto para el CVE.
La lección clave aquí es simple:
la instalación de paquetes es un límite de seguridad, incluso en herramientas de desarrollo locales
Muchas personas desestiman instintivamente problemas como este porque ocurren en herramientas CLI.
Eso es un error.
La verdadera pregunta no es si la herramienta es local.
La verdadera pregunta es:
¿Puede la entrada no confiable hacer que la herramienta obtenga y confíe en código sin una decisión explícita del usuario?
En este caso, sí.
Esa es la verdadera conclusión.
Este problema también refuerza algo importante sobre la buena investigación de vulnerabilidades:
Esa fue exactamente la forma de este CVE.
Esta vulnerabilidad no trataba de un payload llamativo.
Trataba de hacer la pregunta correcta sobre el límite de confianza.
Una herramienta downstream permitió que los datos locales del proyecto influyeran en la selección del paquete. Yeoman instaló el paquete faltante sin confirmación. El resto de la cadena de carga de código hizo el resto.
Por eso esto se convirtió en CVE-2026-42089.
Corregido en yeoman-environment 6.0.1.