PAYATHON 2026

如何为多模块、跨平台 Flutter 应用生成完整的 SBOM?

数据阿明

建议采用“Dart 依赖清单 + 各平台原生依赖清单 + 发布产物核对”的工作流。可以继续使用 cdxgen 和 Gradle 集成,同时明确每份清单的覆盖范围。现有资料不足以保证某个工具只扫描一次项目目录,就能完整覆盖所有平台、传递依赖、指纹和许可证。

为什么扫描结果总是缺一部分?

你已经尝试了多种工具,发现 Gradle 集成的结果更好。Gradle 能获得它实际解析的依赖,但 Flutter 项目的依赖来源还可能包括 Dart 包、其他平台的原生库,以及随应用交付的 Flutter engine。

Flutter 插件也可能按平台拆分实现。识别出一个 Dart 插件包后,还需要检查它在各平台引入的原生组件。Flutter 的插件开发文档说明了这种平台实现结构。

还需要区分两种范围:

  • 源码或构建依赖清单:记录解析、编译项目所需的组件,可以包含开发依赖。
  • 发布产物清单:描述某次具体发布交付的组件,需要结合目标平台和实际构建结果确认。

依赖解析成功后,其代码也不一定全部进入最终产物;手动复制的原生库则可能没有出现在包管理器的依赖图中。

第一步:导出 Dart 依赖,建立核对基线

在 Flutter 应用目录执行:

mkdir -p sbom
flutter pub get
flutter pub deps --json > sbom/pub-deps-all.json
flutter pub deps --json --no-dev > sbom/pub-deps-runtime.json

pub deps 包含直接依赖和传递依赖,--json 输出结构化数据,--no-dev 排除开发依赖。Flutter 项目使用 flutter pub 调用这些子命令,见 Pub 命令说明和 pub deps 文档。

这两个 JSON 文件可用于核对,尚不是 CycloneDX 或 SPDX SBOM。--no-dev 排除了开发依赖,但不能据此认定其余组件全部存在于某个平台的发布包中。

应用应保留与发布构建一致的 pubspec.lock。如果 SDK 支持,可以在 CI 中使用:

flutter pub get --enforce-lockfile

该选项会在锁文件无法满足解析要求或 hosted package 内容哈希发生变化时使命令失败,有助于避免生成清单时悄悄改变依赖。具体行为见 pub get 文档。

多模块项目需要先确认解析边界:使用 Pub workspace 时核对所有成员;存在多个独立应用或解析根目录时,分别生成清单,并记录所属应用和模块。

第二步:生成 Dart 层 SBOM,并检查传递依赖

cdxgen 的项目类型支持表列出了 dart、flutter、pub,并注明对 Dart 传递依赖的支持依赖于 pubspec.lock。生成清单前应先完成依赖解析。

安装支持该类型的 cdxgen 后,可以在包含应用锁文件的目录中从以下命令开始:

cdxgen -t dart -o sbom/dart.cdx.json .

CI 中应固定 cdxgen 版本,并以对应版本文档为准。生成后至少核对两项:

  1. pub deps 中的传递依赖是否都能在 SBOM 中找到对应组件。
  2. SBOM 是否保留了需要的依赖关系,而不只是一个扁平的组件列表。

如果有缺失,先检查锁文件是否被读取、扫描目录是否正确,以及 Git、path、私有包等来源是否被识别。这些检查通常比直接更换扫描工具更容易定位原因。

第三步:按实际发布平台补齐原生依赖

以下是建议的采集范围,可按项目实际使用的依赖机制选择:

范围建议采集的依据需要留意的遗漏
Android对应发布 variant 的 Gradle 已解析依赖插件引入的原生库、手动附带的 AAR、JAR、.so
iOS、macOS实际使用的 CocoaPods 或 Swift Package Manager 解析结果本地库、预编译 framework、XCFramework
Windows、Linux 等桌面目标构建系统依赖记录与打包文件FFI 库、随包分发的动态库
WebDart 依赖与实际交付文件额外引入或复制的 JavaScript、Wasm 等资源

Android 可以沿用已取得较好结果的 Gradle 路径。CycloneDX Gradle Plugin 会从 Gradle 已解析的依赖图生成 SBOM,并支持多项目汇总,见其项目说明。选择配置时应对应实际发布 variant,插件参数则以安装版本的文档为准。

其他平台同样应从实际依赖解析结果入手。只读取声明文件容易遗漏传递依赖;只扫描最终二进制,又可能缺少包名、版本和依赖关系。建议保留两类证据,交叉核对。

第四步:分别补充许可证和组件指纹

许可证、组件标识和文件哈希各有用途,不宜用一个字段代替另一个字段:

  • 组件标识:记录包来源、名称、版本,适用时使用 purl;Git 依赖还应保留解析到的提交。
  • 哈希:注明计算对象是下载归档、原生库文件,还是最终发布包。不同对象的 SHA-256 不能互换。
  • 许可证:优先核对实际使用版本中的许可证文件和相关元数据,保留发现依据;缺失或有冲突时标记待确认。

.dart_tool/package_config.json 可以帮助定位解析后的 Dart 包目录,方便进一步采集许可证证据,见 pub get 文档。不要直接把整个全局 Pub 缓存当作应用依赖范围,否则可能混入其他项目下载过的包。

Flutter framework、engine 和第三方 Dart 包各有许可证来源。LicenseRegistry、showLicensePage 可辅助核对应用展示的许可信息,但它们不能单独提供完整 SBOM 所需的版本、依赖关系和哈希。相关机制见 Flutter FAQ。

第五步:在 CI 中按发布目标汇总并验收

建议为每个平台、架构及发布配置保存一份可追溯的 SBOM,关联源码提交、锁文件、工具版本和发布产物哈希。需要统一交付时,再生成汇总视图。

合并时要处理组件标识、bom-ref 和依赖引用,保留平台与模块归属;不能简单拼接 JSON 数组,也不能仅按包名去重。

验收时应检查实际覆盖情况:

  • 从 Pub 或原生依赖解析结果中抽取一个传递依赖,确认清单中的版本和来源一致。
  • 抽查一个 Flutter 插件,确认它在对应平台使用的原生组件得到记录。
  • 检查随包交付的原生库是否能关联到组件记录或明确的待补充项。
  • 对许可证或哈希缺失的组件保留缺口记录,不填入猜测值。

JSON 格式有效或通过 SBOM schema 校验,只能说明结构符合要求。要确认组件是否齐全、版本是否对应本次构建,仍需核对解析结果和发布产物。可以先用现有工具建立可追溯的流程,再针对具体遗漏补充采集步骤。

备注:内容仅供参考。