土法炼钢兴趣小组的算法知识备份

【开源许可与版权工程】SCA、SBOM 与软件成分分析:FOSSA、BlackDuck、Syft、OSS Review Toolkit

文章导航

分类入口
architectureopensource
标签入口
#sca#sbom#spdx#cyclonedx#syft#fossa#blackduck#ort#dependabot#compliance

目录

十年前,一家公司发布一个 Java 应用,合规团队会问:“你们用了什么第三方库?”开发会打开 pom.xml,数一数 direct dependencies,大概是二十几个,回一份 Excel 就算交差。

今天你再问同样的问题,答案是:我们也不知道

一个普通的 Spring Boot 微服务,直接依赖十几个,间接依赖(transitive dependency)往往超过 400 个,这些间接依赖跨越至少七八种开源许可证,每周有若干个新的 CVE 披露,每天都有版本更新。没有人能靠人力维护这张清单。

软件成分分析(Software Composition Analysis,SCA)软件物料清单(Software Bill of Materials,SBOM) 就是为了回答”你到底用了什么”这个问题而生的工程体系。它已经从 2015 年前后小众合规厂商的产品,演进成 2021 年美国总统行政令(Executive Order 14028)明文要求、2024 年欧盟《网络弹性法案》(Cyber Resilience Act,CRA)立法背书的基础设施。

如果你还没读过本系列前面的许可证知识,建议先看:

本文假设你已经理解 MIT、Apache-2.0、GPL、AGPL、SSPL 的基本差异,聚焦在”怎么用工具把这些许可证和漏洞信息自动化地管起来”。

SCA 流水线架构

一、为什么成分分析变成了强制课题

2014 年的 Heartbleed、2015 年的 Shellshock、2017 年的 Equifax 数据泄露(起因是未升级的 Apache Struts CVE-2017-5638)、2020 年 SolarWinds 供应链攻击、2021 年 Log4Shell(CVE-2021-44228)——这一连串事件的共同规律是:受害企业并不知道自己用了什么版本的什么组件

Log4Shell 爆发的那一周,安全团队第一件事不是打补丁,而是”先找一下我们哪里用了 Log4j”。很多公司花了两到三周才把自己家里的 Log4j 实例全部枚举清楚。原因是 Log4j 经常被另一个库间接引入,而那个库又被另一个库间接引入,在 fat-JAR、容器镜像、嵌入式 agent 中层层嵌套。

这件事直接催生了 2021 年 5 月拜登政府的行政令 EO 14028(Improving the Nation’s Cybersecurity),其中第 4(e) 条款明确要求联邦政府采购的软件必须提供 SBOM。美国商务部国家电信和信息管理局(NTIA)随后发布了《SBOM 最小元素》(The Minimum Elements For a Software Bill of Materials,2021 年 7 月),定义了 SBOM 必须包含的七项数据字段。

欧盟紧随其后。2024 年 10 月通过、2027 年底全面生效的《网络弹性法案》(CRA)对”带数字元素的产品”施加了强制安全义务,其中附件一明确要求制造商”识别并记录产品中包含的组件与漏洞”,行业普遍理解为必须提供 SBOM。

中国方面,等保 2.0(GB/T 22239-2019)在应用安全通用要求里含蓄地要求”对应用系统中使用的开发框架、第三方软件进行管理”;工信部 2022 年发布的《网络产品安全漏洞管理规定》要求厂商对自身及第三方组件漏洞承担披露义务;2023 年后陆续出现的关键信息基础设施(关基)采购指南明确提出”供方应随产品交付软件成分清单”。

换言之,SBOM 在国内外都已经从”加分项”变成”准入门槛”

除了合规压力,工程侧本身也有刚需。过去十年开源依赖图谱的爆炸式增长有几个数据可以参考:

在这种规模下,人工维护依赖清单早已失效。即使你愿意给每个直接依赖标注许可证,传递依赖的数量也会以指数级扩展。比较出名的统计是 Tidelift 2021 年报告:一个中等规模应用的开源组件中,超过 80% 是传递依赖,开发者主动选择的直接依赖往往只是冰山一角。

对版权律师而言,SBOM 是开源合规取证的唯一可行路径;对 CISO 而言,SBOM 是 0-day 响应速度的决定性资产;对 DevOps 团队而言,SBOM 是”升不升版本”的决策依据;对采购与审计而言,SBOM 是合同附件。一份 SBOM 同时服务这四类角色,这也是它能迅速制度化的根本原因。

二、SCA 的工作原理

SCA 这个词有时会被理解成”扫一下代码生成一份 Excel”。但工业级 SCA 系统通常有四个层次。

2.1 依赖发现层

这一层回答”项目里有哪些组件”。

最幼稚的做法是解析包管理器的锁文件——pom.xmlpackage-lock.jsonPipfile.lockgo.sumCargo.lockcomposer.lock 等。这种方式快、准,但只覆盖”你声明的依赖”,不覆盖:

工业级工具必须同时支持三条路径:

  1. 清单解析(manifest-based):读锁文件。
  2. 文件系统扫描(filesystem scan):遍历目录,对每个文件做指纹。
  3. 二进制分析(binary analysis):对已编译的 ELF、PE、Mach-O、WASM 做符号匹配、字符串匹配、机器学习特征匹配。

Black Duck 的 Knowledge Base、FOSSA 的 Revere 引擎、Syft 的 cataloger 插件都属于第 2、3 层。

2.2 许可证识别层

这一层回答”这个组件是什么许可证”。

SPDX license list(截至 2025 年收录约 660 个许可证 ID)是产业共识的词汇表。识别手段:

ScanCode 是这一层事实上的开源标杆,Black Duck、FOSSA、Mend 也都有自研的识别引擎。

一个经常被忽视的问题是许可证声明与源码不一致。举几类常见情形:

