Всем привет! На связи Аркадий Комиссарчук, я аналитик информационной безопасности команды продукта Appsec.Track, занимаюсь исследованиями в сфере безопасной разработки. Тема сегодняшней статьи затрагивает практики SCA & SBOM.

Software Bill of Materials (SBOM) становится всё более важным элементом обеспечения безопасности программного обеспечения. В одной из статей мы уже рассказали, что такое SBOM и как их можно применять. В этой статье мы рассмотрим инструменты для их генерации.

С появлением множества инструментов для генерации SBOM, встаёт вопрос — а какой из них выбрать? Мы сравнили популярные SBOM-генераторы по формату CycloneDX: cdxgen, Trivy, Syft и плагины CycloneDX для конкретных экосистем. Для тестирования использовались репозитории:

  • dvja, использующий Maven зависимости и js-библиотеки

  • WebGoat.NET -- WebGoat проект, реализованный на C# и содержащий NuGet зависимости, а также jquery. Еще в репозитории используются Github Action

В данном обзоре сравниваются результаты сканирования репозитория кода и собранного проекта. Сканирование Docker-образов не сильно отличается от сканирования собранных артефактов, поэтому его рассматривать не будем.

cdxgen

Генератор SBOM от проекта CycloneDX, определившего свой формат SBOM. Поддерживает Java, JavaScript (npm, yarn), Python и многие другие языки. Используется "под капотом" Dependency-Track для генерации SBOM.

Команда для сканирования: cdxgen <path> (вывод будет в файле bom.json)

В DJVA cdxgen определил основной компонент приложения исходя из pom.xml файла в DVJA и поместил его в секцию "metadata":

"component": {
            "group": "com.appsecco",
            "name": "dvja",
            "version": "1.0-SNAPSHOT",
            "properties": [
                {
                    "name": "SrcFile",
                    "value": "/mnt/c/Users/akomissarchuk/dummy-projects/java/dvja/pom.xml"
                }
            ],
            "purl": "pkg:maven/com.appsecco/dvja@1.0-SNAPSHOT?type=war",
            "bom-ref": "pkg:maven/com.appsecco/dvja@1.0-SNAPSHOT?type=war",
            "type": "application"
        },

То же самое в WebGoat.NET:

"component": {
            "type": "application",
            "properties": [
                {
                    "name": "cdx:dotnet:target_framework",
                    "value": "net8.0"
                }
            ],
            "name": "WebGoat.NET",
            "purl": "pkg:nuget/WebGoat.NET@latest",
            "bom-ref": "pkg:nuget/WebGoat.NET@latest"
        },

В этой же секции далее идет информация об обнаруженных типах файлов в проекте по экосистемам, неймспейсах найденных компонентов и файлах исходников:

