Когда пакет тебе выбирает нейронка
На прошлой неделе я попросил Claude Code добавить рендеринг markdown в проект. Через секунду в зависимостях появился flutter_markdown, нормальный пакет от flutter.dev. Я нажал accept. А потом случайно открыл его карточку, а там прямо в шапке:
This project has been discontinued, and will not receive further updates.
Пакет уже больше года как мёртв, есть преемник: flutter_markdown_plus. Просто в обучающих данных агента он всё ещё живой.
Другой пример, ещё свежее. Скажешь Cursor «нужно поднять S3-совместимое хранилище для тестов», и он напишет docker-compose с minio/minio:latest. Только вот репозиторий minio/minio архивирован 25 апреля 2026 года, MinIO Inc. толкает всех в коммерческий AIStor (подробнее). SDK ещё обновляется, но сам сервер мёртв.
Оба факта проверяются за 30 секунд. Открыл карточку, посмотрел в шапку, увидел статус. Это даже не «надо разбираться». Это красная плашка, на которую не посмотрели. Потому что в чате с агентом ты не открываешь pub.dev. Ты нажимаешь accept.
А что насчёт живых пакетов?
С мёртвыми всё понятно. А если пакет живой, активный, миллион скачиваний?
Год назад я полтора дня дебажил неприятный баг. Flutter-приложение, авторизация через flutter_secure_storage. Де-факто стандарт хранения токенов в Keychain и Android Keystore. Активный мейнтейнер, миллионы скачиваний.
Симптом: часть пользователей iOS разлогинивает после обновления через App Store. Не всех. Не сразу. Иногда после нескольких часов в background. read() возвращает null, хотя write() отработал без ошибок. Я думал на сервер, на iOS API, на собственную инвалидацию токенов. Часов восемь ушло на перебор всех гипотез на своей стороне.
А когда я открыл GitHub Issues самого пакета, там был ад. #284: «данные пропадают после обновления через App Store». #223: «после 2+ часов в background». #524: «массово на iOS 16.3+». Сотни комментариев, годами. Корневая причина: KeychainAccessibility применялся только при SecItemAdd, а на update и read не применялся. Починили в v9.1.0.
Мне повезло: патч уже был. Стартанул бы на полгода раньше, сидел бы и ждал, пока чужой человек починит чужой код.
Баги в чужом коде — не твоя зона. Что попадает в твой проект — твоя. Первое не контролируешь никак. Второе: это решение, которое всё чаще принимает не человек, а агент. За 200 миллисекунд. Без открытой карточки. Без чтения issues.
И вот в этот момент, когда последний человеческий фильтр исчезает, появляется отдельный класс атак, рассчитанный именно на него. Раньше они были маргинальной экзотикой: человек редко ошибается в имени знакомого пакета. Теперь, когда имя пакета вообще никто не читает, угроза получила собственное название.
Slopsquatting: атака, которая без ИИ не работала бы
Термин slopsquatting появился весной 2025-го по аналогии с typosquatting’ом. Только typosquatting рассчитан на человека, который опечатался при pip install. А slopsquatting на нейронку, которая опечаталась при генерации кода.
Логика: гоняешь LLM через тысячи запросов «напиши Python-скрипт, который делает X», собираешь имена пакетов, которые она импортирует. Часть существует, часть выдуманы. Регистрируешь выдуманные на PyPI с вредоносным setup.py. Ждёшь.
Главная работа здесь: «We Have a Package for You!» (arXiv:2406.10279, USENIX Security 2025). 576 000 примеров кода, 16 моделей:
-
19,7% рекомендованных пакетов не существуют. Каждый пятый.
-
У коммерческих моделей 5,2%, у open-source 21,7%. В четыре раза разница.
-
58% галлюцинаций повторяются между запусками. Атакующему не нужно мониторить, достаточно собрать топ выдуманных имён.
В марте 2024 Bar Lanyado из Lasso Security сделал PoC: зарегистрировал на PyPI пустой пакет huggingface-cli, самое частое галлюцинируемое имя несуществующего Hugging Face CLI. За три месяца 30 000+ загрузок, в том числе у Hugging Face-owned репо и исследовательского проекта Alibaba. Пакет был пустой. Был бы атакующим, у него было бы 30 000 машин с произвольным кодом.
Честная оговорка: публично задокументированной атаки именно через slop-имя с реальным payload до сих пор нет. Это пока предсказуемая угроза, а не серия инцидентов. Не передёргивай.
Зато с supply chain в целом всё веселее. По отчёту Sonatype State of the Software Supply Chain 2026, в 2025-м в open-source реестрах нашли 454 648 новых вредоносных пакетов, 99% в npm. Из крупного:
-
ultralytics (PyPI, декабрь 2024): XMRig-майнер через GitHub Actions injection, пакет с 60 млн скачиваний.
-
@lottiefiles/lottie-player (октябрь 2024): фишинг мейнтейнера, crypto-drainer в браузере.
-
eslint-config-prettier (июль 2025, CVE-2025-54313): фишинг с домена
npnjs.com, скомпрометированы пакеты с 78 млн weekly downloads. -
Shai-Hulud (сентябрь 2025) → 2.0 (ноябрь 2025): первый self-replicating npm-червь, во второй волне 25 000+ malicious-репозиториев.
-
chalk + debug (сентябрь 2025): фишинг Qix, скомпрометированы пакеты с 2 миллиардами weekly downloads. Крупнейший npm-инцидент в истории.
Это всё происходило, пока ты смотрел в чат и нажимал accept.
Заброшенные пакеты: отдельная категория
Активный ≠ живой. Свежий релиз может оказаться бампом зависимостей бота, а мейнтейнер не отвечал на issues полгода. Проверка по четырём пунктам: last commit (не release, именно коммит), время ответа на последние пять issues, соотношение открытых к закрытым, есть ли SECURITY.md. Три из четырёх красные, ищем альтернативу.
Решение: skill, который заставляет агента проверять пакет
Решений тут вагон. Можно проверять руками. Можно pre-commit hook. Можно CI-сканер. Можно Socket / Snyk / phylum / osv-scanner. Можно Renovate с delay-окном. Всё работает, комбинация лучше.
Я не претендую на «правильный» ответ. Вы программисты, сами выберете удобное. Покажу один инструмент, который у меня стоит. Это skill для Claude Code. Дальше на ваше усмотрение.
Идея: не проверять пакеты руками, а заставить агента самого делать это до установки. У Claude Code для этого skills, у Cursor это rules. Формат разный, смысл один: markdown-файл, агент читает и следует инструкции.
Минимальный skill для Claude Code. Кладёшь в ~/.claude/skills/check-dep/SKILL.md глобально или в .claude/skills/check-dep/SKILL.md в репозитории:
---name: check-depdescription: Проверить пакет перед добавлением. Используй каждый раз, когда добавляешь новую зависимость в package.json, pubspec.yaml, requirements.txt, pyproject.toml, go.mod или Cargo.toml. Также по команде /check-dep <имя>.---# Проверка пакета перед установкойПеред любым `npm install`, `flutter pub add`, `pip install` или эквивалентом — пройди проверку. Хотя бы один красный пункт — остановись и спроси пользователя.## Шаги1. **Реестр** по контексту: npm → registry.npmjs.org, Flutter/Dart → pub.dev, Python → pypi.org, Go → pkg.go.dev, Rust → crates.io.2. **Карточка пакета через WebFetch.** Статус (discontinued/deprecated/archived), дата последнего релиза, репо, паблишер, weekly downloads.3. **Репозиторий через WebFetch.** Archived, дата последнего коммита, открытые issues, SECURITY.md, активность мейнтейнера.4. **Advisory:** для npm — `npm view <name>` + osv.dev, для PyPI — osv.dev, для pub.dev — security advisories на карточке.5. **Имя.** Похоже на популярный пакет, но не совпадает (`flutter-secure-storage` vs `flutter_secure_storage`, `requets` vs `requests`) — красный флаг.## Отчёт```Пакет: <name>@<version>Реестр: <npm|pypi|pub.dev|...>Статус: [GREEN | YELLOW | RED]— Поддержка: <active | maintenance | discontinued | archived>— Последний релиз: <date>— Последний коммит: <date>— Архивирован: <yes | no>— Issues open/closed: <X / Y>— Advisories: <none | список CVE>— Похожие имена: <none | список>Рекомендация: <ставить | спросить | не ставить, альтернатива X>```## Светофор- **RED**: discontinued, archived, последний релиз > 12 мес, critical-CVE, имя похоже на популярный пакет.- **YELLOW**: maintenance mode, релиз 6–12 мес назад, > 50 открытых issues без ответа, нет SECURITY.md, weekly downloads < 1000.- **GREEN**: всё остальное.## Если не GREENНе ставь сам. Покажи отчёт и предложи альтернативу. Альтернативу ищи в поле "Replacement" на карточке, в README основного пакета, в топ-5 категории в реестре.
Две вещи. Первое: не нажимаешь accept вслепую, агент сам приходит с отчётом. Второе: отчёт остаётся в истории, через полгода можно сделать аудит, кто одобрил.
В Cursor аналогичный файл лежит в .cursor/rules/check-dep.mdc. Синтаксис frontmatter чуть другой (globs, alwaysApply), смысл тот же.
Итог
Агент ставит в проект пакеты, которые ты не открывал. Баги внутри чужого кода ты не контролируешь, остаётся ждать патч. А за то, что попадает в package.json / pubspec.yaml / requirements.txt, отвечаешь ты.
ссылка на оригинал статьи https://habr.com/ru/articles/1051000/