Гигиена зависимостей: lock-файлы, заброшенные пакеты и ритм обновлений
Как держать зависимости под контролем: воспроизводимые сборки, выбор пакетов, базовая защита от атак через цепочку поставок и спокойный ритм обновлений.
Открой package.json любого приложения, собранного с ассистентом, и посчитай зависимости. Потом посчитай, сколько из них ты выбирал сам. Обычно разница большая: ассистент ставит пакет на каждую задачу, включая те, что решаются тремя строками. Каждый такой пакет — чужой код, который выполняется в твоей сборке и на твоём сервере, и обновляется он по чужому расписанию.
Lock-файл — в репозиторий
Lock-файл фиксирует точные версии всего дерева зависимостей, включая зависимости зависимостей. Без него две сборки одного и того же коммита могут получить разный код.
package-lock.json,pnpm-lock.yaml,poetry.lockи подобные коммитятся всегда.- В CI и при сборке для продакшна используется установка строго по lock-файлу:
npm ci, а неnpm install. Первая падает при расхождении, вторая молча его исправляет. - Изменения lock-файла в пул-реквесте читаются. Если задача была «поправить кнопку», а в lock-файле поменялось двести строк, стоит спросить почему.
Прежде чем добавить пакет
Самая дешёвая зависимость — та, которую не поставили. Перед установкой задай три вопроса:
- Можно ли обойтись стандартной библиотекой или парой строк своего кода?
- Кто это поддерживает? Когда был последний релиз, отвечают ли на issues, один ли там мейнтейнер?
- Сколько он тянет за собой? Маленький пакет с сотней транзитивных зависимостей — не маленький.
Заброшенные пакеты
Пакет без релизов несколько лет — не обязательно проблема: некоторые вещи просто дописаны. Проблема — когда в нём находят уязвимость, а исправлять некому. Признаки: архивированный репозиторий, открытые сообщения о безопасности без ответа, несовместимость с новыми версиями рантайма.
Раз в квартал пройдись по прямым зависимостям и отметь такие. Для каждой реши заранее, чем заменишь, — заменять в спокойном режиме дешевле, чем в день публикации уязвимости.
Цепочка поставок: базовый минимум
Атака через зависимости — это когда вредоносный код приходит не через твой сервер, а через пакет, которому ты доверяешь.
- Опечатки и выдумки. Пакет с названием, похожим на популярный. Отдельный риск с ИИ: ассистент может предложить пакет, которого не существует, а кто-то может его зарегистрировать как раз под такие подсказки. Перед установкой незнакомого пакета проверь, что он настоящий и тот самый.
- Скрипты установки.
postinstallвыполняется на машине разработчика и в CI с их правами. В CI можно устанавливать с--ignore-scriptsи разрешать скрипты только тем пакетам, которым они действительно нужны. - Захваченные аккаунты мейнтейнеров. Новая патч-версия популярного пакета может оказаться вредоносной. Lock-файл и отсутствие автоматического обновления в продакшн-сборке защищают от того, чтобы она приехала к тебе сама.
Ритм обновлений
Обновлять всё раз в год — гарантия, что обновление превратится в проект на две недели с поломками в неожиданных местах. Обновлять каждый день вручную — гарантия, что этим перестанут заниматься.
Работает такой ритм:
- Мелкие обновления — пачками, регулярно. Бот вроде Dependabot или Renovate раз в неделю или две открывает пул-реквест с минорными и патч-версиями. Тесты прошли — сливаешь.
- Мажорные версии — отдельно и по плану. Одна за раз, с чтением списка изменений.
- Исправления безопасности — сразу. Но сначала пойми, затрагивает ли уязвимость тебя: отчёты аудита шумные, и половина находок — в инструментах сборки, которые не попадают в продакшн. Сортируй по тому, исполняется ли уязвимый код у тебя на сервере.
Уборка
Ассистент ставит пакеты, но редко их удаляет. Инструменты вроде depcheck или knip находят зависимости, которые больше нигде не используются. Каждая удалённая — минус одна вещь, которую надо обновлять и которой надо доверять.
Если хочешь навести порядок в зависимостях без риска что-нибудь сломать, SUBXCAT может разобрать их и настроить обновления так, чтобы они шли сами.