因此许可证识别层在企业环境里通常需要双轨:一条快速路径(读元数据)用于 CI 阻断,一条慢速路径(全文扫描)用于合规审阅。

2.3 漏洞关联层

这一层回答”这些组件有没有已知漏洞”。

主要数据源:

关联的关键是 PURL(Package URL,RFC 草案),形如 pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1。一个稳定的 PURL 能唯一标识”哪个生态的哪个包的哪个版本”,SBOM 工具与漏洞库都围绕 PURL 对齐。

与 PURL 并列的还有 CPE(Common Platform Enumeration),形如 cpe:2.3:a:apache:log4j:2.14.1:*:*:*:*:*:*:*。CPE 是 NVD 早期使用的标识符,痛点是:NVD 分析员需要人工给每个 CVE 分配 CPE;同一个包经常有几个相近但不同的 CPE 字符串,匹配规则容易遗漏;CPE 与 PURL 之间没有官方 1:1 映射。2024 年 NVD 分析延迟恶化后,纯 CPE 匹配路线的误报和漏报问题更加突出,PURL-first 已经成为新系统的默认选择。

除数据源本身的覆盖差异外,关联还要处理版本范围语义。同一个 CVE 可能写成 < 2.17.0>= 2.0, < 2.15.0affected: 2.14.x,不同生态(Maven 的区间语法、npm semver、Debian 的 dpkg --compare-versions)语义不一致。工业级 SCA 往往要为每种生态写独立的版本比较器。这也是为什么 Trivy/Grype 这类扫描器会维护一张各生态的 canonical 版本比较实现。

2.4 策略执行层

这一层回答”发现了风险怎么办”。常见策略规则:

实现手段包括在 CI 中阻断流水线、在 PR 上留评论、在 Dependency-Track 中发告警、在制品库(Artifactory、Nexus)中设拦截规则。OPA(Open Policy Agent)的 Rego 语言和 CycloneDX 的 “License Expression” 字段可以把策略写成声明式代码。

策略层最常犯的错误是一刀切。同一个许可证(如 LGPL-2.1)在不同产品形态下的触发条件完全不同:作为 SaaS 后端使用基本无传染义务,静态链接进嵌入式固件分发就必须开源修改或提供可替换机制。一个成熟的策略引擎需要把”产品形态(分发/SaaS/内部工具)“作为输入维度,否则只能退化为”禁所有 Copyleft”这种一刀切,既杀错又挡不住真风险。关于这个话题展开见 Copyleft 工程边界

三、SBOM:软件物料清单

SBOM 是 SCA 的输出契约。SCA 是过程,SBOM 是结果文档。目前主流有两套格式。

3.1 SPDX(Linux 基金会)

SPDX(Software Package Data Exchange)由 Linux 基金会于 2010 年发起,2021 年以 ISO/IEC 5962:2021 成为国际标准。它的定位是”许可证合规交换”,许可证表达能力最强。

SPDX 2.3(2022 年发布)支持 Tag-Value、RDF、JSON、YAML、Spreadsheet 多种序列化。核心概念:

SPDX 3.0(2024 年发布)做了较大结构重构,拆成 Core、Software、Security、Licensing、Build、AI、Dataset 等 profile,未来可以同时承载 SBOM、AI-BOM(AI 模型物料清单)、Dataset-BOM(数据集清单)。

一个极简的 SPDX 2.3 Tag-Value 片段示例:

SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: my-service-1.4.2
DocumentNamespace: https://example.com/spdx/my-service-1.4.2-abc123
Creator: Tool: syft-1.12.0
Created: 2026-04-01T09:00:00Z

PackageName: log4j-core
SPDXID: SPDXRef-Package-log4j-core-2.17.1
PackageVersion: 2.17.1
PackageDownloadLocation: https://repo.maven.apache.org/maven2/org/apache/logging/log4j/log4j-core/2.17.1/log4j-core-2.17.1.jar
PackageLicenseConcluded: Apache-2.0
PackageLicenseDeclared: Apache-2.0
PackageCopyrightText: Copyright (c) Apache Software Foundation
ExternalRef: PACKAGE-MANAGER purl pkg:maven/org.apache.logging.log4j/log4j-core@2.17.1

Relationship: SPDXRef-Package-my-service DEPENDS_ON SPDXRef-Package-log4j-core-2.17.1

LicenseConcludedLicenseDeclared 的区别很关键:Declared 是工具从 metadata 读到的声明,Concluded 是人工或扫描器综合各种证据得出的结论。两者不一致时,审阅员应以 Concluded 为准并留下理由。

3.2 CycloneDX(OWASP)

CycloneDX 由 OWASP 于 2017 年发起,定位更偏”应用安全”。它的 schema 更简洁,对漏洞、VEX、服务、依赖图谱支持较早。

截至 2024 年 CycloneDX 1.6 支持:

CycloneDX 原生 JSON、XML、Protobuf,社区工具链(cdxgen、Dependency-Track)围绕它搭建。

对应的 CycloneDX 1.5 片段:

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.5",
  "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
  "version": 1,
  "metadata": {
    "timestamp": "2026-04-01T09:00:00Z",
    "tools": [{"vendor": "anchore", "name": "syft", "version": "1.12.0"}],
    "component": {
      "type": "application",
      "bom-ref": "pkg:maven/com.example/my-service@1.4.2",
      "name": "my-service",
      "version": "1.4.2"
    }
  },
  "components": [
    {
      "type": "library",
      "bom-ref": "pkg:maven/org.apache.logging.log4j/log4j-core@2.17.1",
      "group": "org.apache.logging.log4j",
      "name": "log4j-core",
      "version": "2.17.1",
      "purl": "pkg:maven/org.apache.logging.log4j/log4j-core@2.17.1",
      "licenses": [{"license": {"id": "Apache-2.0"}}]
    }
  ],
  "dependencies": [
    {
      "ref": "pkg:maven/com.example/my-service@1.4.2",
      "dependsOn": ["pkg:maven/org.apache.logging.log4j/log4j-core@2.17.1"]
    }
  ]
}

