Инструмент для безопасной генерации, анализа и форматирования Software Bill of Materials (SBOM).
Документация (docs/):
- Руководство пользователя — быстрый старт
- Полное руководство (Manual) — все команды, флаги и примеры
- Демонстрация команд — гифки работы CLI
- Примеры результатов — образцы SBOM и отчётов
- Права доступа — требования к файловой системе
- Troubleshooting — частые проблемы и их решения
Разделы README:
Что делает:
- Генерирует SBOM двумя генераторами (cdxgen + syft) и объединяет результат — из локальной директории, Git-репозитория (GitHub / GitLab) или контейнерного образа
- Дедуплицирует компоненты по PURL и уязвимости по CVE+компонент
- Создаёт два подписанных SBOM: без уязвимостей и с ними
- Сканирует уязвимости параллельно через Trivy, OWASP Dependency-Check, Clair
- Встраивает найденные уязвимости в SBOM (CycloneDX 1.5)
- Опционально обогащает уязвимости идентификаторами БДУ ФСТЭК
- Экспортирует читаемые отчёты: Excel (.xlsx), Word (.docx), ODT (.odt)
- Готовит SBOM под требования ФСТЭК (
secsbom cert— поля GOST)
PyPI:
pip install sbom-pipelineGitHub Packages:
pip install sbom-pipeline \
--index-url https://${GITHUB_TOKEN}@pypi.pkg.github.com/geminishkv/Исходники:
git clone https://github.com/geminishkv/sbom_genform.git
cd sbom_genform
python3 -m venv venv && source venv/bin/activate
pip install -e ".[dev]"После установки доступна помощь:
secsbom --help📖 Полное пошаговое руководство со всеми командами, сценариями и переменными окружения — в docs/USAGE.md.
Утилиту нужно запускать от обычного пользователя. Запуск с правами администратора/root завершается ошибкой, чтобы не выполнять сканеры и Docker-команды с лишними привилегиями.
Полный пайплайн генерирует SBOM, дедуплицирует компоненты, подписывает SBOM, сканирует уязвимости и экспортирует отчёты:
# локальный демо-проект
secsbom run
# указание локального проекта из примеров
secsbom run --path examples/project_inject
# удалёный репозиторий github с токеном
secsbom run --url https://github.com/org/repo --token ghp_...
# удалёный репозиторий gitlab с токеном
secsbom run --url https://gitlab.com/org/repo --token glpat-... По умолчанию артефакты пишутся в secgensbom_out, отчёты — в
secgensbom_reports.
Сгенерировать и подписать SBOM без этапа сканирования уязвимостей:
secsbom gen-sbom --path examples/project_injectПросканировать уже готовый SBOM (и/или контейнерный образ) и сформировать отчёты только по уязвимостям:
# путь к папке проекта + готовый SBOM
secsbom scan secgensbom_out/app-bom-dedup-signed.json --path examples/project_inject
# только готовый SBOM
secsbom scan secgensbom_out/app-bom-dedup-signed.json
# только контейнерный образ
secsbom scan --clair --image nginx:latestОтформатировать готовые SBOM JSON в Excel/Word/ODT:
secsbom format --sbom-dir secgensbom_out --report-dir secgensbom_reportsПроверить подпись SBOM:
secsbom verify secgensbom_out/merged-bom-signed.jsonПримечание: SHA-256-подпись — это контрольная сумма целостности (защита от случайной порчи), а не криптографическая подпись: она не подтверждает аутентичность источника, поскольку хэш и
.sigпересчитываются после любого изменения файла. Для аутентичности и неотрекаемости используйте внешнюю подпись (cosign / sigstore).
Посмотреть краткую информацию по SBOM:
secsbom info secgensbom_out/merged-bom-signed.jsonПроверить наличие внешних инструментов:
secsbom statusСравнить два SBOM:
secsbom diff old-bom.json new-bom.jsonДобавить поля GOST для отчёта по требованиям ФСТЭК:
secsbom cert sbom.json \
--component-name "My Product" \
--component-version "1.0.0" \
--manufacturer "My Company"secsbom run --no-cdxgen # не использовать cdxgen
secsbom run --no-syft # не использовать syft
secsbom run --bdu # включить BDU-обогащение
secsbom run --clair --image nginx:latest
secsbom run -o secgensbom_out --reports-dir secgensbom_reportsТе же настройки можно передавать через переменные окружения:
export PROJECT_DIR=examples/project_inject
export OUTPUT_DIR=secgensbom_out
export REPORTS_DIR=secgensbom_reports
export BDU=true
export NVD_API_KEY=<token>
secsbom runДля scan используются кэши внешних данных:
.dependency-check-data/DEP_CHECK_DATA— база NVD для OWASP Dependency-Check.bdu_cache/BDU_CACHE_DIR— соответствия CVE -> BDU ID
Сами этапы scan при повторном запуске выполняются заново, кэшируются именно
базы и справочные ответы внешних инструментов.
| Файл | Описание |
|---|---|
secgensbom_out/app-bom-cdxgen.json |
SBOM от cdxgen |
secgensbom_out/app-bom-syft.json |
SBOM от syft |
secgensbom_out/app-bom-merged.json |
Объединённый SBOM (cdxgen + syft) |
secgensbom_out/app-bom-dedup.json |
После дедупликации компонентов |
secgensbom_out/app-bom-dedup-signed.json |
Подписанный SBOM без уязвимостей |
secgensbom_out/app-bom-dedup-signed.sig |
SHA-256 контрольная сумма (без уязв.) |
secgensbom_out/merged-bom-signed.json |
Подписанный SBOM с уязвимостями |
secgensbom_out/merged-bom-signed.sig |
SHA-256 контрольная сумма (с уязв.) |
secgensbom_out/vulns-normalized.json |
Нормализованные уязвимости |
<sbom>(cert).json |
SBOM с полями GOST/ФСТЭК (команда cert) |
Если включено BDU-обогащение, в merged-bom-signed.json идентификатор БДУ записывается в vulnerabilities[].properties[] как свойство с именем ru.fstec.bdu:id.
| Путь | Сканер |
|---|---|
secgensbom_out/trivy/trivy-fs.json |
Trivy — файловая система |
secgensbom_out/trivy/sbom-vulns.json |
Trivy — анализ SBOM |
secgensbom_out/dependency-check/ |
OWASP Dependency-Check |
secgensbom_out/clair/ |
Clair (если включён) |
| Путь | Формат | Содержимое |
|---|---|---|
secgensbom_reports/excel/*.xlsx |
Excel | Лист 1: компоненты, Лист 2: уязвимости |
secgensbom_reports/docx/*.docx |
Word | Таблица компонентов + таблица уязвимостей |
secgensbom_reports/odt/*.odt |
ODT | То же самое |
| Колонка | Источник |
|---|---|
| № п/п | порядковый номер |
| Наименование компонента | components[].name из SBOM |
| Версия компонента | components[].version из SBOM |
| Тип пакета / тип компонента | тип экосистемы из PURL (pypi, maven, npm, apk, …) |
| PURL / технический идентификатор компонента | components[].purl из SBOM |
| Язык (языки) | определяется по PURL-типу (dependency.py) |
| Признак принадлежности к поверхности атаки | свойство компонента CycloneDX: attack-surface / attackSurface / isAttackSurface / GOST: attack_surface |
| Признак выполнения функций безопасности | свойство компонента CycloneDX: security-function / securityFunction / isSecurityFunction / GOST: security_function |
| Принадлежность к контейнерному образу | metadata.component.name (только если type = "container") |
| Роль компонента в составе контейнерного образа | свойство компонента: container-role / containerRole / cdx:docker:layer / layer |
| Адрес веб-ресурса | реестровый URL, вычисленный по PURL |
| Колонка | Источник |
|---|---|
| Компонент | имя пакета из сканера |
| Версия | версия при сканировании |
| CVE / ID | идентификатор уязвимости |
| CVSS | оценка CVSS v3 / v2 |
| Критичность | CRITICAL / HIGH / MEDIUM / LOW / UNKNOWN |
| Описание | первые 200 символов описания |
| Сканер | trivy / clair / dependency-check |
| Исправлено в версии | версия с исправлением (если известна) |
| Рекомендация / компенсирующая мера | Trivy: PrimaryURL → «Обновить до X»; Clair: Links[0]; Dependency-Check: notes → references[].url |
| Статус допустимости в рассматриваемой конфигурации | Trivy: поле Status (fixed, affected, will_not_fix, end_of_life, …) |
Если пайплайн запущен с --bdu или BDU=true, во всех форматах отчётов уязвимостей появляется отдельная колонка BDU / ID. Если BDU-обогащение выключено, эта колонка не выводится.
Подробные примеры итогового SBOM, vulns-normalized.json и экспортируемых таблиц вынесены в docs/RESULT_EXAMPLES.md.
Короткий пример структуры после полного запуска:
secgensbom_out/
├── app-bom-dedup-signed.json
├── merged-bom-signed.json
├── vulns-normalized.json
├── dependency-check/
├── trivy/
└── clair/
secgensbom_reports/
├── excel/merged-bom-signed.xlsx
├── docx/merged-bom-signed.docx
└── odt/merged-bom-signed.odt
Обогащение идентификаторами БДУ ФСТЭК выключено по умолчанию.
Включение через CLI:
secsbom run --bduВключение через переменную окружения:
export BDU=true
secsbom runПри включённом BDU пайплайн:
- запрашивает соответствия
CVE -> BDU IDчерезbdu.fstec.ru - добавляет BDU ID в итоговый CycloneDX SBOM как свойство уязвимости
- выводит BDU ID в экспортируемые отчёты
Пример фрагмента SBOM:
{
"id": "CVE-2023-1234",
"properties": [
{
"name": "ru.fstec.bdu:id",
"value": "BDU:2023-01813"
}
]
}Образ включает Python, Syft, Trivy, Docker CLI и Node.js/npx (cdxgen для non-Python проектов).
OWASP Dependency-Check запускается внутри утилиты через docker run (Docker-in-Docker), его Java-зависимость утяжелила бы основной образ на ~400 МБ.
Clair требует отдельного работающего HTTP-сервера — поэтому он вынесен в docker-compose.yml.
Безопасность образа: контейнер работает от непривилегированного пользователя sbom (uid 1001) — secsbom отказывается запускаться от root. В docker-compose.yml сервисам secgensbom / scan заданы security_opt: no-new-privileges и cap_drop: ALL. В образ встроен HEALTHCHECK, OCI-метки указывают лицензию Apache-2.0.
docker pull geminishkv/sbom-pipeline:latest
docker run --rm \
-v "$(pwd)/examples/project_inject:/app/project_inject" \
-v "$(pwd)/secgensbom_out:/app/secgensbom_out" \
-v "$(pwd)/secgensbom_reports:/app/secgensbom_reports" \
-v /var/run/docker.sock:/var/run/docker.sock \
geminishkv/sbom-pipeline:latestПолный стек с OWASP Dependency-Check и Clair v4 запускается через docker-compose.yml.
Поднимает Clair, собирает SBOM по исходному коду, сканирует уязвимости (Trivy + Dependency-Check + Clair) и контейнерный образ, обогащает БДУ ФСТЭК и выгружает отчёты — NVD-ключ не нужен:
SKIP_CLAIR=false BDU=true IMAGE_NAME=nginx:latest docker compose up --build- Код проекта берётся из
./examples/project_inject— замените содержимое этой папки на свой проект (или переопределите volumesecgensbomвdocker-compose.yml). IMAGE_NAME— контейнерный образ для сканирования через Clair (по умолчаниюpostgres:14).SKIP_CLAIR=falseвключает анализ контейнерного образа;BDU=trueдобавляет идентификаторы БДУ ФСТЭК.- Без
NVD_API_KEYбаза NVD для Dependency-Check скачается автоматически при первом запуске (~220 МБ, несколько минут; она кэшируется в./.dependency-check-dataи переиспользуется далее). Clair на первом запуске прогревает updater'ы до ~10 минут. - Результат:
./secgensbom_out/(SBOM-артефакты) +./secgensbom_reports/{excel,docx,odt}/(отчёты по компонентам и уязвимостям).
Минимальный запуск (только демо-проект, без сканирования образа):
docker compose up --buildПорядок запуска:
clair-db(Postgres) — база данных Clair, ожидает healthcheckclair— HTTP-сервер уязвимостей Clair v4, ожидает готовности БДsecgensbom— основной пайплайн, стартует после того, как Clair пройдёт healthcheck
Что происходит внутри secgensbom:
- Trivy запускается напрямую (встроен в образ)
- OWASP Dependency-Check запускается через
docker run owasp/dependency-check(Docker-in-Docker через/var/run/docker.sock) - Clair сканирует образ через
clairctl, подключаясь кhttp://clair:8080
Переменные окружения:
| Переменная | По умолчанию | Описание |
|---|---|---|
SOURCE |
local |
Источник проекта: local, github, gitlab |
PROJECT_DIR |
/app/project_inject |
Путь к проекту внутри контейнера |
SKIP_CLAIR |
false |
Отключить сканирование Clair (true/false) |
CLAIR_ENDPOINT |
http://clair:8080 |
Адрес Clair API |
IMAGE_NAME |
— | Docker-образ для сканирования Clair |
BDU |
false |
Включить обогащение идентификаторами БДУ ФСТЭК |
DEP_CHECK_DATA |
/app/.dependency-check-data |
Кэш NVD для Dependency-Check |
NVD_CACHE_DIR |
$DEP_CHECK_DATA/nvd-api-cache |
Кэш ответов NVD API для повторного CVSS lookup |
SBOM_COMPONENT_NETWORK |
false |
Разрешить сетевые уточнения языков/URL при экспорте компонентов |
Примечание: Если
IMAGE_NAMEне задан, шаг Clair пропускается автоматически.
NVD_CACHE_DIR хранит ответы NVD API по CVE и используется Clair fallback при заполнении пустого CVSS. Повторный запуск берёт оценку из уже сохранённого ответа и не делает новый HTTP-запрос.
По умолчанию экспорт компонентов не ходит в npm/NuGet/Debian/GitHub за уточнениями; для максимально быстрых и воспроизводимых отчётов язык и URL вычисляются из PURL.
Частые ошибки запуска, сетевых лимитов NVD, Docker/Clair и пустых отчётных колонок собраны в docs/TROUBLESHOOTING.md.
| Workflow | Триггер | Назначение |
|---|---|---|
ci.yml |
push / PR → main | lint + mypy + pytest (3.11–3.13) |
secgensbom.yml |
push → securitycheck, вручную |
запуск пайплайна, сохранение SBOM и отчётов |
publish.yml |
тег v*.*.* |
GitHub Packages + PyPI + Docker Hub + GitHub Release |
Публикация новой версии — один тег запускает всё:
git tag v2.2.0 -m "Release v2.2.0"
git push origin v2.2.0Файл secgensbom/secgensbom.yml — это переиспользуемый шаблон CI для GitLab. Любой другой проект в GitLab может подключить готовый SBOM-шаг одной строкой, не копируя конфигурацию:
flowchart TD
IN(["Источник\nlocal / github / gitlab / образ"]) --> STEP1
subgraph pipeline["secsbom run — этапы"]
subgraph STEP1["1 · Генерация SBOM"]
direction TB
CDXGEN["cdxgen\napp-bom-cdxgen.json"]
SYFT["syft\napp-bom-syft.json"]
GEN["generate.py — merge\napp-bom-merged.json"]
CLAIR_SCAN["clair.py — run_scan_report()\nclair-*.json\nполучение пакетов образа"]
CLAIR_ENRICH["clair.py — enrich_sbom_with_clair_packages()\nдобавление пакетов образа в SBOM\n(только добавление, без обновления)"]
CDXGEN --> GEN
SYFT --> GEN
GEN --> CLAIR_SCAN --> CLAIR_ENRICH
end
STEP1 --> DEDUP["2 · dedup.py\napp-bom-dedup.json\nдедуп компонентов по PURL\n(cdxgen + syft + Clair)"]
DEDUP --> SIGN1["3 · sign.py\napp-bom-dedup-signed.json + .sig\nSHA-256 — SBOM без уязвимостей"]
SIGN1 --> SCAN
subgraph SCAN["4 · scanner/ — параллельно (ThreadPoolExecutor)"]
direction LR
TRIVY_FS["trivy.py — scan_filesystem\ntrivy-fs.json (опц. --path)"]
TRIVY_SBOM["trivy.py — scan_sbom\nsbom-vulns.json"]
DEPCHECK["depcheck.py — docker run\ndependency-check-report.* (опц. --path)"]
CLAIR_VULNS["clair.py — parse_report_findings()\n+ NVD CVSS fallback (опц. --image)"]
end
TRIVY_FS & TRIVY_SBOM & DEPCHECK & CLAIR_VULNS --> CROSS["cross-populate CVSS\nмежду сканерами"]
CROSS --> DDUP2["5 · dedup.py\nдедуп уязвимостей\nпо CVE + компонент"]
DDUP2 --> MERGE["6 · vuln_merger.py\nvulnerabilities[] в SBOM\n+ БДУ ФСТЭК (опц. --bdu)\nvulns-normalized.json"]
MERGE --> SIGN2["7 · sign.py\nmerged-bom-signed.json + .sig\nSHA-256 — SBOM с уязвимостями"]
SIGN2 --> EXPORT["8 · exporter.py"]
end
CLAIR_SCAN -.->|"отчёт повторно используется\nна этапе 4"| CLAIR_VULNS
EXPORT --> XLSX["secgensbom_reports/\n*.xlsx"]
EXPORT --> DOCX["secgensbom_reports/\n*.docx"]
EXPORT --> ODT["secgensbom_reports/\n*.odt"]
sbom_genform/
├── src/sbom_pipeline/
│ ├── cli.py # secsbom / secsbom-pipeline (typer)
│ ├── pipeline.py # оркестратор
│ ├── generate.py # генерация SBOM
│ ├── dedup.py # дедупликация
│ ├── sign.py # SHA-256 подпись
│ ├── exporter.py # xlsx / docx / odt
│ ├── vuln_merger.py # встраивание уязвимостей
│ ├── config.py # конфигурация
│ ├── scanner/
│ │ ├── trivy.py
│ │ ├── depcheck.py
│ │ └── clair.py
│ └── enrichters/
│ ├── bdu.py # БДУ ФСТЭК (CVE → BDU ID)
│ └── nvd.py # NVD API (CVSS fallback)
├── docker/
│ └── Dockerfile.secgensbom
├── examples/project_inject/ # уязвимый PHP проект
├── secgensbom/secgensbom.yml # GitLab CI shared template
├── .github/workflows/
│ ├── ci.yml
│ ├── secgensbom.yml
│ └── publish.yml
├── tests/test_smoke.py
├── pyproject.toml
└── .env.example
Copyright (c) 2026 Elijah S Shmakov