Берегите очко смолоду
Часть 8 — про зависимости, вайб-код: чужой код в проекте и свой, который «работает».
Почему это касается не только кода#
Зависимость редко выглядит опасной в момент установки. Она просто решает задачу: парсит файл, рисует график, ходит в API. Проблема появляется позже, когда в проекте уже десятки пакетов, часть из них устарела, а часть никто не понимает.
Вайб-код усиливает этот эффект: агент быстро добавляет библиотеку и зелёный тест, но не всегда проверяет цену решения. Поэтому зависимости нужно ревьюить так же внимательно, как собственный код.
Почему это касается не только кода#
Зависимость редко выглядит опасной в момент установки. Она просто решает задачу: парсит файл, рисует график, ходит в API. Проблема появляется позже, когда в проекте уже десятки пакетов, часть из них устарела, а часть никто не понимает.
Вайб-код усиливает этот эффект: агент быстро добавляет библиотеку и зелёный тест, но не всегда проверяет цену решения. Поэтому зависимости нужно ревьюить так же внимательно, как собственный код.
Однажды я просто запустил аудит зависимостей на небольшом проекте — скорее из любопытства, чем из тревоги. Отчёт выкатил порядка пятидесяти с лишним уязвимостей. Несколько — high и critical. И это при том, что я честно писал «свой» код аккуратно. Просто «свой» код в современном проекте — это процентов пять от того, что реально исполняется.
Дерево зависимостей — это поверхность атаки#
Вы ставите один веб-фреймворк, а вместе с ним приезжает сотня пакетов, которые вы никогда не открывали. И уязвимости живут именно там. В том заходе среди находок был и сам веб-фреймворк — связка из обхода прокси, SSRF, отказа в обслуживании и XSS. А под ним, в транзитивных зависимостях, — ReDoS на безобидной с виду регулярке и старое доброе prototype pollution.
Первый инстинкт — пойти точечно патчить каждую строчку отчёта руками. Это путь в ад. Чинить надо фазами.
Сначала прямые зависимости: поднять их до свежих версий, потому что чаще всего уязвимость уже починена выше по дереву, просто вы сидите на старой.
pnpm up --latest
pnpm audit
Что осталось после этого — это транзитивные пакеты, которые тянет кто-то из ваших зависимостей и держит на старой версии. Их добиваешь не хаками, а через overrides: говоришь пакетному менеджеру «вот этот транзитивный пакет везде бери не ниже такой версии».
{
"pnpm": {
"overrides": {
"uязвимый-пакет@<1.2.3": ">=1.2.3"
}
}
}
Контринтуитивная находка: алерты были выключены#
Самое обидное я понял уже потом. На уровне репозитория были отключены security-алерты Dependabot. Логика, как мне казалось, была здравой: меня задолбали PR с мажорными бампами, я их выключил и забыл.
Проблема в том, что одним тумблером я снёс заодно и уведомления о безопасности. Это разные вещи, но живут рядом, и легко выключить оба, думая, что глушишь только шум. Обновления ради версии и алерты о реальных дырах — это два независимых переключателя. Шум можно глушить. Алерты безопасности должны быть включены всегда.
Пиннинг и воспроизводимость#
Дальше — про контроль над тем, что вообще попадает в сборку. Если ваш билд каждый раз тянет «последнее», то однажды он тихо втянет скомпрометированную версию, и вы даже не поймёте, в какой момент это случилось.
Поэтому базовые образы пинятся по digest, а не по плавающему тегу вроде latest. Шаги GitHub Actions — по конкретному коммиту (SHA), а не по тегу, который автор может переписать. Lockfile — frozen: на CI установка падает, если реальное дерево разошлось с зафиксированным.
- uses: actions/checkout@<полный-sha-коммита>
pnpm install --frozen-lockfile
Воспроизводимость — это и есть контроль над supply-chain. Если сборка детерминирована, у атакующего меньше щелей, чтобы подсунуть что-то между «я проверил» и «оно собралось».
Install — это исполнение кода#
Про что часто забывают: install не просто скачивает файлы. Пакеты умеют выполнять postinstall-скрипты, и это произвольный код, который запускается у вас на машине и на CI с вашими правами. Скомпрометированный мелкий пакет может ничего не делать в рантайме и весь свой вред нанести именно на установке.
Современные пакетные менеджеры дают allow-list: по умолчанию build-скрипты заблокированы, и выполняются только те пакеты, которым вы явно разрешили. Это разумный дефолт — пусть установка по умолчанию ничего не исполняет, а исключения вы добавляете осознанно.
И да — транзитивные dev-зависимости тоже считаются. «Это же только сборочный инструмент, в прод-артефакт он не попадает» — слабое утешение, когда уязвимость в этом инструменте исполняется ровно там, где у вас лежат исходники и секреты сборки. Чинится такое нередко только через override, потому что напрямую вы этот пакет даже не ставили.
«Работает» — не значит «безопасно»#
Вторая половина истории — про код, который пишется наспех. Я раз за разом ловлю один и тот же набор паттернов, и он подозрительно стабилен:
- публичный Swagger, забытый включённым в проде, — готовая карта всех ручек;
- продублированная бизнес-логика, где копия не знает про лимиты и проверки оригинала;
- отсутствие идемпотентности в одной из двух почти одинаковых веток — ровно та история из части про деньги;
- самописное экранирование строк перед SQL вместо нормальной параметризации;
- проверка прав уже после того, как мутация выполнена.
Такой код великолепно проходит сценарий «happy path» и зелёный CI. Он гораздо хуже думает про то, чего в задаче не написали: про злоумышленника, про повтор, про гонку, про права. «Работает» отдаёт легко. «Безопасно» — почти никогда по своей инициативе.
Поэтому зелёный CI и «вроде всё крутится» — это не сертификат безопасности. Это значит ровно то, что заявленные сценарии отработали.
И вот здесь две темы сходятся. Supply-chain и наспех написанный код — про одно и то же по сути: в проект постоянно затекает код, который написали не вы. Часть притащили зависимости, часть написали сами — но на скорую руку. И тот, и другой надо ревьюить не только на «делает ли он то, что нужно», но и на «что он делает, когда его попросят сделать плохое». Это другой взгляд, и его приходится включать осознанно — сам он не включается.
Если проект быстро растёт на зависимостях, генерации кода и зелёном CI, стоит проверить не только версии пакетов. Напишите мне в Telegram или на почту — помогу разобрать supply chain, обновления, автогенерированный код и проверки, которые должны ловить опасное поведение до прода.