CycloneDX 的 bom-ref 是 BOM 内部的引用锚点,通常直接复用 PURL,这让图谱构建变得极简。

3.3 两格式对比

维度 SPDX CycloneDX
发起方 Linux 基金会 OWASP
国际标准 ISO/IEC 5962:2021 ECMA-424(2024)
主要定位 许可证合规交换 应用安全与漏洞
许可证表达 最完整(SPDX Expression 是业界事实标准) 引用 SPDX ID
漏洞/VEX 3.0 新增 Security profile 原生一等公民
序列化 Tag-Value、JSON、YAML、RDF、XLSX JSON、XML、Protobuf
文件粒度 支持 File、Snippet 默认 Component 级
工具生态 FOSSology、ORT、tern、spdx-tools cdxgen、Dependency-Track、CycloneDX CLI
与 PURL 2.3 起支持 1.0 起原生
AI/数据 3.0 Profile 1.5+ ML-BOM

实践建议:

值得单独说明的是 VEX(Vulnerability Exploitability eXchange)。VEX 不是 SBOM 的替代品,而是补丁——当 SBOM 告诉你”我用了 Log4j 2.14.0”,VEX 回答”这个 Log4j 在我的产品里的 JNDI 查找功能被禁用,CVE-2021-44228 不可利用”。CycloneDX 1.4 起把 VEX 纳入一等公民,CISA 也发布了独立的 VEX 规范(Common Security Advisory Framework / CSAF VEX)。在给下游客户交付 SBOM 时,SBOM + VEX 成对交付才能避免客户被一堆”其实不受影响的 CVE”淹没。

四、商业工具详解

商业 SCA 市场格局在 2020 年后发生了明显分化:一部分以开发者体验为卖点(Snyk、FOSSA),一部分仍固守”法律合规 + 审计报告”的老护城河(Black Duck、Sonatype),一部分挤在中间(Mend、Veracode SCA、Checkmarx SCA)。Gartner 的 AST(Application Security Testing)魔力象限里 SCA 近年基本稳定在这几家。下面逐一拆解。

4.1 Snyk

以色列起家,2015 年成立,2022 年估值一度达 76 亿美元(后有回调)。产品线覆盖 SCA(Snyk Open Source)、SAST(Snyk Code)、容器(Snyk Container)、IaC(Snyk IaC)。

特点:

4.2 FOSSA

2015 年旧金山成立。相对 Snyk 更偏合规。

特点:

4.3 Black Duck(Synopsys)

Black Duck Software 成立于 2002 年,是整个 SCA 行业的奠基者,2017 年被 Synopsys 以 5.65 亿美元收购。2024 年 Synopsys 将软件完整性业务(含 Black Duck、Coverity、Seeker)分拆成独立公司,仍沿用 Black Duck 品牌。

特点:

4.4 Mend(原 WhiteSource)

以色列 WhiteSource 于 2011 年成立,2022 年改名 Mend.io。

特点:

4.5 Sonatype Nexus IQ

Sonatype 是 Maven Central 的运营方,2008 年成立,做 Nexus Repository(制品库)起家,Nexus IQ 是其 SCA 产品。

特点:

一个用于快速横向对比的表格:

工具 定位 漏洞数据源 许可证识别 二进制分析 典型年费(中型团队)
Snyk 开发者优先 自研 Intel + NVD + GHSA 5–20 万美元
FOSSA 合规优先 自研 + NVD 有限 3–15 万美元
Black Duck 合规 + 审计 BDSA(自研) 10–50 万美元
Mend 修复自动化 自研 + NVD 有限 5–20 万美元
Sonatype IQ 制品库阻断 Sonatype Data Research 有限 10–40 万美元

上述数字基于公开招投标与行业报告估算,不同规模、模块、席位差异极大,以实际报价为准。

五、开源工具详解

5.1 Syft + Grype(Anchore)

Anchore 公司 2020 年开源 Syft(SBOM 生成)与 Grype(漏洞扫描),是目前最活跃的开源 SBOM 工具链。

Syft 的能力:

一个典型工作流:

# 安装(Linux / macOS)
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin

# 对容器镜像生成 SBOM(CycloneDX JSON)
syft docker.io/library/nginx:1.25.3 -o cyclonedx-json > nginx-1.25.3.cdx.json

# 对本地目录生成 SPDX
syft dir:. -o spdx-json > my-app.spdx.json

# 对二进制文件做成分分析
syft /usr/local/bin/kubectl -o table

# 指定 cataloger(例如只看 Java 与 OS 包)
syft my-app.jar --catalogers java-cataloger,rpm-db-cataloger

Grype 消费 Syft 输出:

# 直接扫描
grype docker.io/library/nginx:1.25.3

# 从 Syft SBOM 读入,离线扫描(SBOM-first 工作流)
syft -o json docker.io/library/nginx:1.25.3 | grype

# 仅报告 high/critical 且阻断 CI
grype sbom:nginx-1.25.3.cdx.json --fail-on high --only-fixed

Grype 使用 Anchore 维护的漏洞数据库(聚合 NVD、GHSA、Alpine secdb、Debian OVAL、Red Hat OVAL、Amazon ALAS、Ubuntu USN、Wolfi、Chainguard 等)。

Syft 默认不下载源码,它只读包管理器元数据和文件指纹,因此速度极快——扫一个几百 MB 的容器镜像通常 10 秒内完成。这也是它与 ORT/ScanCode 的根本定位差异:Syft 回答”里面有什么”,ORT/ScanCode 回答”每一行代码是什么许可证”。二者配合而不是互相替代。

