
Локальный помощник установки пакетов слишком доверял именам пакетов, передаваемым вызывающей стороной. В yeoman-environment отсутствующие генераторы могли устанавливаться без подтверждения пользователя, превращая управляемые атакующим метаданные проекта в путь установки пакета и выполнения кода.
Локальный помощник установки пакетов слишком доверял именам пакетов, предоставленным вызывающей стороной. В yeoman-environment отсутствующие генераторы могли быть установлены без подтверждения пользователя, превращая управляемые атакующим метаданные проекта в путь установки пакетов и выполнения кода.
Я обнаружил эту проблему при проверке generator-jhipster с простым вопросом безопасности:
Могут ли управляемые атакующим метаданные проекта заставить инструмент разработчика загрузить и выполнить сторонний код до того, как пользователь явно запросил это?
В данном случае ответ был «да».
То, что изначально выглядело как проблема JHipster, оказалось глубокой первопричиной в yeoman-environment.
Уязвимое поведение находилось в механизме локальной установки генераторов Yeoman, где отсутствующие пакеты, предоставленные вызывающей стороной, устанавливались автоматически без подтверждения пользователя. В потребителе, передающем управляемые атакующим имена пакетов в этот путь, этого было достаточно для создания реальной цепочки установки пакетов и выполнения кода.
Эта проблема стала CVE-2026-42089.
yeoman-environment: yeoman-environment на GitHub
Пакет: yeoman-environment (npm)
CVE: CVE-2026-42089
Это затронуло yeoman-environment — слой выполнения, лежащий в основе загрузки и начальной загрузки генераторов Yeoman. Официальный проект описывает его как компонент, управляющий жизненным циклом и обнаружением генераторов, и по состоянию на 26 июня 2026 г. на странице пакета npm значилось 1 466 426 еженедельных загрузок, что делает этот пакет широко распространённым в экосистеме инструментов JavaScript.
конфигурация проекта, управляемая атакующим -> имена пакетов генераторов, предоставленные вызывающей стороной -> yeoman-environment молча устанавливает отсутствующие пакеты -> потребитель загружает установленный код генератора -> установка пакетов и выполнение кода во время начальной загрузки CLI
yeoman-environment — это среда выполнения и слой загрузки генераторов, лежащий в основе инструментов на базе Yeoman.
Среди прочего он обрабатывает:
То есть он находится прямо на границе доверия.
Соответствующий вопрос не в том, является ли Yeoman «просто локальным инструментом».
Соответствующий вопрос в том, может ли недоверенный ввод влиять на поведение установки пакетов и загрузки кода.
В данном случае — мог.
Я не искал здесь повреждения памяти или ошибки только с аварийным завершением.
Более сильной целью была поверхность расширения и разрешения пакетов.
Любая система, которая:
заслуживает пристального внимания.
Это особенно верно, когда потребитель может извлекать эти имена пакетов из локальных данных проекта.
Это именно то место, где обычная конфигурация может незаметно превратиться в границу безопасности.
Там и следовало искать.
Сначала я воспроизвел поведение через generator-jhipster.
Важный путь был таким:
.yo-rc.json проекта объявляет пакет blueprintЭто означало, что даже такая безобидная команда, как:
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 по умолчанию обрабатывал имена пакетов, предоставленные вызывающей стороной, как устанавливаемые без подтверждения пользователя.
Это делает небезопасные предположения доверия у потребителей материально хуже.
Именно поэтому исправление добавило шлюз подтверждения.
Я использовал два уровня доказательств, потому что они демонстрируют как первопричину, так и реальное влияние на потребителя.
Первое доказательство использовало немодифицированный generator-jhipster.
Я создал проект с корневым .yo-rc.json, который ссылался на пакет blueprint, ещё не установленный, а затем выполнил:
jhipster --help
Это заставило JHipster передать отсутствующий blueprint в поток локальной установки генераторов Yeoman до завершения справки.
Важный результат:
Это чётко установило реальное условие срабатывания.
Второе доказательство использовало контролируемый локальный реестр и пакет, предназначенный для безопасной демонстрации эффектов на стороне импорта.
Это было важно, потому что я хотел показать более сильную историю:
Это была самая сильная цепочка доказательств, поскольку она вывела проблему за рамки:
«неожиданная попытка установки»
и перешла к:
«установка плюс последующий путь загрузки кода действительно достижимы»
Это та точка, где сбой границы доверия становится гораздо труднее игнорировать.