Пиков / КАПО
Как читать авторский курс и нормативные материалы

Это учебные материалы, а не заключение о соответствии конкретного продукта. Организационные модели курса отделяйте от терминов стандарта: ППК в опубликованном проекте — машиночитаемый перечень, а живой реестр его версий является практикой сопровождения. Термин OSA в разных материалах имеет разный охват. Статус и обязательность нормативного документа проверяются отдельно; проект КАПО опубликован как проект первой редакции.

П34. SBOM quality gate

Контекст практики Мусорный SBOM ломает всё, что идёт после него: сканер не сопоставит компонент без PURL/CPE, юрист не оценит лицензию без поля licenses, аудитор не докажет полноту без графа зависимостей. Quality gate ставится перед загрузкой SBOM в анализатор и считает измеримые показатели покрытия. Опора — обязательные поля компонента по BSI TR-03183-2 v2.1.0 (раздел 5.2.2). Контролируемый результат — gate, который валит CI, если покрытие ниже порога, ещё до того как SBOM попадёт в сервис аудита.

Цель

Получить скрипт + CI-шаг, который по CycloneDX-файлу считает: - PURL coverage — доля компонентов с непустым purl; - license coverage — доля компонентов с заполненным licenses (id / name / expression); - наличие dependency graph (секция dependencies непуста); - число компонентов без версии и без идентификатора (worklist).

Gate падает, если PURL coverage < 90% или license coverage < 80% (пороги настраиваются). Проверяемый результат: на «хорошем» SBOM gate зелёный, на намеренно урезанном — красный с числами.

Команда или действие

Скрипт sbom-quality.sh (нужен только jq):

#!/usr/bin/env bash
set -euo pipefail
SBOM="${1:-sbom.cdx.json}"
MIN_PURL=90
MIN_LIC=80

total=$(jq '.components | length' "$SBOM")
with_purl=$(jq '[.components[] | select(.purl and .purl != "")] | length' "$SBOM")
with_lic=$(jq '[.components[] | select((.licenses|length)>0)] | length' "$SBOM")
with_ver=$(jq '[.components[] | select(.version and .version != "")] | length' "$SBOM")
deps=$(jq '.dependencies | length' "$SBOM")

purl_pct=$(( with_purl * 100 / total ))
lic_pct=$(( with_lic * 100 / total ))

printf 'components=%s purl=%s versioned=%s deps_nodes=%s\n' \
  "$total" "$purl_pct" "$lic_pct" "$with_ver" "$deps"

# worklist: компоненты без PURL
jq -r '.components[] | select((.purl // "")=="") | "  no-purl: \(.name) \(.version // "?")"' "$SBOM"

fail=0
[ "$purl_pct" -lt "$MIN_PURL" ] && { echo "FAIL: purl coverage < ${MIN_PURL}%"; fail=1; }
[ "$lic_pct"  -lt "$MIN_LIC"  ] && { echo "FAIL: license coverage < ${MIN_LIC}%"; fail=1; }
[ "$deps" -eq 0 ] && { echo "FAIL: no dependency graph"; fail=1; }
exit $fail

CI-шаг (GitLab):

sbom_quality:
  stage: test
  image: alpine:3.20
  before_script: [ "apk add --no-cache jq bash" ]
  script: [ "bash sbom-quality.sh sbom.cdx.json" ]

Что вы должны увидеть

На качественном SBOM — строка метрик и exit 0:

components=147 purl=98% license=91% versioned=145 deps_nodes=147

На урезанном (часть компонентов без purl, типичный симптом C/C++ SBOM, где нет пакетного менеджера) — worklist и exit 1:

components=63 purl=71% license=44% versioned=58 deps_nodes=0
  no-purl: libfoo 2.3.1
  no-purl: vendored-zlib ?
FAIL: purl coverage < 90%
FAIL: license coverage < 80%
FAIL: no dependency graph

Признак готовности: gate отделяет «годен к загрузке в анализатор» от «сначала дозаполнить координаты». Числа purl%/license% совпадают с тем, что вы посчитаете вручную на маленьком SBOM из 5 компонентов.

Для C/C++ legacy низкий PURL coverage — это норма и сигнал Без пакетного менеджера у статически слинкованных и вендоренных библиотек PURL часто пуст. Не «подгоняйте» порог под такой SBOM — gate должен вывести worklist (что дораспознать через CPE/build evidence), а не молча пропустить. Это вход в П25. CPE alias worklist.

Если не получилось

Критерий зачёта

Показать преподавателю: (1) скрипт + CI-шаг; (2) зелёный прогон на качественном SBOM с метриками; (3) красный прогон на урезанном SBOM с worklist no-purl и exit 1; (4) совпадение посчитанных метрик с ручным подсчётом на маленьком примере. Устно — почему quality gate ставится до загрузки в анализатор и какие обязательные поля BSI TR-03183-2 (раздел 5.2.2) он проверяет.

Нормативная связка