另一个实践要点是 Syft 的 --source-name--source-version 参数:默认情况下它会用扫描目标的目录名或镜像名,若想让输出的 SBOM 在 Dependency-Track 里稳定对应同一个项目,最好显式传递。例如:

syft dir:. \
  --source-name my-service \
  --source-version $(git describe --tags --always) \
  -o cyclonedx-json > sbom.cdx.json

5.2 OSS Review Toolkit(ORT)

ORT 由 HERE Technologies 发起,2017 年开源,现已捐给 OpenChain。它的定位是企业级合规工作流,而不是单点扫描工具。ORT 由若干 subcommand 组成:

典型命令:

# 1. 分析依赖(产出 analyzer-result.yml)
ort analyze -i ./my-project -o ./ort-output

# 2. 下载源码
ort download -i ./ort-output/analyzer-result.yml -o ./ort-output/downloads

# 3. 扫描许可证
ort scan -i ./ort-output/analyzer-result.yml -o ./ort-output

# 4. 查漏洞
ort advise -i ./ort-output/scan-result.yml -o ./ort-output \
  --advisors OSV,VulnerableCode

# 5. 执行策略
ort evaluate -i ./ort-output/scan-result.yml -o ./ort-output \
  --rules-resource /rules/osadl-matrix.rules.kts

# 6. 生成报告
ort report -i ./ort-output/evaluation-result.yml -o ./ort-output/reports \
  --report-formats SpdxDocument,CycloneDx,WebApp,NoticeTemplate

ORT 的”策略 + 报告模板”设计非常适合大型企业,西门子、博世、华为、宝马均有公开案例使用 ORT。

ORT 的另一特色是 curations(人工修订)。开源生态里许可证元数据错漏很多——某个包声明是 MIT,实际源码里带了 GPL 代码;某个包根本没写 LICENSE 字段。面对这种情况,ORT 允许在 curations.yml 里写”对这个 PURL,concluded license 为 xxx,理由为 yyy”,把人工判断沉淀下来,下次扫描自动复用。这是企业级合规流程中”一次审阅,永久生效”的关键机制。ORT 官方还维护了一个公共的 ort-config 仓库,沉淀了社区共识的 curations,可以直接引用。

5.3 ScanCode Toolkit

nexB 公司发起,2015 年开源,是许可证识别事实上的开源标杆。ORT、FOSSology 都会在内部调用 ScanCode。

pip install scancode-toolkit

scancode --license --copyright --package --info --json-pp result.json ./my-project

ScanCode 的许可证规则库(scancode-licensedb)手工维护了几千条正则与文本模板,准确率在开源工具里第一。ScanCode.io(配套的 SaaS/自托管 Web 界面)让审阅者可以在 UI 里对扫描结果做 triage。

5.4 FOSSology

Linux 基金会项目,2008 年由惠普开源,定位是人工审阅平台

FOSSology 在中国电信、中兴、华为内部有多年使用记录。它更像 Bugzilla 的 OSS 合规版,不是 CI 工具。

5.5 Dependency-Track

OWASP 旗舰项目,2013 年起由 Steve Springett 维护。定位是SBOM 聚合与持续监控平台

工作模型:

  1. CI 在每次构建后把 CycloneDX SBOM POST 到 Dependency-Track。
  2. Dependency-Track 解析 SBOM,把组件对应到内置的 PURL/CPE 数据库。
  3. 订阅 NVD、GHSA、OSV、Snyk、Sonatype OSS Index、VulnDB,周期性地刷新漏洞数据。
  4. 当新 CVE 影响某个历史 SBOM,触发 webhook/邮件/Jira 告警。
  5. 允许给组件打 VEX 注解:“Log4j 在我产品里不可利用,因为 JNDI lookup 已禁用”。

它解决的核心痛点是时间维度的成分分析:扫一次只能告诉你此刻风险,Dependency-Track 告诉你”一周前上线那个版本是否受今天新披露的 CVE 影响”。

部署一般是 Docker Compose 起三个容器(apiserver、frontend、postgres),数分钟即可。

Dependency-Track 的几个高频用法:

5.6 cdxgen

CycloneDX 官方的 SBOM 生成器,印度工程师 Prabhu Subramanian 主力维护,由 OWASP Foundation 托管。

相比 Syft,cdxgen 的差异:

npm install -g @cyclonedx/cdxgen

# 自动探测项目类型
cdxgen -o bom.json .

# 指定类型(多项目共存时)
cdxgen -t java -o java-bom.json ./backend
cdxgen -t js   -o js-bom.json   ./frontend

# 容器镜像
cdxgen -t docker -o image.bom.json myorg/myapp:1.0

# 深度模式(解析 vendored / 二进制)
cdxgen --deep -t go -o go-deep.bom.json .

六、CI 集成

SCA 落到 CI 上,本质要回答三个问题:什么时候扫扫出来怎么办产物放在哪。常见的模式有四类:

  1. PR 时扫:每次 Pull Request 触发扫描,发现新增的高危依赖时给 PR 评论、阻断合并。优点是”谁引入,谁修复”,责任归属清晰;缺点是扫描耗时会拖慢 PR 反馈,对于大型 monorepo 尤其明显。
  2. 主干/Release 时扫:合入主干或打 tag 时扫描,作为质量门禁之一。适合对 PR 流速敏感的高频项目,但”发现问题再回退”的修复成本比 PR 时发现高。
  3. 制品库入库时扫:在 Nexus/Artifactory 这类制品库代理公共源时扫描,把风险挡在外网与内网的边界。适合有统一制品库的中大型企业,本质是”供应源头治理”。
  4. 持续监控:SBOM 入库后,依赖监控平台(Dependency-Track)在新 CVE 发布时回溯所有历史 SBOM,发现受影响产品就触发响应。这是时间维度的保护,与前三类是互补而非替代关系。

