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

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

ручные third_party каталоги

Почему это важно для SCA/OSA

В проектах без Conan/vcpkg зависимости часто живут в руками собранном каталоге — third_party/, extern/, libs/, vendor/include. Туда складывают заголовки и заранее собранные .a/.so/.lib, а сборка подхватывает их через -Ithird_party/include и -Lthird_party/lib. С точки зрения компилятора это работает идеально; с точки зрения SCA — это чёрный ящик. Нет манифеста, который перечислил бы, что лежит в каталоге, нет версий, нет соответствия системным пакетам. Каталог third_party/ отвечает на вопрос «как собрать», но не на вопрос «из чего собрано».

Отличие от vendoring в том, что здесь обычно лежат не исходники, а артефакты — заголовки плюс прекомпилированные библиотеки, часто непонятного происхождения (кто-то когда-то скачал libfoo-dev и положил в репозиторий). Это одновременно и слепая зона состава, и риск provenance: бинарь в third_party/lib/ мог быть собран кем угодно и из чего угодно.

Как это проявляется

Зависимость не объявлена нигде, кроме флагов компилятора. Восстанавливать состав приходится из самого каталога и из команд сборки:

# Что вообще лежит в каталоге зависимостей
find third_party/ -name '*.h' -o -name '*.a' -o -name '*.so*' | sort

# Как сборка подхватывает каталог — ключи -I/-L/-l в логе компиляции
grep -oE '\-I *[^ ]*third_party[^ ]*|\-L *[^ ]*third_party[^ ]*|\-l[a-z0-9_]+' build.log | sort -u

# Версия часто спрятана в заголовке — ищем макрос версии
grep -rhoE '#define [A-Z_]*VERSION[A-Z_]* +"?[0-9][0-9A-Za-z._-]*' third_party/include/

# Прекомпилированный .a без исходников: чьи символы внутри?
nm -C third_party/lib/libfoo.a 2>/dev/null | grep ' T ' | head

Без этих шагов libfoo.a попадёт в линковку, но не в SBOM: генератор фактов не знает ни имени пакета, ни версии, ни лицензии.

Слепая зона или приём

Слепая зона — разрыв между include path и идентичностью компонента. -Ithird_party/openssl/include доказывает, что заголовки OpenSSL доступны компилятору, но не доказывает ни версию, ни что слинкована именно эта сборка, ни что бинарь в lib/ не подменён. Прекомпилированные .a усугубляют: символы есть, а исходного релиза и лицензии нет.

Приём: include/link path как карта зависимостей Соберите все -I/-L/-l, указывающие в third_party/, из реального лога сборки — это исчерпывающий список того, что проект оттуда тянет. Дальше каждый пункт привязывайте к версии (макрос в заголовке) и к источнику (откуда взят бинарь). Каталог, который не отражён ни в одном -I/-L, — мёртвый груз, его можно исключить из анализа.

Что делать на проекте

  1. Снимите реальные -I/-L/-l из лога компиляции, а не из CMake-конфига: фактическая команда — источник правды (см. build logs как аудиторский артефакт).
  2. Для каждой библиотеки в каталоге извлеките версию из заголовочного макроса и зафиксируйте pkg:generic/<name>@<version>.
  3. Прекомпилированные .a/.so прогоните через nm -C/readelf -d и сверьте с системным пакетом через dpkg -S/rpm -qf, если такая библиотека есть в дистрибутиве (см. dpkg/apt metadata).
  4. Запишите хеш каждого прекомпилированного артефакта и путь occurrences — provenance прекомпила иначе не доказать (см. hash-based identification).
  5. Введите правило: новый элемент в third_party/ сопровождается README с именем, версией, лицензией и источником загрузки.
  6. Непривязанное — в unresolved libraries.

Связь