SBOM: где ломается учёт зависимостей в сборке
Сборка проходит зелёной, образ уходит в реестр, релиз едет в прод. Через несколько дней приходит уведомление об уязвимости в транзитивной зависимости — и точный состав этой поставки восстановить нечем: манифесты перезаписаны следующей сборкой, файл фиксации версий в артефакт не попал, версии подтягивались по диапазону. Вопрос «есть ли у нас уязвимый компонент именно в этой версии?» остаётся без ответа.
Дальше начинается ручной перебор. Инженер ищет коммит по дате релиза, пересобирает проект тем же набором исходников и всё равно не может утверждать, что получил тот же результат: обновлённый минорный пакет, другой набор системных библиотек в базовом образе, кэш внутреннего прокси. На такую реконструкцию уходят часы, а окно между обнаружением уязвимости и подтверждённым ответом всё это время открыто.
Корень в том, что SBOM рождается не там, где должен. Его генерируют отдельным шагом постфактум, по файлу зависимостей и вне контекста сборки: в список попадают прямые зависимости, но не то, что реально легло в слои образа — транзитивные пакеты системного менеджера, вендорные библиотеки, скопированные в артефакт. Без привязки к неизменяемому идентификатору образа документ описывает «примерно эту сборку», а не конкретную поставку, и учёт компонентов перестаёт быть проверяемым.
Плюс разрозненность форматов: одна команда отдаёт состав сборки в одном виде, другая в другом, и свести их в один запрос без ручной нормализации нельзя. Разбираем, как встроить генерацию в конвейер так, чтобы прозрачность поставки подтверждалась для каждого артефакта.
Практическая польза регулярной генерации SBOM
Регулярная генерация SBOM превращает перечень зависимостей из разовой бумажки в поток данных. Генератор Anchore Syft запускается в сборочном конвейере на каждую сборку основной ветки, читает дерево зависимостей из манифестов менеджеров пакетов и файловой системы образа, а результат складывает документом в формате CycloneDX рядом с самим артефактом. Дальше документ подписывается и уезжает в реестр: версия образа и её состав связаны жёстко, а не по памяти инженера.
Когда приходит оповещение об уязвимости в транзитивной библиотеке, работа идёт запросом к реестру, а не обходом десятков репозиториев. Ответ — перечень сервисов и версий, где компонент присутствует, с точностью до сборки. Разбор инцидента сокращается с нескольких часов на согласование «где это вообще используется» до минут: сразу видно, что патчить, а что можно не трогать.
Прослеживаемость работает и в обратную сторону. Для любого запущенного контейнера поднимается его SBOM, и на вопрос «какие компоненты и под какими лицензиями сейчас в проде» отвечает документ, а не реконструкция по истории изменений. Это же закрывает внешние запросы по безопасности: состав поставки выгружается из реестра, а не собирается заново.
Контроль зависимостей держится на сравнении двух SBOM: разница показывает, что реально изменилось при обновлении базы образа или версии библиотеки. Неучтённые транзитивные зависимости, которые тянутся вглубь на несколько уровней, перестают быть сюрпризом — класс отказов «после релиза выяснилось, что приехало лишнее» уходит из практики.
Ключевой эффект даёт именно регулярность: расписание в конвейере даёт непрерывную картину, разовый снимок — только фотографию на дату. Вместо десяти таблиц и заметок в разных местах появляется единый реестр компонентов, с которым работают и разработка, и безопасность.
Способы формирования состава: сравнение подходов
Описание состава можно получить тремя разными путями, и они не взаимозаменяемы. Автоматическая генерация на этапе сборки фиксирует всё, что реально попало в артефакт; полуручной режим добавляет записи о компонентах, которые анализатор не распознаёт; разовый разбор готового артефакта нужен там, где пересборка не планируется. Форматы описания состава — CycloneDX или SPDX — выбираются отдельно от способа: важна совместимость с системами, которые будут эти данные читать.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Автоматический, в конвейере сборки | Регулярные релизы, нужен свежий состав на каждый артефакт | Требует отдельной стадии; динамические загрузки видны не всегда |
| Полуручной, с ручной верификацией | Смешанный стек, часть компонентов ставится вне пакетных менеджеров | Нужен регламент поддержки; состав устаревает между ревизиями |
| Разовый разбор готового артефакта | Системы, которые не пересобираются | Даёт срез на дату разбора, сам не обновляется |
Разница между вариантами — это разница между свежестью данных и стоимостью их поддержки. Автоматический режим даёт максимальную глубину анализа: видны прямые и транзитивные зависимости, вложенные системные пакеты. Ограничение генерации — компоненты, подгружаемые во время работы из внешних источников, в такой разбор не попадают.
Полуручной подход закрывает эту дыру ценой регламента: у каждой ручной записи должны быть версия и источник, иначе описание теряет достоверность. Разовый разбор полезен как отправная точка — он показывает полную картину на текущий момент, но не обновляется вместе с системой. Рабочая схема — автоматическая генерация в конвейере как основа плюс периодический пересбор состава для редко обновляемых систем.
Как мы настраиваем генерацию SBOM: этапы работ
Настройка генерации SBOM — это не подключение одного расширения, а последовательность из шести шагов: от инвентаризации компонентов до передачи документации. Каждый этап отвечает за свой класс ошибок, и пропуск любого из них приводит к перечню, который формально существует, но не отражает реальный состав артефакта.
- Инвентаризация компонентов. Обходим репозитории, фиксируем языки, менеджеры зависимостей, наличие транзитивных и вендорных библиотек, компонентов внутри образов. На выходе — перечень сервисов с пометкой, где генерация покрывает всё автоматически, а где нужна ручная доводка.
- Схема потоков. Определяем, в какой момент формируется документ — при сборке артефакта или при выпуске образа, где он хранится и как связывается с конкретной сборкой, чтобы по номеру релиза можно было найти именно его перечень.
- Встраивание в сборку. Генерация становится шагом конвейера: если документ не сформирован, сборка останавливается. Так перечень перестаёт быть необязательным артефактом, который делают «когда попросят».
- Настройка правил. Выбираем формат — SPDX или CycloneDX, задаём состав полей, глубину разбора зависимостей и поведение для компонентов без метаданных: помечаем явно, а не выкидываем молча.
- Проверка результата. Сверяем состав из документа с фактическим содержимым артефакта, проверяем версии, лицензии и дубликаты. Расхождения — это либо ошибка генератора, либо следы ручных правок сборки.
- Передача документации. Отдаём схему, описание правил, инструкцию для разработчика и порядок обновления при смене зависимостей, чтобы настройка не разошлась с реальной сборкой при первом же крупном изменении.
Состав поставки: конфигурации, схемы и регламенты
Настройка генерации SBOM — это не разовый отчёт по одному релизу, а изменение сборочного контура: сборка сама формирует перечень компонентов, подписывает его и передаёт в хранилище. Заказчику остаётся не «магия подрядчика», а набор артефактов, по которым его команда работает дальше самостоятельно и воспроизводит результат на новом проекте.
Что именно передаём:
- Конфигурации сборочного контура — задания и шаблоны этапов, где включена генерация перечня, с пояснениями по каждому параметру.
- Схемы процессов — путь от изменения кода до публикации SBOM: точки сборки, проверок и передачи артефакта, с ролями и ответственными.
- Политики контроля состава — правила остановки сборки при компоненте с известной уязвимостью или недопустимой лицензией.
- Политики доступа — матрица прав на перечни, настройка подписей и проверка подлинности артефактов.
- Шаблоны отчётов — выгрузки в стандартных форматах описания состава (SPDX, CycloneDX) с фильтрами по критичности.
- Инструкции для команды — как выпустить релиз, откатить правило и разобрать расхождение в составе.
- Регламент обновления — периодичность пересмотра запрещённого списка и порядок действий при смене зависимостей.
Каждый пункт принимаем на вашем контуре: запускаем сборку с намеренно проблемной зависимостью и проверяем, что правило срабатывает, а отчёт формируется корректно.
Кейс: как сократился разбор инцидентов после внедрения
Что было до автоматизации
Инцидент с уязвимостью в транзитивной библиотеке разбирали вручную: инженер поднимал журналы сборок, сверял манифесты зависимостей и файлы фиксации версий по каждому сервису. На определение полного списка затронутых компонентов уходило от 4 до 6 часов, а по части сборок картина оставалась неполной — приходилось пересобирать сервисы «на всякий случай». При десятках репозиториев это растягивалось на отдельный рабочий день и задерживало выпуск исправления.
Что изменили
Генерацию SBOM включили прямо в шаг сборки: документ состава компонентов создаётся автоматически и привязывается к неизменяемому идентификатору образа, а не к имени тега. Сообщение об уязвимости из внешнего источника запускает поиск по всем накопленным документам — в Dependency-Track такой запрос возвращает перечень затронутых сборок за минуты, включая транзитивные зависимости, которых нет в явных манифестах.
Отдельно закрыли вопрос качества данных: образ считается выпущенным только при наличии SBOM, поэтому доля сборок с документом состава дошла до 100%.
Метрики после
Время определения затронутых компонентов сократилось с 4–6 часов до 15–25 минут, причём это полный перечень, а не выборка по нескольким сервисам. Количество пересборок «для подстраховки» упало почти до нуля: видно, в каких образах компонент есть, а в каких нет. Разбор инцидента больше не зависит от того, кто из инженеров помнит историю конкретного репозитория.
Чем отличается разовый отчёт о составе от регулярной генерации SBOM?
Разовый отчёт — снимок состава на момент запроса: инвентаризация того, что уже развёрнуто. Регулярная генерация привязывает SBOM к каждому артефакту на этапе сборки, поэтому документ существует для любой версии, попавшей в среду. Разница не в формате файла, а в том, какие вопросы система способна закрыть.
| Критерий | Разовый отчёт | Регулярная генерация |
|---|---|---|
| Актуальность данных | Устаревает после первого же релиза | Обновляется с каждой сборкой |
| Трудозатраты | Ручной сбор под каждый запрос | Настраивается один раз в конвейере |
| Покрытие выпусков | Только попавшие в выборку версии | Все артефакты, включая откаты |
| Повторные проверки | Каждый раз заново | Выборка из готового хранилища |
| Новая уязвимость | Инвентаризация с нуля | Поиск по компоненту за минуты |
Актуальность — ключевое расхождение. Снимок отвечает на вопрос «что было в системе в момент аудита» и после пары релизов теряет связь с реальностью: в нём остаются снятые зависимости и нет новых. Когда приходит уведомление об уязвимости, нужно за минуты понять, какие работающие версии её содержат, — по снимку это требует нового обхода.
Второй водораздел — повторяющиеся проверки. Регулярная генерация превращает ответ на запрос безопасности в выборку по индексу компонентов: сборка публикует SBOM в хранилище, поиск по затронутой библиотеке выдаёт список версий и сред. Ручной отчёт такого сценария не выдерживает — стоимость повторения сопоставима с первой инвентаризацией.
Рабочая схема — комбинировать: разовый аудит для унаследованных систем, которые уже работают и не пересобираются, регулярная генерация — для всего, что выпускается дальше. Правила подписи и хранения SBOM делают такую проверку воспроизводимой, а не зависящей от того, кто и когда вспомнил собрать отчёт.
Технические сомнения: частые вопросы по внедрению
Сомнения вызывает не сама генерация перечня, а её встраивание в работающий конвейер сборки: что придётся менять, как быть с компонентами, поставленными руками, и не окажется ли перечень неполным. Собрали четыре возражения, которые чаще всего звучат на этапе оценки, и ответ на каждое.
Придётся ли перестраивать конвейер сборки?
Шаг генерации добавляется после формирования образа, остальные этапы и их параметры не меняются. Он вне критического пути: при недоступности генератора образ публикуется с пометкой, а обязательность перечня включается отдельным правилом.
Что с компонентами, поставленными руками?
Их находит сканирование содержимого образа: сверяются пути установки, сигнатуры файлов и записи менеджеров пакетов. Такие библиотеки попадают в перечень с пометкой источника, а доля нераспознанных файлов идёт в отчёт.
Как убедиться, что перечень полный?
Полнота проверяется сравнением с эталоном и покрытием по слоям образа: считается доля файлов, отнесённых к известным компонентам. Расхождение выше порога становится блокирующим правилом, а не предупреждением.
Уже есть сканер и хранилище — отказываться?
Перечень отдаётся в стандартных открытых форматах, поэтому подхватывается текущими сканерами и реестром без замены. Генератору нужен доступ на чтение образов и запись в хранилище перечней — политики описываем отдельно.
Если вашего сценария в списке нет — опишите его, вернёмся с оценкой и планом.Обсудим задачу: что прислать для оценки и плана
Чтобы вернуться с оценкой сроков и планом работ по генерации состава ПО (SBOM), доступ к вашему контуру на старте не нужен. Достаточно описания того, как устроены сборка и релиз, — этого хватает, чтобы увидеть объём работ и узкие места.
Что описать по пунктам:
- Сборочный контур — какие системы собирают артефакты, где хранятся зависимости и есть ли доступ к манифестам на этапе сборки, а не только в готовом образе.
- Языки и менеджеры пакетов по сервисам — от них зависит, какие форматы состава мы генерируем и как сводим их в один документ.
- Тип артефактов — образы, архивы, системные пакеты: для каждого нужен свой набор источников данных.
- Потребитель состава — реестр, система контроля уязвимостей или внешний аудит: от этого зависит формат выгрузки и степень детализации.
- Точка встраивания — состав появляется в процессе сборки, перед публикацией артефакта или по расписанию для уже выпущенных версий.
- Хранение и история — сколько версий состава нужно держать и как быстро находить состав конкретного релиза.
- Ограничения по доступу — закрытые сегменты и изолированные контуры, где сборка идёт без внешней сети.
Если части данных пока нет — опишите как есть: пробелы учитываем в плане работ и закрываем на этапе обследования. По описанию вернёмся с оценкой сроков, составом этапов и тем, что понадобится от вашей команды.