成熟的 SCA 体系通常是前扫后监控的组合:PR 时做增量扫描(快)、主干 release 时做全量扫描(慢但完整)、Dependency-Track 做持续监控(时间覆盖)。

6.1 GitHub Dependency Graph 与 Dependabot

GitHub 内置两件事:

对于企业 / 军工敏感代码不能上 GitHub SaaS 的场景,GitHub Enterprise Server 的 “Advanced Security” 许可里也提供 Dependabot、Secret Scanning、Code Scanning 三件套。

Dependabot 典型配置示例:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "gomod"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 5
    groups:
      minor-and-patch:
        update-types: ["minor", "patch"]
    ignore:
      - dependency-name: "github.com/internal/legacy-sdk"

  - package-ecosystem: "npm"
    directory: "/web"
    schedule:
      interval: "daily"
    versioning-strategy: increase-if-necessary

  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "monthly"

groups 是 2023 年新增的能力,能把一堆 patch 升级打包成一个 PR,显著减少 review 疲劳。对于更激进的全自动化,可以考虑 Renovate(Mend 开源维护),它支持 monorepo 按 package 级别分组、交叉关联 CVE、在 PR 里嵌入 release note 摘要。

6.2 Gitee 依赖扫描

Gitee(开源中国旗下)企业版在 2023 年后上线了”依赖扫描”,数据源包括 CNVD、NVD,对 Maven、npm、pip、Go 模块做依赖分析;“许可证识别”模块使用 ScanCode 规则库的本地化版本。Gitee 也接入了 OpenAtom 开源基金会的合规检查模板。

此外 GitCode、CodeArts(华为云)也都在 2024 年陆续推出了自研 SCA 能力,基本都是 ScanCode + 国产漏洞库的组合。

腾讯工蜂(git.code.tencent.com)、阿里云云效(codeup)也提供类似的内置 SCA 能力。对于主要在国内 DevOps 平台上开发的团队,优先用平台内置功能,省去自搭一套 Syft + Dependency-Track 的运维开销。代价是:这些平台的 SBOM 导出能力通常不完整,很难满足对外交付的 NTIA 最小元素要求,仍需在发版环节额外生成一份独立 SBOM。

6.3 在 GitHub Actions 里集成 Syft

以下是一个典型的”每次 push 生成 SBOM、附件到 Release、并上传到 Dependency-Track”的工作流:

# .github/workflows/sbom.yml
name: Generate SBOM

on:
  push:
    branches: [main]
    tags: ['v*']
  pull_request:
  workflow_dispatch:

permissions:
  contents: write  # 用于上传到 Release

jobs:
  sbom:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Syft
        uses: anchore/sbom-action/download-syft@v0

      - name: Generate CycloneDX SBOM
        run: |
          syft dir:. \
            -o cyclonedx-json=sbom.cdx.json \
            -o spdx-json=sbom.spdx.json

      - name: Grype scan (fail on high)
        uses: anchore/scan-action@v3
        with:
          sbom: sbom.cdx.json
          fail-build: true
          severity-cutoff: high

      - name: Upload SBOM artifacts
        uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: |
            sbom.cdx.json
            sbom.spdx.json

      - name: Upload to Dependency-Track
        if: github.ref == 'refs/heads/main'
        env:
          DT_URL: ${{ secrets.DEPENDENCY_TRACK_URL }}
          DT_API_KEY: ${{ secrets.DEPENDENCY_TRACK_API_KEY }}
          PROJECT_UUID: ${{ secrets.DT_PROJECT_UUID }}
        run: |
          curl -sS -X POST "$DT_URL/api/v1/bom" \
            -H "X-Api-Key: $DT_API_KEY" \
            -H "Content-Type: multipart/form-data" \
            -F "project=$PROJECT_UUID" \
            -F "bom=@sbom.cdx.json"

      - name: Attach SBOM to Release
        if: startsWith(github.ref, 'refs/tags/')
        uses: softprops/action-gh-release@v2
        with:
          files: |
            sbom.cdx.json
            sbom.spdx.json

几个要点:

对容器镜像,把 syft dir:. 替换为 syft <image>:<tag> 即可。配合 cosign attach sbomcosign attest --predicate sbom.cdx.json 可以把 SBOM 签名后附到 OCI Registry,形成可验证的软件供应链证明(in-toto attestation)。

把 SBOM 附到镜像的完整命令组合:

# 1. 构建镜像
docker buildx build -t registry.example.com/app:1.4.2 --push .

# 2. 生成 SBOM
syft registry.example.com/app:1.4.2 -o cyclonedx-json > sbom.cdx.json

# 3. 签名镜像
cosign sign registry.example.com/app:1.4.2 --key cosign.key

# 4. 把 SBOM 作为 attestation 附到镜像
cosign attest \
  --predicate sbom.cdx.json \
  --type cyclonedx \
  --key cosign.key \
  registry.example.com/app:1.4.2

# 5. 下游验证
cosign verify-attestation \
  --key cosign.pub \
  --type cyclonedx \
  registry.example.com/app:1.4.2 \
  | jq -r '.payload' | base64 -d | jq '.predicate'

这种做法让 SBOM 永远跟随镜像走,而不是散落在 Release 资产或某个邮件附件里。符合 SLSA(Supply-chain Levels for Software Artifacts)level 3 的供应链成熟度模型。

七、合规要求:EO 14028、CRA、等保 2.0

7.1 美国 EO 14028

2021 年 5 月 12 日签署,行政令序号 14028。第 4 节专门讲”增强软件供应链安全”。相关落地文件:

