Прямой ответ
Аудит зависимостей — процесс из трёх циклов: знать (что установлено: lock-файл + SBOM), проверять (pip-audit/osv-scanner против баз CVE — в CI и периодически), действовать (обновление по факту уязвимости с оценкой «уязвимость достижима в моём коде?» — не каждая CVE в зависимости означает дыру у вас). К этому добавляется защита самой цепочки поставки: установка по lock-файлу с хешами (подмена пакета в индексе не пройдёт), осторожность с новыми зависимостями (typosquatting — reqeusts/python-nmap-подделки — реальные кампании), и пины GitHub Actions по SHA (CI-гайд). Ключевая мысль, снимающая панику: алерт о CVE — вход для оценки, а не приговор — половина уязвимостей в транзитивных зависимостях недостижима при вашем использовании, и правильная реакция — анализ с фиксацией решения, а не экстренное «обновить всё любой ценой» (которое ломает прод чаще, чем сами CVE).
Инструменты: pip-audit, osv, SBOM
pip-audit (от PyPA) — сканер окружения/lock-файла против PyPI Advisory Database: pip-audit -r requirements.txt (или по установленному venv, или --fix для автоматического подъёма версий) — вывод: пакет, версия, ID уязвимости, фикс-версия. osv-scanner (Google, база OSV.dev) — шире по источникам и языкам, удобен в CI одним бинарником. SBOM (CycloneDX/SPDX — cyclonedx-py генерирует из окружения) — инвентарь «что внутри продукта»: нужен, когда приходят вопросы «используете ли вы log4j/requests/…?» (от клиентов, аудита, регуляторов) — с SBOM ответ собирается за минуту вместо раскопок. Практика: сканер в CI на каждый PR (новые уязвимости в новых зависимостях ловятся до мержа), ночной scheduled-прогон (уязвимости публикуются в уже установленном — PR-скан этого не видит), SBOM — артефакт релиза.
Чтение CVE: достижима ли уязвимость
Алерт «критическая RCE в зависимости X» — начало анализа из четырёх вопросов. Первый: используется ли уязвимый код (CVE в XML-парсере при отсутствии XML-входа; уязвимость в CLI-утилите пакета, вызываемой только из консоли — не ваш вектор). Второй: достигается ли он недоверенными данными (уязвимость в десериализации внутреннего формата против парсинга пользовательского ввода — разные миры). Третьй: какова фикс-версия и чего стоит обновление (патч-релиз — ставить сразу; мажор — плановая задача; «фикса нет» — митигация: WAF-правило, отключение фичи, замена зависимости). Четвёртый: задокументировано ли решение («CVE-2026-XXXX: не затронуты, потому что X; пересмотрим при обновлении до Y» — запись в реестре рисков, чтобы не анализировать заново через месяц). Этот анализ занимает 15–60 минут и отличает зрелый процесс от «обновляем всё при каждом письме» (алерт-фатиг — путь к игнорированию действительно критичного).
Supply chain: подмена, тайпсквоттинг, хеши
Векторы, против которых CVE-сканер бессилен. Typosquatting: вредоносный пакет с именем-двойником (python-requests-tools, colourama) — защита: внимательность при добавлении (страница PyPI: возраст, мейнтейнеры, загрузки), внутренние белые списки новых зависимостей в команде («одобряем пакет после взгляда на репозиторий»), сканеры (pip-audit частично, специализированные — socket.dev-подобные). Подмена/компрометация релиза (украденный токен мейнтейнера, вредоносная минорная версия — реальные инциденты в npm/PyPI): lock-файл с хешами (--require-hashes / uv-lock по построению) фиксирует байты — новая сборка «той же версии» не установится; пины версий без «плавания» диапазонов в проде. CI-цепочка: GitHub Actions пинятся по SHA (uses: actions/checkout@<sha>, dependabot обновляет пины — ветка тегов может быть пересоздана), сторонние действия — только от проверенных издателей. Для своих публикаций — обратная сторона: 2FA на аккаунте PyPI, trusted publishing вместо токенов (упаковка).
Обновленческая рутина: связка с Dependabot
Аудит без обновлений — коллекционирование новостей: найденные уязвимости должны куда-то уходить. Процесс-связка: Dependabot/Renovate ведут плановые обновления (процесс); security-алерты (GitHub Security / pip-audit в CI) — вне очереди, с SLA по критичности (критичные достижимые — сутки, остальные — спринт); транзитивные зависимости обновляются через пересборку lock-файла (прямая зависимость пинит старую уязвимую транзитивку — bump родителя); записи-исключения («уязвимость принята до …») — с датой пересмотра, иначе «временное» решение живёт годами. Метрика здоровья — не «ноль алертов» (недостижимо), а время закрытия критичных и отсутствие просроченных исключений.
Практический сценарий: утро после публикации CVE
В 09:00 ночной прогон pip-audit присылает: urllib3 2.1.0 — CVE-2026-XXXXX (high, cookie leak on redirect). Процесс: (1) оценка достижимости — сервис ходит во внешние API с редиректами и передаёт cookie-авторизацию на первый хост? — да, достижимо; (2) фикс-версия 2.1.1 — патч-релиз, совместимость ожидаемая; (3) uv lock --upgrade-package urllib3 (или dependabot-PR уже открыт — security-алерты быстрее), прогон тестов, pip-audit чист; (4) деплой по стандартной стратегии (rolling) — к обеду закрыто; (5) строка в журнале: дата, CVE, решение, время закрытия — через квартал журнал показывает реальный SLA, а не ощущения. Контрсценарий (CVE недостижима): тот же анализ, запись «не затронуты: редиректы с cookie не используются», дата пересмотра — при обновлении пакета запись закроется автоматически. Оба сценария — рутина на час; разница с «паникой/игнором» — в задокументированном процессе.
FAQ
Как часто запускать сканер? В CI — на каждый PR (новое в диффе) и ночной scheduled (новое в базах CVE по установленному); локально — по желанию. Раз в квартал «вручную» — пропуск всех уязвимостей, опубликованных между запусками.
pip-audit или Snyk/коммерческие сканеры? pip-audit+OSV закрывают CVE-часть бесплатно и без отправки кода наружу; коммерческие добавляют лицензионный анализ, приватные базы и дашборды — оправданы при compliance-требованиях. Начинать с открытого стека.
Что делать, если уязвимость в заброшенной зависимости без фикса? Варианты по порядку: митигация использования (не давать недоверенные данные в уязвимый путь), замена зависимости (часто существует живой форк), вендоринг с патчем (крайняя мера с явной ответственностью), принятие риска с записью и компенсацией (WAF/изоляция) — решение принимается осознанно и документируется, а не «само рассосётся».
Нужен ли аудит для пет-проектов? Минимальный — да: pip-audit раз в месяц и обновления патчей стоят десять минут; полный процесс (SBOM, журнал рисков) — по мере появления пользователей и данных. Порог серьёзности — не размер команды, а цена утечки.
Источники: pip-audit (PyPA), OSV.dev, SLSA (supply chain levels) — ссылки в шапке.