"properties": [
            {
                "name": "cdx:bom:componentTypes",
                "value": "maven\\nnpm"
            },
            {
                "name": "cdx:bom:componentNamespaces",
                "value": "antlr\\naopalliance\\nasm\\ncglib\\ncom.google.code.gson\\ncom.jgeppert.struts2.bootstrap\\ncommons-beanutils\\ncommons-chain\\ncommons-codec\\ncommons-collections\\ncommons-digester\\ncommons-fileupload\\ncommons-io\\ncommons-lang\\ncommons-logging\\ncommons-validator\\ndom4j\\njavassist\\njavax.servlet\\njavax.transaction\\njunit\\nlog4j\\nmysql\\nognl\\norg.apache.commons\\norg.apache.logging.log4j\\norg.apache.struts\\norg.apache.struts.xwork\\norg.apache.velocity\\norg.freemarker\\norg.hibernate\\norg.slf4j\\norg.springframework\\noro\\nsslext\\nxml-apis"
            },
            {
                "name": "cdx:bom:componentSrcFiles",
                "value": "pom.xml\\nsrc/main/webapp/assets/jquery-3.2.1.min.js\\nsrc/main/webapp/assets/showdown.min.js"
            }
"properties": [
            {
                "name": "cdx:bom:componentTypes",
                "value": "github\\nnpm\\nnuget"
            },
            {
                "name": "cdx:bom:componentNamespaces",
                "value": "actions\\ndocker"
            },
            {
                "name": "cdx:bom:componentSrcFiles",
                "value": "WebGoat.NET/WebGoat.NET.csproj\\nWebGoat.NET/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js\\nWebGoat.NET/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js\\nWebGoat.NET/wwwroot/lib/jquery-validation/dist/additional-methods.min.js\\nWebGoat.NET/wwwroot/lib/jquery-validation/dist/jquery.validate.min.js\\nWebGoat.NET/wwwroot/lib/jquery/dist/jquery.min.js"
            }

Далее идёт перечисление обнаруженных компонентов в секции components. Для каждого обнаруженного компонента выводится доказательство его присутствия в проекте (evidence). Это могут быть файлы манифестов, исходные файлы самих компонентов и многое другое (см. спецификацию, раздел Evidence).

Пример — в DJVA используется showdown, но не в качестве npm-модуля, а просто в виде приложенного скрипта, и cdxgen смог определить это из имени найденного .min.js файла:

 {
            "group": "",
            "name": "showdown",
            "version": "02-06-2017",
            "purl": "pkg:npm/showdown@02-06-2017",
            "type": "library",
            "bom-ref": "pkg:npm/showdown@02-06-2017",
            "properties": [
                {
                    "name": "SrcFile",
                    "value": "src/main/webapp/assets/showdown.min.js"
                }
            ],
            "evidence": {
                "identity": [
                    {
                        "field": "purl",
                        "confidence": 0.25,
                        "methods": [
                            {
                                "technique": "filename",
                                "confidence": 0.25,
                                "value": "src/main/webapp/assets/showdown.min.js"
                            }
                        ],
                        "concludedValue": "src/main/webapp/assets/showdown.min.js"
                    }
                ]
            }
        },

В том числе evidence добавляется и для компонентов, обнаруженных при анализе файлов манифестов. Пример найденного Json.Net в .csproj файле WebGoat:

{
            "group": "",
            "name": "Json.Net",
            "version": "1.0.33",
            "purl": "pkg:nuget/Json.Net@1.0.33",
            "type": "library",
            "bom-ref": "pkg:nuget/Json.Net@1.0.33",
            "properties": [
                {
                    "name": "SrcFile",
                    "value": "WebGoat.NET/WebGoat.NET.csproj"
                }
            ],
            "evidence": {
                "identity": [
                    {
                        "field": "purl",
                        "confidence": 0.7,
                        "methods": [
                            {
                                "technique": "manifest-analysis",
                                "confidence": 0.7,
                                "value": "WebGoat.NET/WebGoat.NET.csproj"
                            }
                        ],
                        "concludedValue": "WebGoat.NET/WebGoat.NET.csproj"
                    }
                ]
            }
        },

Все компоненты описываются через название, группу (при наличии) и версию. Для каждого определяется его purl и тип (library, framework, application и т.д.)/

В SBOM компонентами могут являться не только библиотеки или программные модули, используемые в проекте, но и многие другие элементы. Подробнее — в спецификации.

alt
Типы компонентов в спецификации CycloneDX

Для maven-компонентов cdxgen также определил scope (область использования) и разделил их на необязательные ("scope": "optional"), которые, например, применяются для тестирования, и обязательные зависимости проекта ("scope": "required"). Пример — компоненты spring-test и spring-core:

{
            "group": "org.springframework",
            "name": "spring-test",
            "version": "3.0.5.RELEASE",
            "scope": "optional",
            "purl": "pkg:maven/org.springframework/spring-test@3.0.5.RELEASE?type=jar",
            "type": "framework",
            "bom-ref": "pkg:maven/org.springframework/spring-test@3.0.5.RELEASE?type=jar",
            "properties": [
                {
                    "name": "cdx:maven:component_scope",
                    "value": "test"
                },
                {
                    "name": "SrcFile",
                    "value": "pom.xml"
                }
            ],
            "evidence": {
                "identity": [
                    {
                        "field": "purl",
                        "confidence": 0.5,
                        "methods": [
                            {
                                "technique": "manifest-analysis",
                                "confidence": 0.5,
                                "value": "pom.xml"
                            }
                        ],
                        "concludedValue": "pom.xml"
                    }
                ]
            },
            "tags": [
                "framework"
            ]
        },
        {
            "group": "org.springframework",
            "name": "spring-core",
            "version": "3.0.5.RELEASE",
            "scope": "required",
            "purl": "pkg:maven/org.springframework/spring-core@3.0.5.RELEASE?type=jar",
            "type": "framework",
            "bom-ref": "pkg:maven/org.springframework/spring-core@3.0.5.RELEASE?type=jar",
            "properties": [
                {
                    "name": "cdx:maven:component_scope",
                    "value": "compile"
                },
                {
                    "name": "SrcFile",
                    "value": "pom.xml"
                }
            ],
            "evidence": {
                "identity": [
                    {
                        "field": "purl",
                        "confidence": 0.5,
                        "methods": [
                            {
                                "technique": "manifest-analysis",
                                "confidence": 0.5,
                                "value": "pom.xml"
                            }
                        ],
                        "concludedValue": "pom.xml"
                    }
                ]
            },
            "tags": [
                "framework"
            ]
        },

Кроме обычных программных компонентов в WebGoat cdxgen также определил github-action файлы, которые были в репозитории, и корректно написал для низ purl. Однако для одного (checkout) тип компонента почему-то был определен как application:

{
            "group": "actions",
            "name": "checkout",
            "version": "v2",
            "purl": "pkg:github/actions/checkout@v2",
            "type": "application",
            "bom-ref": "pkg:github/actions/checkout@v2"
        },
        {
            "group": "docker",
            "name": "setup-buildx-action",
            "version": "v2",
            "purl": "pkg:github/docker/setup-buildx-action@v2",
            "type": "library",
            "bom-ref": "pkg:github/docker/setup-buildx-action@v2"
        },
        {
            "group": "docker",
            "name": "login-action",
            "version": "v2",
            "purl": "pkg:github/docker/login-action@v2",
            "type": "library",
            "bom-ref": "pkg:github/docker/login-action@v2"
        },
        {
            "group": "docker",
            "name": "metadata-action",
            "version": "v4",
            "purl": "pkg:github/docker/metadata-action@v4",
            "type": "library",
            "bom-ref": "pkg:github/docker/metadata-action@v4"
        },
        {
            "group": "docker",
            "name": "build-push-action",
            "version": "v3",
            "purl": "pkg:github/docker/build-push-action@v3",
            "type": "library",
            "bom-ref": "pkg:github/docker/build-push-action@v3"
        }

Всего для DJVA cdxgen определил 65 компонентов, для WebGoat.NET — 21.

Наглядно визуализировать содержание CycloneDX SBOM можно с помощью инструмента Sunshine от самих CycloneDX:

В секции dependencies расписаны зависимости для каждого компонента, включая основной. Для одного компонента указываются только прямые зависимости, а граф транзитивных зависимостей можно получить, сопоставив все прямые зависимости каждого компонента.

"dependencies": [
        {
	        // Основной компонент и его прямые зависимости
            "ref": "pkg:maven/com.appsecco/dvja@1.0-SNAPSHOT?type=war",
            "dependsOn": [
                "pkg:maven/cglib/cglib@2.2?type=jar",
                "pkg:maven/com.google.code.gson/gson@2.8.1?type=jar",
                "pkg:maven/com.jgeppert.struts2.bootstrap/struts2-bootstrap-plugin@2.5.1?type=jar",
                "pkg:maven/commons-codec/commons-codec@1.10?type=jar",
                "pkg:maven/javax.servlet/jsp-api@2.0?type=jar",
                "pkg:maven/javax.servlet/servlet-api@2.4?type=jar",
                "pkg:maven/junit/junit@4.5?type=jar",
                "pkg:maven/mysql/mysql-connector-java@5.1.42?type=jar",
                "pkg:maven/org.apache.logging.log4j/log4j-api@2.3?type=jar",
                "pkg:maven/org.apache.logging.log4j/log4j-core@2.3?type=jar",
                "pkg:maven/org.apache.struts/struts2-config-browser-plugin@2.3.30?type=jar",
                "pkg:maven/org.apache.struts/struts2-core@2.3.30?type=jar",
                "pkg:maven/org.apache.struts/struts2-junit-plugin@2.3.30?type=jar",
                "pkg:maven/org.apache.struts/struts2-spring-plugin@2.3.30?type=jar",
                "pkg:maven/org.hibernate/hibernate-annotations@3.4.0.GA?type=jar",
                "pkg:maven/org.hibernate/hibernate-core@3.3.1.GA?type=jar",
                "pkg:maven/org.hibernate/hibernate-entitymanager@3.4.0.GA?type=jar",
                "pkg:maven/org.slf4j/slf4j-log4j12@1.5.2?type=jar",
                "pkg:maven/org.springframework/spring-orm@3.0.5.RELEASE?type=jar",
                "pkg:maven/org.springframework/spring-tx@3.0.5.RELEASE?type=jar"
            ]
        },

Стоит отметить, что cdxgen не повторяется при указывании зависимостей пакетов. То есть, если пакет A зависит от пакета B, и пакет C зависит от пакета B, то cdxgen указывает пакет B только в зависимостях пакета A. Пример — пакет pkg:maven/org.apache.struts/struts2-spring-plugin@2.3.30 зависит от пакетов:

{
            "ref": "pkg:maven/org.apache.struts/struts2-spring-plugin@2.3.30?type=jar",
            "dependsOn": [
                "pkg:maven/org.apache.commons/commons-lang3@3.2?type=jar",
                "pkg:maven/org.springframework/spring-beans@3.0.5.RELEASE?type=jar",
                "pkg:maven/org.springframework/spring-web@3.0.5.RELEASE?type=jar"
            ]
        },

И пакет pkg:maven/org.springframework/spring-web@3.0.5.RELEASE зависит от spring-beans, но в его зависимостях он не указывается, как и остальные его зависимости, которые уже были упомянуты выше по списку:

{
            "ref": "pkg:maven/org.springframework/spring-web@3.0.5.RELEASE?type=jar",
            "dependsOn": []
        },

С одной стороны cdxgen сокращает работу по разбору транзитивов проекта, убирая повторения компонентов, но с другой — он по сути предоставляет не очень точную информацию о зависимостях отдельных компонентов, из-за чего полный граф зависимостей по такому SBOM построить не получится. На это можно смотреть с разных сторон, но в любом случае это довольно важный момент, который следует учитывать при работе с зависимостями в SBOM от cdxgen.

Для WebGoat.NET ситуация с зависимостями при проверке кода похуже — записались только прямые зависимости, которые cdxgen смог определить из файла манифеста .csproj:

Транзитивные зависимости для C# cdxgen самостоятельно определить не смог, в отличие от Java (где cdxgen самостоятельно через maven их определяет), поэтому перед сканированием следует выполнить dotnet restore для генерации project.assets.json, содержащего зависимости проекта. После этого действия cdxgen определил аж 246 компонентов, включая все NuGet компоненты и jquery.

Итог: cdxgen хорошо подходит для multi-language проектов: он детектирует как Maven/NuGet, так и npm/JS зависимости, указывает типы компонентов и уровень уверенности в их наличии, строит корректный граф зависимостей и хорошо справляется с задачей инвентаризации проекта.

Trivy

Многофункциональный инструмент безопасности от Aqua Security. Помимо SBOM, умеет находить уязвимости, мисматчи лицензий и сканировать как директории, так и образы контейнеров.

Для сканирования локальной директории проекта или репозитория (в том числе удаленного) используются команды fs и repo соответственно.

Команда: trivy fs/repo --format cyclonedx --output sbom.json <path>

SBOM, сгенерированный, Trivy имеет много отличий от cdxgen. Во-первых, если в cdxgen название проекта, и, соответственно, главного компонента, было определено из главного файла манифеста, то в выводе Trivy в качестве основного компонента используется указанная директория проекта (в данном случае Trivy запускался прямо из папки проекта с путём .), а ссылка на него (bom-ref) — сгенерированный UID:

"component": {
      "bom-ref": "21212107-6743-4c0e-9346-884faa85ef9c",
      "type": "application",
      "name": ".",
      "properties": [
        {
          "name": "aquasecurity:trivy:SchemaVersion",
          "value": "2"
        }
      ]
    }

В случае сканирования удаленного репозитория в качестве названия приложения используется репозиторий:

"component": {
      "bom-ref": "4d4e4cd6-94b5-4ffc-a67d-54d8b2c3b985",
      "type": "application",
      "name": "https://github.com/appsecco/dvja",
      "properties": [
        {
          "name": "aquasecurity:trivy:SchemaVersion",
          "value": "2"
        }
      ]
    }

Несмотря на это, основной компонент все равно спокойно определяется из файла манифеста и помещается в список, причем дважды:

"components": [
	// первый раз - компонент типа application, созданный по pom.xml DVJA 
    {
      "bom-ref": "7716a724-a237-49e0-8c17-9138602b1ae0",
      "type": "application",
      "name": "pom.xml",
      "properties": [
        {
          "name": "aquasecurity:trivy:Class",
          "value": "lang-pkgs"
        },
        {
          "name": "aquasecurity:trivy:Type",
          "value": "pom"
        }
      ]
    },
    // второй раз - компонент с именем com.appsecco/dvja типа library
    {
      "bom-ref": "pkg:maven/com.appsecco/dvja@1.0-SNAPSHOT",
      "type": "library",
      "group": "com.appsecco",
      "name": "dvja",
      "version": "1.0-SNAPSHOT",
      "purl": "pkg:maven/com.appsecco/dvja@1.0-SNAPSHOT",
      "properties": [
        {
          "name": "aquasecurity:trivy:PkgID",
          "value": "com.appsecco:dvja:1.0-SNAPSHOT"
        },
        {
          "name": "aquasecurity:trivy:PkgType",
          "value": "pom"
        }
      ]
    },

В итоге мы имеем три разных объекта, обозначающих главный компонент проекта. Это может создавать определенную путаницу. Но, как будет указано ниже, зависимости отсчитываются от компонента типа library и граф зависимостей может быть построен от него.

В WebGoat.NET Trivy сразу не смог определить даже прямые NuGet зависимости из .csproj файла, не говоря уж о JS-библиотеках:

 "components": [],
  "dependencies": [
    {
      "ref": "c314ad0e-4705-49da-adcf-8b415e93b2f0",
      "dependsOn": []
    }
  ],

И даже после генерации project.assets.json все равно ничего не вышло. Trivy поддерживает только определенные виды файлов для C#, поэтому нужно сгенерировать packages.lock.json командой dotnet restore -p:RestorePackagesWithLockFile=True.

Обработав манифест pom.xml или packages.lock.json Trivy также генерирует список компонентов в разделе components. Из интересного, Trivy добавляет в properties информацию о лицензиях компонентов (сработало только для maven-зависимостей):

{
      "bom-ref": "pkg:maven/commons-collections/commons-collections@3.1",
      "type": "library",
      "group": "commons-collections",
      "name": "commons-collections",
      "version": "3.1",
      "purl": "pkg:maven/commons-collections/commons-collections@3.1",
      "properties": [
        {
          "name": "aquasecurity:trivy:PkgID",
          "value": "commons-collections:commons-collections:3.1"
        },
        {
          "name": "aquasecurity:trivy:PkgType",
          "value": "pom"
        }
      ]
    },
{
      "bom-ref": "pkg:nuget/System.Security.Cryptography.ProtectedData@6.0.0",
      "type": "library",
      "name": "System.Security.Cryptography.ProtectedData",
      "version": "6.0.0",
      "purl": "pkg:nuget/System.Security.Cryptography.ProtectedData@6.0.0",
      "properties": [
        {
          "name": "aquasecurity:trivy:PkgID",
          "value": "System.Security.Cryptography.ProtectedData/6.0.0"
        },
        {
          "name": "aquasecurity:trivy:PkgType",
          "value": "dotnet-core"
        }
      ]
    },

Для обоих проектов Trivy определил только Java и C# зависимости, не учитывая JS-библиотеки — минус очко для Trivy. Для DJVA в сумме получилось 58 компонентов, для WebGoat.NET — 237.

В разделе dependencies все в порядке. Trivy выписал для каждого компонента полностью все зависимости, и, как было сказано выше, в отличие от cdxgen Trivy не исключает повторения:

Подытожим: Trivy не определил JS-библиотеки, включенные в проект в виде файлов, не распознал файлы .csproj и project.assets.json, но выдал полную информацию о зависимостях обнаруженных компонентов.

Syft

Лёгкий и быстрый SBOM-генератор от Anchore. Работает с множеством форматов пакетов, включая контейнеры.

Команда: syft scan . -o cyclonedx-json=syft.json

После сканирования dvja Syft определил всего лишь 21 maven-компонент, взяв их напрямую из файла манифеста, так как для нормальной работы сканера требуется сборка проекта. В качестве основного компонента указана директория сканирования, как в Trivy:

"component": {
            "bom-ref": "af63bd4c8601b7f1",
            "type": "file",
            "name": "/mnt/c/Users/akomissarchuk/dummy-projects/java/dvja"
        }

В компоненты также добавлены поля с метаданными. Из интересного — присутствует поле CPE, который автоматически генерируется самим Syft:

"components": [
        {
            "bom-ref": "pkg:maven/cglib/cglib@2.2?package-id=75386f8689bcc8ab",
            "type": "library",
            "group": "cglib",
            "name": "cglib",
            "version": "2.2",
            "cpe": "cpe:2.3:a:cglib:cglib:2.2:*:*:*:*:*:*:*",
            "purl": "pkg:maven/cglib/cglib@2.2",
            "properties": [
                {
                    "name": "syft:package:foundBy",
                    "value": "java-pom-cataloger"
                },
                {
                    "name": "syft:package:language",
                    "value": "java"
                },
                {
                    "name": "syft:package:type",
                    "value": "java-archive"
                },
                {
                    "name": "syft:package:metadataType",
                    "value": "java-archive"
                },
                {
                    "name": "syft:location:0:path",
                    "value": "/pom.xml"
                },
                {
                    "name": "syft:metadata:-:artifactID",
                    "value": "cglib"
                },
                {
                    "name": "syft:metadata:-:groupID",
                    "value": "cglib"
                }
            ]
        },

Соответственно, в dependencies тоже небогато — только зависимости из файла манифеста:

https://i.imgur.com/TWeNRdZ.png

Собрав проект и просканировав заново, Syft уже смог определить компоненты по-нормальному — нашлось 65. Но ситуация с зависимостями лучше не стала, все новые компоненты были так же записаны в прямые зависимости к основному (причем основной продублировался):

В WebGoat.NET без локфайла Syft также не справился с определением NuGet компонентов, но зато нашел все Github Action модули:

После генерации lockfile Syft прекрасно его распарсил, определив 240 компонентов и все их зависимости, правда, не привязав их к главному компоненту.

Ни в одном проекте JS библиотеки Syft не обнаружил.

Итог: Syft не справился с графом зависимостей для Java-проекта и не определил JS библиотеки, но смог собрать нормальный SBOM по WebGoat.NET с предварительно сгенерированным lockfile.

CycloneDX Plugin for Maven/.NET

Для экосистем .NET и Maven существуют официальные плагины для генерации CycloneDX SBOM:

Для DJVA плагин обнаружил 59 компонентов, естественно, только maven. Для каждого компонента он добавляет хэши и лицензии:

{
      "type" : "library",
      "bom-ref" : "pkg:maven/org.apache.struts/struts2-core@2.3.30?type=jar",
      "publisher" : "Apache Software Foundation",
      "group" : "org.apache.struts",
      "name" : "struts2-core",
      "version" : "2.3.30",
      "description" : "Apache Struts 2",
      "scope" : "required",
      "hashes" : [
        {
          "alg" : "MD5",
          "content" : "e775580415dfa3a77e92f0ce6229268d"
        },
        {
          "alg" : "SHA-1",
          "content" : "0d2281c1a99f65b1ab19a5efd83a00a00995166d"
        },
        {
          "alg" : "SHA-256",
          "content" : "21161cc0b5056f34e0afca1feb42ebcc4bbcba0e2b075aa241c9be76b890fc65"
        },
        {
          "alg" : "SHA-512",
          "content" : "3a3c4083e8b0947f760f5f3d216450a62b7048247bc705939f255203ab721e343cd68d52233fb2240db3d8066435d922acf32a10c6cb28d132194c97d4025895"
        },
        {
          "alg" : "SHA-384",
          "content" : "ef2df3a2388461c384e842ee90eb4eabf562596aada15a784d7fc0c2ffc3b38915dd4cd1f2c896226501760b88e4ad64"
        },
        {
          "alg" : "SHA3-384",
          "content" : "655ee6e71047b5403957ab0c063b83a39294bae20cbe5cd72599d9c5371a03e05d6eabca5ae1a14a6d0e5eb08f00831c"
        },
        {
          "alg" : "SHA3-256",
          "content" : "46aaa149a8f9f0e2de483bb49c7932b9bd44d6416d3c572e46f89cc2d19a8c4f"
        },
        {
          "alg" : "SHA3-512",
          "content" : "1b39134ed5c8df0adffb610113b46c7b7fa51eeb1a9bad1159c16670f27931c772ca6e05576742c2dfd8f77a1e5940517adb194376811f2d7b97569fdb20c034"
        }
      ],
      "licenses" : [
        {
          "license" : {
            "id" : "Apache-2.0"
          }
        }

А также добавляет ссылки, основанные на данных о пакете из Maven Repository:

"externalReferences" : [
        {
          "type" : "website",
          "url" : "http://struts.apache.org/struts2-core/"
        },
        {
          "type" : "build-system",
          "url" : "https://builds.apache.org/hudson/view/S-Z/view/Struts"
        },
        {
          "type" : "distribution-intake",
          "url" : "https://repository.apache.org/service/local/staging/deploy/maven2"
        },
        {
          "type" : "issue-tracker",
          "url" : "https://issues.apache.org/jira/browse/WW"
        },
        {
          "type" : "mailing-list",
          "url" : "http://mail-archives.apache.org/mod_mbox/struts-user/"
        },
        {
          "type" : "vcs",
          "url" : "http://git.apache.org/struts.git/struts2-core"
        }
      ]

В остальном не отличается от результатов из cdxgen.

Для WebGoat.NET потребовалось указать файл .sln, после чего он сам провел restore пакетов. В результате всё также хорошо — хэши, лицензии, ссылки и метаинформация из NuGet репозитория (даже описания модулей подтянулись):

{
      "type": "library",
      "bom-ref": "pkg:nuget/Castle.Core@5.1.0",
      "authors": [
        {
          "name": "Castle Project Contributors"
        }
      ],
      "name": "Castle.Core",
      "version": "5.1.0",
      "description": "Castle Core, including DynamicProxy, Logging Abstractions and DictionaryAdapter",
      "scope": "excluded",
      "hashes": [
        {
          "alg": "SHA-512",
          "content": "8577F1ECE48138365EC39BB0236A6E542910CAC2547F88193F37196A01AFC2C7B7A0860BDBA7039E0BD1F72EC8240CD4561FBABDE1FBC91E6EF43F6DA3454E76"
        }
      ],
      "licenses": [
        {
          "license": {
            "id": "Apache-2.0"
          }
        }
      ],
      "copyright": "Copyright (c) 2004-2022 Castle Project - http://www.castleproject.org/",
      "purl": "pkg:nuget/Castle.Core@5.1.0",
      "externalReferences": [
        {
          "url": "http://www.castleproject.org/",
          "type": "website"
        },
        {
          "url": "https://github.com/castleproject/Core",
          "type": "vcs"
        }
      ]
    },

Как результат, нативные плагины для экосистем предоставляют самую полную информацию о пакетах и их зависимостях, но только в своей экосистеме.

Вывод

Нет однозначно лучшего SBOM-генератора, у всех есть свои особенности.

Резюме по каждому:

  • cdxgen показал себя лучше остальных, наполнил SBOM самой полной информацией о пакетах и смог определить JS-библиотеки, однако для .NET проекта потребовал сделать restore пакетов для вывода полной информации о зависимостях.

  • Trivy можно использовать для разнообразных источников — filesystem, repository, image и т.д. И результативный SBOM выглядит неплохо. Однако компонентов он определил меньше и столкнулся с проблемами при сканировании .NET проекта, а также не определил JS-библиотеки.

  • Syft без дополнительных действий обнаружил поразительно малое количество компонентов, особенно для Java, но зато информация о них была чуть более полной, чем в остальных.

  • Плагины для Maven и .NET дали самые верные и самые полные результаты, но, само собой, обнаружили только компоненты своих экосистем.

Возможно, самым верным решением было бы использовать language-specific SBOM-генераторы для проектов с одним языком и cdxgen для multi-language проектов.

Комментарии (1)


  1. kozlyuk
    09.06.2025 08:52

    Есть ли генераторы SBOM, которые способны работать с проектами на C++ (CMake), линкующими библиотеки статически? В этом случае точного списка зависимостей с версиями в коде нет, а бинарник тоже не ссылается на прилинкованные статически библиотеки.