对中国企业出海美国的影响:向美国联邦、州政府、军方供货必须提供 SBOM;商业 SaaS 的大型美国客户(金融、医疗)也逐步把 SBOM 写进供应商合同。

与 EO 14028 配套的还有几个具体法规节点:

7.2 欧盟《网络弹性法案》(CRA)

2022 年 9 月草案、2024 年 10 月正式通过(Regulation (EU) 2024/2847),自公布后 36 个月全面适用(2027 年底),报告义务于 21 个月后生效(2026 年 9 月)。

核心义务:

业内普遍解读:CRA 附件一第 2(1) 条的”识别 & 记录”等同于要求 SBOM。ENISA 也公开表态支持 SPDX/CycloneDX。

CRA 对开源项目作者的处理是一个专门博弈点:商业性质的开源(open-source software stewards)承担有限义务,纯个人业余开源不在范围内。对中国开源维护者的实际影响较小,但对”把开源 fork 后商业化销往欧盟”的公司,必须把 SBOM、漏洞响应、CE 合格声明全部走完。

CRA 另一条容易被忽视的高压线是主动利用披露 24 小时义务——在企业内部这意味着安全响应团队必须有能够 24 小时在岗的流程与人员,并且能够快速从 SBOM 反向定位到受影响产品版本、客户清单,才能在 24 小时内完成通知。SBOM 没有做持久化、没有版本化的公司,这条义务几乎不可能满足。

EU CRA 的落地时间线值得记忆:

时间 里程碑
2024-10-23 法案正式通过
2024-12-11 在 Official Journal 发布
2025-06-11 第 14 条报告义务指南出台窗口(委员会实施法案)
2026-09-11 漏洞报告义务生效
2027-12-11 全面适用(所有主要义务)

国内厂商对欧盟出口前应预留至少 12–18 个月准备期来搭 SBOM 能力与漏洞响应流程。

7.3 中国等保 2.0 与 SBOM

GB/T 22239-2019(信息安全技术 网络安全等级保护基本要求,俗称等保 2.0)本身没有”SBOM”字样,但相关条款已经等价要求:

此外:

实务操作上,国内企业的 SBOM 字段通常沿用 SPDX/CycloneDX,但增加一列”国产替代建议”或”国密合规状态”以满足本地化要求。

另外值得留意的三份国内标准/草案:

从合规触发点上说,一家公司若同时满足以下条件中的两条及以上,就值得把 SBOM 列为刚性工程目标:

八、工程落地路径

8.1 第一步:把 Syft 跑起来

落地的第一天不要去谈”全公司统一 SCA 平台”,先让 Syft 在 一个项目 的 CI 里跑起来,输出 CycloneDX JSON,上传为 Artifact。这一步成本极低(半天工作量),价值极高:从此团队至少能回答”我用了什么”。

注意事项:

8.2 第二步:接入漏洞数据库

部署 Dependency-Track(或者直接用 SaaS,如 OWASP 自托管版本),把 SBOM 推上去,让它订阅 NVD/GHSA/OSV。

这一步会暴露第一个现实:历史技术债远大于新增风险。几乎所有项目第一次扫描都会发现几十个 high/critical。此时不要 panic——按”可利用性 × 修复成本”做优先级,先处理”有公开 exploit 且一条命令能升的”,把中长期不能升的组件挂 VEX 标注并上报给安全团队备案。

Dependency-Track 的最小部署(Docker Compose):

# docker-compose.yml
version: "3.8"
services:
  dtrack-apiserver:
    image: dependencytrack/apiserver:4.11.0
    environment:
      - ALPINE_DATABASE_MODE=external
      - ALPINE_DATABASE_URL=jdbc:postgresql://db:5432/dtrack
      - ALPINE_DATABASE_USERNAME=dtrack
      - ALPINE_DATABASE_PASSWORD=changeme
    deploy:
      resources:
        limits:
          memory: 8G
    volumes:
      - dtrack-data:/data
    depends_on: [db]
    ports: ["8081:8080"]

  dtrack-frontend:
    image: dependencytrack/frontend:4.11.0
    environment:
      - API_BASE_URL=http://localhost:8081
    ports: ["8080:8080"]

  db:
    image: postgres:15
    environment:
      - POSTGRES_DB=dtrack
      - POSTGRES_USER=dtrack
      - POSTGRES_PASSWORD=changeme
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  dtrack-data:
  pgdata:

首次启动后到管理界面开启 NVD、GitHub Advisories、OSV 三个 analyzer 数据源,等待首轮镜像同步(NVD 完整镜像约 200 MB,OSV 约 500 MB),之后 API Key 即可用于从 CI 上传 SBOM。

8.3 第三步:许可证策略网关

等漏洞治理走上正轨后,再上许可证。典型策略:

许可证类别 合并到 main 发布到公网 SaaS 自用
公共领域 / CC0
MIT / BSD / Apache-2.0
MPL-2.0 / LGPL(动态链接) ✔(附义务)
LGPL 静态链接 需审批 需审批
GPL-2.0 / GPL-3.0 需审批 仅随 GPL 产品
AGPL-3.0 ❌ 原则禁 需审批
SSPL / BSL / Commons Clause
未知 / 无声明

策略用 ORT Evaluator 的 Kotlin DSL 或 Dependency-Track 的 Policy Engine 落地,结合 OPA Rego 还能把”我们的产品是 SaaS 还是分发品”当成参数传入。

一个最小的 OPA Rego 策略示例:

package sbom.license

# 默认黑名单
forbidden_licenses := {
    "AGPL-3.0-only", "AGPL-3.0-or-later",
    "SSPL-1.0",
    "Commons-Clause",
    "BUSL-1.1",
}

# 产品类型参数(通过 input.product_type 传入)
# "distributed"(分发)| "saas"(自用)| "internal"(内部工具)

