Как читать авторский курс и нормативные материалы
Это учебные материалы, а не заключение о соответствии конкретного продукта. Организационные модели курса отделяйте от терминов стандарта: ППК в опубликованном проекте — машиночитаемый перечень, а живой реестр его версий является практикой сопровождения. Термин OSA в разных материалах имеет разный охват. Статус и обязательность нормативного документа проверяются отдельно; проект КАПО опубликован как проект первой редакции.
П09. VDR и VEX: различие факта и решения
Контекст Эта практика без сканера. Её задача — научиться отличать VDR-факт (машинная связь «компонент ↔ CVE», которую выдал инструмент) от VEX-решения (человеческий вывод о применимости с обоснованием, issuer/владельцем и сроком). На проектах эти два слоя постоянно сваливают в одну кучу: «сканер сказал High — патчим». VEX превращает поток находок в управляемые статусы; кто выпускает VEX и какие evidence нужны, разобрано в VEX в процессе РБПО.
Цель
Взять одну находку из VDR (полученного в П06. Grype поверх готового SBOM или П08. Dependency-Track как портфель SBOM) и оформить поверх неё корректное VEX-решение not_affected в формате CycloneDX — с обоснованием из разрешённого списка justification, автором/issuer и ссылкой на evidence.
Проверяемый результат: валидный VEX-фрагмент, где у находки заполнены analysis.state, analysis.justification, analysis.detail и analysis.response, а affects[].ref указывает на конкретный компонент. Автором решения должна быть ответственная за продукт сторона, а не база уязвимостей или сканер.
Команда или действие
Сначала вычленяем факт из VDR, затем дописываем поверх него решение. jq здесь — только просмотр, не сканер.
# 1. Достать из VDR один факт: id, severity, что затронуто
jq '.vulnerabilities[] | {id, severity: .ratings[0].severity, affects}' vdr.cdx.json
# 2. Оформить VEX-решение руками (это и есть содержание практики)
cat > vex.cdx.json <<'JSON'
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"metadata": {
"authors": [{ "name": "Product Security / Организация" }],
"component": { "type": "application", "name": "my-product", "version": "1.0.0" }
},
"vulnerabilities": [
{
"id": "CVE-2022-25883",
"source": { "name": "NVD", "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-25883" },
"analysis": {
"state": "not_affected",
"justification": "code_not_reachable",
"detail": "semver вызывается только на доверенном вводе в build-time, в рантайме недостижим; evidence: build log + call graph + issue SEC-123",
"response": ["will_not_fix"]
},
"affects": [{ "ref": "pkg:npm/semver@7.3.7" }]
}
]
}
JSON
# 3. Самопроверка структуры
jq '.metadata.authors[0].name as $issuer | .vulnerabilities[] | {issuer: $issuer, id, state: .analysis.state, just: .analysis.justification}' vex.cdx.json
Разрешённые значения, а не свободный текст
state∈ {resolved,exploitable,in_triage,false_positive,not_affected} (CycloneDX) либо {affected,not_affected,fixed,under_investigation} (модель OpenVEX/SSVC).justificationдляnot_affected— из списка:code_not_present,code_not_reachable,requires_configuration,requires_dependency,requires_environment,protected_by_compiler,protected_at_runtime,protected_at_perimeter,protected_by_mitigating_control. Свободный текст идёт только вdetail, не вjustification.
Что вы должны увидеть
Шаг 1 (факт из VDR) — без всякого решения:
{ "id": "CVE-2022-25883", "severity": "high", "affects": [{ "ref": "pkg:npm/semver@7.3.7" }] }
Шаг 3 (решение поверх факта) — появился слой analysis и issuer:
{ "issuer": "Product Security / Организация", "id": "CVE-2022-25883", "state": "not_affected", "just": "code_not_reachable" }
Ключевая разница, которую вы должны проговорить: в VDR нет поля analysis вообще — там только факты (id, ratings, affects). VEX добавляет именно analysis (state + justification + detail + response). Один и тот же факт допускает разные VEX в разных продуктах.
Один факт — три решения
CVE-2021-44228(Log4Shell) вlog4j-core 2.14.1: - сервис, где класс не на classpath в рантайме →not_affected,justification: code_not_reachable; - сервис на периметре, ещё не обновлён →affected/exploitable,response: ["update"], SLA по severity; - после обновления до 2.17.1 →fixed/resolved. Факт во всех трёх случаях один; различает их триаж.
Если не получилось
Типичные ошибки -
not_affectedбезjustification. Это не триаж, а отписка. CycloneDX и здравый смысл требуют обоснование из разрешённого списка; пустойjustification= находка не разобрана. - Свободный текст вjustification."мы посмотрели, не страшно"— невалидно. Свободное описание идёт вdetail;justification— только из перечня. -affects[].refне совпадает сbom-ref/PURL компонента. Решение «висит в воздухе»: формально VEX есть, но ни к какому компоненту не привязан. Сверьте ref с PURL из исходного SBOM. - Нет issuer или evidence. VEX без автора решения и доказательств превращается в ручное подавление finding. Укажите ответственную роль и evidence: SBOM, build log, reachability report, triage issue. - Просроченноеnot_affected. Обоснование приняли для версии 1.2, продукт уехал на 2.0. Решение надо пересмотреть — уnot_affectedтоже есть срок жизни.
Критерий зачёта
Показать преподавателю:
1. факт из VDR (шаг 1) и VEX-решение поверх него (шаг 3) — рядом, чтобы видна была разница слоёв;
2. валидный justification из разрешённого списка и осмысленный detail;
3. issuer решения и минимум одно evidence, подтверждающее применимость именно к вашему продукту;
4. устно: почему один и тот же VDR-факт может дать разные VEX в разных продуктах;
5. кто владелец этого решения и когда оно подлежит пересмотру (роль, не сканер).
Нормативная связка
VEX в процессе РБПО · М06. Поиск уязвимостей и управление VDR/VEX · CycloneDX ECMA-424 (VEX-расширение) · OpenVEX · CSAF · NIST SP 800-218 SSDF (RV.2 — оценка и реагирование) · Политика управления уязвимостями компонентов · Регламент ретеста после исправления
Смежные практики: П06. Grype поверх готового SBOM · П08. Dependency-Track как портфель SBOM · П35. VEX triage · П10. БДУ ФСТЭК как национальный источник