npm-червь ставит хуки в ваш AI-агент
4 августа 2026 года в 09:35 UTC в npm вышла версия keyv@6.0.0 со вредоносной полезной нагрузкой. В течение часа следом пошло всё семейство cacheable: flat-cache, file-entry-cache, cacheable-request и другие. Кампания — новая волна Shai-Hulud — затронула более 400 имён пакетов и свыше тысячи версий, суммарно более двух миллиардов установок в месяц.
Интересен здесь не масштаб. Интересно то, что нагрузка не ограничилась кражей учётных данных: она оставила после себя вход внутрь инструментов самого разработчика — включая AI-агента.
Проблема: доверие спускается вниз по дереву
keyv почти никто не выбирает осознанно. Он приходит через eslint, через сборочные инструменты, через слои, которые никто не открывает. Отравите пакет на такой глубине — и всё дерево под ним становится каналом доставки.
Хуже другое: provenance отработал ровно так, как задумано. Манифест npm фиксирует, что keyv@6.0.0 опубликован через GitHub Actions в роли trusted publisher, с валидными аттестациями. Вредоносный код уже находился в репозитории, когда легитимный workflow собирал и подписывал артефакт. Подпись подтверждает происхождение файла, а не его содержимое.
Что изменилось: закрепление внутри инструментов разработчика
Нагрузка работает в несколько этапов. Хук preinstall запускает setup.mjs при установке. Загрузчик подтягивает второй файл, Math_Symbol.js, который сканирует машину в поисках токенов GitHub и npm, облачных ключей, токенов Vault, служебных токенов Kubernetes и строк подключения к базам данных — а затем использует найденное, чтобы переопубликовать себя через чужие аккаунты.
Отдельного внимания заслуживает третий этап. Червь записывает в сам репозиторий два файла:
.claude/settings.jsonс хукомSessionStart;.vscode/tasks.jsonс задачей и параметром"runOn": "folderOpen".
Удалите node_modules — путь выполнения останется. Достаточно, чтобы кто-то открыл папку в редакторе или начал сессию с агентом. Это не изъян самих инструментов: VS Code закрывает автоматические задачи политикой workspace trust, а агент применяет ту же модель доверия к настройкам из репозитория. Но в проекте, с которым команда работает ежедневно, это доверие выдано давно.
Практическая проверка
Сначала ищите следы закрепления, только потом меняйте ключи:
# 1) подсаженные пути выполнения внутри репозитория
grep -rn "Math_Symbol" . --include="*.js" --include="*.mjs" 2>/dev/null
grep -n "SessionStart" .claude/settings.json 2>/dev/null
grep -n "folderOpen" .vscode/tasks.json 2>/dev/null
# 2) не тянет ли дерево заражённую версию?
npm ls keyv flat-cache file-entry-cache cacheable cacheable-requestЗатем закройте дверь по всему дереву через overrides в package.json:
{
"overrides": {
"keyv": "<6.0.0",
"flat-cache": "<6.1.24",
"file-entry-cache": "<11.1.6",
"cacheable": "<2.5.1",
"cacheable-request": "<13.0.20"
}
}Оговорки из практики
Порядок важен. Ротация токенов до удаления закрепления просто отдаёт атакующему новые ключи. Сначала чистка, потом ротация.
Отзыв версии не мгновенный. Публичные предупреждения появились около 10:18 UTC; час спустя восемь релизов всё ещё резолвились по тегу latest. Любая сборка в этом окне могла подтянуть отравленный артефакт.
Одного `--ignore-scripts` мало. Эта кампания использовала preinstall, но предыдущие волны переносили выполнение на import time — об этом мы писали раньше. Отключение скриптов — шаг, а не решение.
Настоящая защита — lockfile. npm ci плюс осознанная задержка перед переходом на свежеопубликованную версию сокращают окно уязвимости сильнее любой постфактум-проверки.
Итог
Цепочка поставок больше не заканчивается на реестре. Теперь она продолжается в конфигурационных файлах, которые редактор и агент читают при открытии проекта. Относитесь к .claude/ и .vscode/ как к исполняемому коду: проверяйте их в код-ревью с тем же вниманием, что и обновление зависимости.
Первоисточники: разбор Snyk и отчёт Socket.