deny[msg] {
    some component
    component := input.components[_]
    license := component.licenses[_].license.id
    forbidden_licenses[license]
    input.product_type == "distributed"
    msg := sprintf("分发产品禁止使用 %s 许可证(组件:%s@%s)",
        [license, component.name, component.version])
}

deny[msg] {
    some component
    component := input.components[_]
    license := component.licenses[_].license.id
    license == "GPL-3.0-only"
    input.product_type == "distributed"
    not component.properties[_].name == "license_approval"
    msg := sprintf("GPL-3.0 组件 %s 需走白名单审批",
        [component.name])
}

在 CI 中调用:

opa eval \
  --data policy.rego \
  --input sbom.cdx.json \
  --format pretty \
  'data.sbom.license.deny'

有 deny 输出就阻断构建。Rego 的优点是策略即代码——策略本身可以进版本库、走 PR review、写单测。

8.4 第四步:SBOM 发布与存档

配套还需要建立几条最低限度的运营 SLA

事件 响应 SLA
Critical CVE(CVSS ≥ 9.0)且已有公开 exploit 8 小时内确认影响范围 + 48 小时内热修
High CVE(7.0 ≤ CVSS < 9.0) 72 小时内确认 + 7 天内修复或 VEX 豁免
Medium / Low 30 天内随例行版本合并修复
许可证高风险引入(AGPL/SSPL) PR 阻断 + 24 小时内人工复核
新 CVE 影响过往 Release(回溯) 7 天内发布客户通告

SLA 写下来之后,SCA 工具才真正开始发挥作用;没有 SLA 的 SBOM 只是一份没人看的 JSON。

九、工程坑点

实际落地一年后你大概率会踩到的坑:

  1. SBOM 不是一次性产物。常见误解是”发版前扫一下就行”。现实是:依赖锁文件每次 npm install 都会变,容器基础镜像每次重建都会变,CVE 每天都在新增。SBOM 必须按构建生成、按版本归档、按时间监控,任何一环缺失都失去价值。

  2. Go 二进制的模块信息缺失。Go 1.18+ 用 -buildvcs 嵌入了模块信息,但 1.17 及以下、以及 -trimpath 构建的二进制,syft 读不到依赖。解决办法是构建时强制保留 buildinfo,或扫描源码而不是二进制。

  3. Python 动态依赖看不到requirements.txt 能抓到显式依赖,但 setup.py 动态 import、conda 环境、notebook magic 安装的包都抓不到。建议以 pip freeze 的输出(运行时真实安装结果)为准。

  4. vendored 代码被漏扫。某些团队把 third_party/zlib/ 复制进仓库但没有任何元数据。此时只有 ScanCode 等基于文件指纹的工具才认得出,单靠 Syft 不够。

  5. Fat-JAR 内层依赖。Spring Boot 的可执行 JAR 里嵌套了几百个小 JAR,Syft 默认会递归解开,但某些 Shade 插件重打包后类名被重写,反解失败。治理思路:禁 shade 重写,或在 Maven 阶段先扫 dependency:list

  6. License 冲突误报。同一个包在 NPM 写 MIT、源码头写 ISC、NOTICE 写 Apache-2.0——这不一定是违规,很多项目历史上就是这么”多许可证并存”。要手工建立”已审白名单”而不是每个 PR 都报红。

  7. 容器基础镜像的 CVE 风暴。扫一个官方 Ubuntu 22.04 基础镜像能轻松扫出 200+ CVE,大部分不可利用。策略要把基础镜像 CVE 与应用层 CVE 分轨治理,基础镜像层面以”及时更新 base image”代替”逐条修复”。

  8. 漏洞数据库滞后与误报。NVD 在 2024 年经历了严重的分析积压,大量 CVE 超过 3 个月仍无 CPE 映射,导致 Grype/Trivy 误报或漏报。推荐多源交叉:OSV + GHSA + 厂商原生库。

  9. SBOM 本身成为敏感资产。一份完整 SBOM 等于告诉攻击者”我用了哪个 0-day 影响的库”。对外发布的 SBOM 与对内使用的 SBOM 应区分:对外脱敏(如不含内网路径、构建机器名),对内保真。

  10. AI 模型与数据集不在经典 SBOM 范围。用 HuggingFace 上一个 Apache-2.0 权重训练了自己的模型,这个模型在你产品里是”组件”吗?SPDX 3.0、CycloneDX 1.5 的 AI-BOM/ML-BOM 正在补这个缺口,但工具链成熟度还不高。

  11. 多仓库去重。一家公司有上千个微服务,每个服务都引入 Jackson——漏洞管理应按”组件 × 版本 × 受影响服务”维度汇总,而不是每服务各自一张表。Dependency-Track 的 “Portfolio” 视图就是解决这个的。

  12. 依赖混淆攻击(dependency confusion)。攻击者在公共 registry 上抢注你的内部包名,同版本号抢先发布,你的构建会误从外网拉取恶意包。防御手段:内部包统一使用 scope(如 @mycorp/xxx)或独立 registry 且禁止 fallback 到公共源。

  13. 恶意 typo-squattingloadsh 冒充 lodashcolros 冒充 colors,这类包往往在首次安装时植入挖矿脚本。SCA 工具需要订阅恶意包情报(如 Sonatype OSS Index、Checkmarx SCS、PyUp Safety),不是所有漏洞库都覆盖。

  14. CI 产物外泄。SBOM 里含内部 npm registry URL、内部包名、commit hash,这些本身是敏感情报。在 GitHub Actions 里用 upload-artifact 时,如果仓库是 public,Artifact 也可能被外部访问(取决于仓库配置)。

  15. 跨团队多语言一致性。前端用 Syft、后端用 ORT、运维用 Trivy——三份 SBOM 格式近似但字段值略有差异,聚合到 Dependency-Track 后产生”重复组件但不同 PURL”的脏数据。治理办法是统一工具链,或在聚合层做 PURL 归一化。

  16. 签名密钥轮换。cosign 签名的 SBOM 在密钥轮换后验证失败,需要保留历史公钥或使用 Fulcio 的短期证书配合 Rekor 透明日志。SLSA L3+ 的标准做法是结合 OIDC 与 Fulcio 实现 “keyless” 签名。

