基于项目工程构建SBOM(软件物料清单)的研究
最近公司启动了一个新医疗项目,最终需要在国外注册使用(如美国FDA,欧盟),因此项目全生命周期管理过程必须严格,为满足合规要求,其中必不可少的一步是生成SBOM(软件物料清单)。临时给安排一个任务,来研究SBOM的生成。涉及到四个SBOM生成工具,sbom-tool,DycloneDX,Sift,ORT。然后就开发语言来说,公司项目有三种模式,纯C#项目,C++和C#混合使用的项目,纯C++项目,这里就最近几天的研究心得做一个总结。
sbom-tool
SBOM Tool是我最先开始,毕竟本人主要从事C#开发,而SBOM Tool(SBOM Tool.exe)是微软开发的开源命令行工具,用于自动生成和验证符合SPDX 2.2/3.0标准的软件物料清单(SBOM),支持跨平台安装和多种使用场景。这里安装方法就不做介绍,网上资料搜下就是,包括后续几种工具安装都不做介绍。我这里是作为dotnet工具全局安装的。可以直接在windows系统的CMD命令下,定位到工程编译之后的输出目录,使用类似如下的关键的命令来生成SBOM。若是要持续的集成CD/CI,在Visual Studio开发工具下,也可以在项目工程上右键->属性->生成->项目的生成成功后的事件中植入此命令,来对每次的构建都重新生成新的sbom。当然也可以将命令作为一个.bat批处理文件,在项目生成成功后来执行此文件来处理SBOM的生成。
直接CMD中使用示例1:sbom-tool generate -b 项目工程编译之后的输出目录 -pn "MyApp" -pv "1.0.0" -ps 供应商名称 -m ./sbom-output
sbom-tool构建SBOM关键参数和示例
-b:构建产物的根目录,即代码编译之后的输出目录
-bc: 项目源码目录,生成时的一些元数据会通过源码获取到,非必须.
-pn: 包名称,建议显示指定
-pv:包版本号
-ps:包的供应商信息,指定包名称后不能为空
-m: 指定生成的SBOM的输出目录,-D强制删除已有目录。
-bl: 指定包含的文件,创建一个包含文件列表的文本文本,每个文件使用完整路径单独一行,使用此参数工具只会处理生成列表中明确指定的文件
在工程文件的生成后事件配置执行sbom生成命令代码,我这里代码是注释的状态:
<!-- 定义生成SBOM的目标 -->
<Target Name="GenerateSBOM" AfterTargets="Build">
<!--使用bl命令进行文件过滤,只处理指定的文件-->
<!--<Exec Command="sbom-tool generate -b $(OutputPath) -bl $(SbomIncludeFilesPath) -pn $(SbomPackageName) -pv $(SbomPackageVersion) -ps $(SbomCompanay) -m $(OutputPath)\sbom" />-->
<!--<Exec Command="sbom-tool generate -b $(OutputPath) -bc $(SbomSourceCodePath) -pn $(SbomPackageName) -pv $(SbomPackageVersion) -ps $(SbomCompanay) -m $(OutputPath)\sbom" />-->
<!--<Exec Command="D:\Release\sbom\Generate-SBOM.bat" />-->
</Target>
我这里首先是针对C#的项目使用sbom-tool工具进行SBOM生成,这个生成的是符合SPDX标准的SBOM,这个过程中确实很顺利,使用简单的命令运行立马出来结果,但当我需要排除一些文件或组件时,确发现sbom-tool无对应的命令来排除文件,当然,其提供了-bl命令参数来指定只对那些文件生成SBOM。然后针对深度依赖,即嵌套依赖问题识别不友好或者说识别不了,查阅下来说需要借助与其他手段或工具,针对C++项目也是如此。
CycloneDX
此时就开始了对第二个工具CycloneDX的研究,这个工具生成的是符合CycloneDX标准的SBOM,这里我也是将其作为dotnet工具进行全局安装的(安装命令:dotnet tool install --global CycloneDX),需要注意的是不要使用旧的项目,如.NET Framework的项目来使用此工具,我这边就是在NET Framework中使用了此工具进行生成,怎么走都没通,之前就是忽略了网上AI资料所提的,SycloneDX for .NET目前不支持早期的版本,.NET Framework,.NET Core的早期版本(如NET Core 2.1,NET Core 3.1,NET Core 5.0),后来拿公司的一个.NET 8项目来尝试使用此CycloneDX,立马就能走通了。但在C#项目下,CycloneDX 默认情况下它会分析项目的 NuGet 包依赖(包括直接和传递依赖),但不会自动包含项目引用(Project References)所生成的 DLL 文件,因为这些 DLL 被视为“内部组件”而非外部第三方依赖。作为解决办法,从 CycloneDX .NET 工具 v2.0+ 开始,官方支持通过命令行参数显式包含项目引用。使用 --include-project-references 参数,注意,此时就需要使用工程目录,而不能使用解决方案目录,即.csproj文件,而非.sln文件。在只有.dll文件,即随便拷贝一个外部无用的.DLL文件放在编译后的输出目录中,CycloneDX .NET不会自动识别其组成,即SBOM中没有此项。若需要的话可以将其作为一个NuGet私有源进行引入。
使用CycloneDX生成SBOM的参考命令:
命令示例:dotnet cyclonedx ..\CSharp_Sbom.sln --output-format json --exclude-dev --exclude-test-projects -sn YourCompany -sv 1.0.0 -o ..\SbomScript\CycloneDX_sbom -fn sbom.json

在C#项目中使用CycloneDX工具时,也是顺利的生成了需要的SBOM,但是当在C++项目上生成时,这里先说下,使用的C++项目时临时创建的,无标准的包管理器,也没有用CMake,Conan或vcpkg管理依赖。最终生成出来的sbom文件中确没有任何组件或者说没有任何依赖,也就是json文件中的dependencies元素里面没有包含任何东西。也就是无法直接使用这个工具来生成C++项目的SBOM,这就尴尬了。后来查资料大概意思是说:因为C++缺乏统一的依赖管理机制(如 Java 的 Maven 或 Node.js 的 npm , C#的NuGet),不能直接解析依赖信息。
Syft
然后就开始了我的第三个工具Syft的研究,Syft会自动扫描,检测构建产物中的二进制文件和动态链接库来识别第三方组件,自动识别提取生成依赖包,并输出SBOM。支持格式:CycloneDX、SPDX、Syft JSON 等,是由 Anchore 开发的开源 CLI 工具,是Go语言编写。
Syft生成SBOM参考命令: syft . -o cyclonedx-json=../../sbom/syft/syft.cdx.json -vv


-v 输出警告信息
-vv 输出调试信息
使用Syft确实能做到自动识别C++的项目生成SBOM,但也仅仅是有限的支持,即仅能识别已知特征的库,无法还原完整依赖树(尤其静态链接、动态加载的以及自定义编译的库),只因C++项目无包管理器或者说有些C++项目没有使用统一的一套包管理流程,纯 CMake/Makefile + 静态链接情况下,只有Syft可行。能从二进制中识别 OpenSSL、zlib、libpng、glibc 等常见库(即使静态链接),经过一番折腾,这里又看到了另外一个工具OSS Review Toolkit (ORT) ,但经过了解下来说这是一个全流程的合规审计工具,因为目前只需要一个SBOM快速生成工具,貌似学习使用曲线更高,一下子就吓到了我这种懒人,也就懒得研究了。
最后附上一个简单的总结对比说明:


浙公网安备 33010602011771号