十、选型建议

场景 推荐组合
个人项目 / 小团队 Syft + Grype(本地跑) + GitHub Dependabot
创业公司 A 轮前 Syft + Grype + Dependency-Track(自托管)
中型企业(百人研发) 上面基础上 + ORT(合规报告)+ Renovate(升级机器人)
大型企业(千人以上) 商业 SCA(FOSSA/Black Duck/Snyk 三选一)+ 内部 Dependency-Track + 自研策略
金融、电信、国央企 Black Duck 或 Nexus IQ 作底座(审计报告齐全) + 国产化 SCA(补 CNVD 数据源)
军工、关基 完全离线:本地 ScanCode + 本地 VulnerableCode + 本地漏洞库同步

一个经验法则:工具只是 30%,流程与人占 70%。再贵的 Black Duck 也挡不住开发绕过 CI 直推制品;再好用的 Syft 也救不了一家没人读 SBOM 的公司。能跑得下去的标志是:有一个 OSPO 或安全团队每周 review 一次 Dependency-Track 告警、每季度 review 一次许可证策略、每年复审一次 SBOM 发布政策。

再给一个选型决策树,帮助快速定位:

关于 OSPO 如何搭建,见 企业开源办公室(OSPO)与开源 PMO 建设

十一、中国企业实践概览

以下信息仅基于公开演讲、博客、白皮书,不代表内部实际架构。

华为:2020 年前后在内部上线基于 ORT 和 FOSSology 的开源合规平台”EulerMakerSec”(名称未公开)。华为是 ORT 的主要企业贡献者之一,公开提交了若干 Gradle/Maven 解析器改进。鸿蒙/欧拉项目的每个版本都随产品发布 SPDX SBOM。

字节跳动:字节的开源办公室 2022 年前后在 OpenAtom 会议上介绍过内部 SCA 体系,核心组件包括:基于 ScanCode 的许可证扫描、自研漏洞库(聚合 NVD/GHSA/OSV/CNVD)、与内部制品库 Bits 深度集成。对外的火山引擎云产品在 2024 年起陆续支持为客户生成 CycloneDX SBOM。

蚂蚁集团:在 2023 年 KubeCon China 演讲中介绍了基于 CycloneDX + Dependency-Track 的内部方案,结合自研的 “AntSec SBOM” 把 SBOM 签名(cosign)+ in-toto provenance 写入了 OceanBase/SOFAStack 等对外发布产品。蚂蚁也是 CycloneDX 标准的企业贡献者之一。

腾讯:腾讯代码安全团队公开过基于 Syft + Grype 的容器镜像扫描流水线,覆盖所有上 TKE(Tencent Kubernetes Engine)的业务镜像;在 OSPO 层面主要用商业工具做合规兜底(具体厂商未官宣)。

阿里巴巴:Dragonwell JDK、OceanBase、PolarDB 等对外产品在 2024 年后发布 SBOM(SPDX/CycloneDX 并存);云效 DevOps 平台内建了依赖扫描与许可证检查模块。

小米 / vivo / OPPO:Android/鸿蒙 衍生系统出海面临 Google GMS 和欧盟 CRA 双重合规压力,均已在 ROM 发布流程里加入 SBOM 生成步骤,供渠道商与监管核查。

这几家的共同规律:内部早用开源自研 + 外部合规交付用 SPDX/CycloneDX 标准

另一个值得关注的趋势是国产 SCA 厂商的崛起。2023 年后几家安全厂商陆续推出本地化 SCA 产品:

国产厂商普遍的优势是 CNVD/CNNVD 数据同步及时支持国产化操作系统(麒麟、统信、欧拉)的包格式解析对国产 CPU 架构(鲲鹏、飞腾、龙芯、兆芯)二进制的识别。劣势在国际开源生态(Rust、Swift、现代前端工具链)覆盖尚在追赶。对于”信创 + 国际化并存”的企业,常见策略是商业双栈——一家国产 + 一家国际,在内部聚合层合并去重。

最后是一个成本观察:人力成本通常高于工具成本。一家 500 人研发规模的企业,搭建并维护 SCA/SBOM 体系的人力投入普遍在 3–5 FTE(full-time equivalent),包括安全工程师、OSPO 合规、DevOps 集成、法务审阅各占其中一部分。而工具年费按座位通常在 20 万到 200 万人民币区间,二者加起来只是整体研发成本的千分之几,却能避免一起 Equifax、一次 GPL 诉讼、一批客户流失——投产比极高。

如果你的产品要出海到美国 / 欧盟,请顺带阅读 出海合规:许可证、出口管制与数据合规


本文为工程参考,不构成法律意见。涉及具体法律风险请咨询专业法律顾问。

十二、参考资料

标准与规范

法规

工具官方文档

商业厂商公开资料

行业报告与实践


上一篇:数字天堂、不乱买、罗盒:中国 GPL 诉讼第一案系列 | 下一篇:企业开源办公室(OSPO)与开源 PMO 建设

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-22 · architecture / opensource

开源许可与版权工程

面向中国工程团队的开源许可、版权与合规系列。从 GPL、AGPL、Apache、木兰协议到中国真实案例、SCA/SBOM 工具链与出海合规,讲清楚开源在工程落地中的坑与方法。


By .