开源协议精讲 / 第九章:SPDX 标准与工具
第九章:SPDX 标准与工具
引言
当你的项目依赖数十甚至数百个开源组件时,手动管理许可证信息几乎不可能。SPDX(Software Package Data Exchange)提供了一套标准化的方式来描述软件包中的许可证信息,让合规工作自动化成为可能。
9.1 SPDX 概述
9.1.1 什么是 SPDX?
SPDX(Software Package Data Exchange)是由 Linux 基金会维护的开放标准,用于以标准化的方式交流软件物料清单(SBOM)和许可证信息。
| 项目 | 信息 |
|---|---|
| 全称 | Software Package Data Exchange |
| 维护组织 | Linux 基金会 / SPDX 工作组 |
| 当前版本 | SPDX 2.3(2022)/ SPDX 3.0(2024) |
| ISO 标准 | ISO/IEC 5962:2021 |
| 官网 | https://spdx.org/ |
9.1.2 SPDX 的核心功能
SPDX 核心功能:
├── 许可证标识(License ID)
│ └── 标准化的许可证短标识
├── 软件物料清单(SBOM)
│ └── 列出所有组件及其许可证
├── 版权信息
│ └── 记录版权持有人和声明
├── 安全漏洞信息(SPDX 3.0)
│ └── 关联 CVE 信息
└── 数据格式
└── RDF、JSON、Tag-Value、YAML
9.1.3 为什么需要 SPDX?
没有 SPDX 的混乱:
同一个许可证的多种写法:
├── "MIT License"
├── "MIT"
├── "Expat License"
├── "The MIT License (MIT)"
├── "mit"
└── "MIT许可证"
→ 机器无法自动识别和比较
有了 SPDX 的标准化:
统一标识:MIT
→ 工具可以自动处理
9.2 SPDX 许可证标识
9.2.1 许可证短标识(Short Identifier)
SPDX 为每个已知许可证定义了唯一的短标识:
| 许可证名称 | SPDX 短标识 |
|---|---|
| MIT License | MIT |
| BSD 2-Clause | BSD-2-Clause |
| BSD 3-Clause | BSD-3-Clause |
| Apache License 2.0 | Apache-2.0 |
| GNU GPL v2.0 only | GPL-2.0-only |
| GNU GPL v2.0 or later | GPL-2.0-or-later |
| GNU GPL v3.0 only | GPL-3.0-only |
| GNU GPL v3.0 or later | GPL-3.0-or-later |
| GNU LGPL v2.1 | LGPL-2.1-only |
| GNU LGPL v3.0 | LGPL-3.0-only |
| GNU AGPL v3.0 | AGPL-3.0-only |
| Mozilla Public License 2.0 | MPL-2.0 |
| Eclipse Public License 2.0 | EPL-2.0 |
| Creative Commons Zero 1.0 | CC0-1.0 |
| The Unlicense | Unlicense |
| ISC License | ISC |
| Boost Software License 1.0 | BSL-1.0 |
| zlib License | Zlib |
完整列表:https://spdx.org/licenses/
9.2.2 SPDX 表达式(License Expression)
SPDX 支持使用布尔逻辑组合多个许可证:
| 运算符 | 含义 | 示例 |
|---|---|---|
AND | 同时适用 | MIT AND Apache-2.0 |
OR | 可选其一 | MIT OR Apache-2.0 |
WITH | 附加例外 | GPL-2.0-only WITH Classpath-exception-2.0 |
+ | 或更高版本 | GPL-2.0-or-later(等同于 GPL-2.0+) |
() | 优先级 | (MIT OR Apache-2.0) AND GPL-3.0 |
常见表达式示例:
MIT # 单一许可证
MIT OR Apache-2.0 # 二选一
GPL-2.0-only WITH Classpath-exception-2.0
Apache-2.0 AND MIT # 同时适用
(Apache-2.0 OR MIT) AND GPL-3.0 # 复杂组合
9.2.3 SPDX 许可证异常(License Exception)
SPDX 定义了常见的许可证例外:
| 异常标识 | 说明 | 常见搭配 |
|---|---|---|
Classpath-exception-2.0 | Classpath 例外 | GPL-2.0 |
GCC-exception-3.1 | GCC 编译器例外 | GPL-3.0 |
Font-exception-2.0 | 字体例外 | GPL-2.0 |
LLVM-exception | LLVM 例外 | Apache-2.0 |
9.2.4 SPDX 许可证列表(License List)
SPDX 许可证列表是开源世界中最权威的许可证索引:
| 分类 | 说明 |
|---|---|
| 许可证(License) | 完整的许可证文本 |
| 例外(Exception) | 许可证的例外条款 |
| 已弃用 | 不建议使用的旧标识 |
| 待定 | 正在审核中的标识 |
版本更新:SPDX 许可证列表大约每季度更新一次。
9.3 SPDX 文件格式
9.3.1 SPDX 文档结构
SPDX 文档可以包含以下部分:
SPDX 文档结构:
├── 文档创建信息(Document Creation)
├── 包信息(Package)
│ ├── 包名、版本、供应商
│ ├── 许可证信息
│ └── 版权信息
├── 文件信息(File)
│ ├── 文件名、类型
│ ├── 许可证信息
│ └── 版权信息
├── 片段信息(Snippet)
├── 其他许可信息(Other Licensing)
├── 关系信息(Relationship)
└── 注解(Annotation)
9.3.2 Tag-Value 格式示例
SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: my-project
DocumentNamespace: https://example.com/spdx/my-project-1.0
Creator: Tool: scancode-toolkit-30.0.0
PackageName: my-project
SPDXID: SPDXRef-Package
PackageVersion: 1.0.0
PackageDownloadLocation: https://github.com/example/my-project
FilesAnalyzed: true
PackageLicenseConcluded: Apache-2.0
PackageLicenseDeclared: Apache-2.0
PackageCopyrightText: Copyright 2024 Example Corp.
FileName: ./lib/foo.js
SPDXID: SPDXRef-File-foo
FileCopyrightText: Copyright 2023 John Doe
LicenseConcluded: MIT
LicenseInfoInFile: MIT
FileName: ./lib/bar.js
SPDXID: SPDXRef-File-bar
FileCopyrightText: Copyright 2022 Jane Smith
LicenseConcluded: Apache-2.0
LicenseInfoInFile: Apache-2.0
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package
Relationship: SPDXRef-Package CONTAINS SPDXRef-File-foo
Relationship: SPDXRef-Package CONTAINS SPDXRef-File-bar
9.3.3 JSON 格式示例
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "my-project",
"documentNamespace": "https://example.com/spdx/my-project-1.0",
"creationInfo": {
"created": "2024-01-01T00:00:00Z",
"creators": ["Tool: scancode-toolkit-30.0.0"]
},
"packages": [
{
"SPDXID": "SPDXRef-Package",
"name": "my-project",
"versionInfo": "1.0.0",
"downloadLocation": "https://github.com/example/my-project",
"licenseConcluded": "Apache-2.0",
"licenseDeclared": "Apache-2.0",
"copyrightText": "Copyright 2024 Example Corp.",
"hasFiles": ["SPDXRef-File-foo", "SPDXRef-File-bar"]
}
],
"files": [
{
"SPDXID": "SPDXRef-File-foo",
"fileName": "./lib/foo.js",
"copyrightText": "Copyright 2023 John Doe",
"licenseConcluded": "MIT",
"licenseInfoInFiles": ["MIT"]
}
]
}
9.3.4 格式选择
| 格式 | 特点 | 适用场景 |
|---|---|---|
| Tag-Value | 简洁、可读 | 手动编辑、小型项目 |
| JSON | 结构化、广泛支持 | 自动化工具、API |
| YAML | 可读、简洁 | 配置管理 |
| RDF/XML | 语义网标准 | 数据交换、查询 |
9.4 许可证扫描工具
9.4.1 开源工具
| 工具 | 语言 | 功能 | 特点 |
|---|---|---|---|
| ScanCode Toolkit | Python | 许可证/版权检测 | 准确度高、SPDX 输出 |
| FOSSology | C/C++ | 全面合规分析 | 功能强大、有 Web UI |
| licensee | Ruby | 许可证检测 | GitHub 官方工具 |
| askalono | Rust | 许可证文本匹配 | 速度快 |
| scancode.io | Python | Web 界面的 ScanCode | 易用 |
9.4.2 商业工具
| 工具 | 厂商 | 功能 |
|---|---|---|
| Black Duck | Synopsys | 全面 SCA 平台 |
| WhiteSource/Mend | Mend | 开源安全与合规 |
| Snyk | Snyk | 安全扫描与合规 |
| FOSSA | FOSSA | 许可证合规自动化 |
| JFrog Xray | JFrog | 制品扫描 |
9.4.3 ScanCode Toolkit 使用示例
# 安装
pip install scancode-toolkit
# 扫描项目
scancode -clpieu --json-pp output.json /path/to/project
# 输出 SPDX 格式
scancode -clpieu --spdx-tv output.spdx /path/to/project
# 仅扫描许可证
scancode -l --json-pp licenses.json /path/to/project
输出示例:
{
"headers": [
{
"tool_name": "scancode-toolkit",
"tool_version": "30.0.0",
"options": {
"input": ["/path/to/project"],
"--json-pp": "output.json"
}
}
],
"files": [
{
"path": "lib/foo.js",
"type": "file",
"licenses": [
{
"key": "mit",
"score": 100.0,
"name": "MIT License",
"short_name": "MIT",
"spdx_license_key": "MIT"
}
],
"copyrights": [
{
"value": "Copyright 2023 John Doe"
}
]
}
]
}
9.4.4 npm audit 与 license-checker
# 安装 license-checker
npm install -g license-checker
# 扫描当前项目
license-checker
# 输出 JSON
license-checker --json > licenses.json
# 过滤特定许可证
license-checker --production --failOn "GPL-2.0;GPL-3.0"
# 输出 CSV
license-checker --csv --out licenses.csv
输出示例:
[email protected]
├─ MIT: [email protected]
├─ MIT: [email protected]
├─ Apache-2.0: [email protected]
├─ ISC: [email protected]
└─ GPL-2.0: [email protected] ← 警告!
9.4.5 Python pip-licenses
# 安装
pip install pip-licenses
# 列出所有依赖的许可证
pip-licenses
# 输出格式化表格
pip-licenses --format=markdown
# 输出 JSON
pip-licenses --format=json
# 过滤特定许可证
pip-licenses --fail-on="GPL"
9.5 SBOM(软件物料清单)
9.5.1 什么是 SBOM?
SBOM(Software Bill of Materials)是软件组件的清单,类似于食品的成分表。
SBOM 包含:
├── 组件名称
├── 版本号
├── 供应商/作者
├── 许可证信息
├── 依赖关系
├── 哈希值(校验完整性)
└── 已知漏洞(CVE)
9.5.2 为什么 SBOM 重要?
| 原因 | 说明 |
|---|---|
| 合规要求 | 满足许可证义务 |
| 安全管理 | 快速识别受漏洞影响的组件 |
| 供应链透明 | 了解软件的组成 |
| 法律保护 | 在诉讼中证明合规 |
| 客户要求 | 企业客户要求提供 SBOM |
9.5.3 SBOM 标准
| 标准 | 组织 | 特点 |
|---|---|---|
| SPDX | Linux 基金会 | ISO 标准、全面 |
| CycloneDX | OWASP | 安全导向 |
| SWID Tags | ISO/IEC | 资产管理导向 |
9.5.4 生成 SBOM
# 使用 syft 生成 SBOM(推荐)
# 安装
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh
# 生成 SPDX 格式
syft /path/to/project -o spdx-json > sbom.spdx.json
# 生成 CycloneDX 格式
syft /path/to/project -o cyclonedx-json > sbom.cdx.json
# 扫描 Docker 镜像
syft docker:image:tag -o spdx-json > image-sbom.spdx.json
9.6 合规自动化
9.6.1 CI/CD 集成
将许可证检查集成到 CI/CD 流程中:
# GitHub Actions 示例
name: License Check
on: [push, pull_request]
jobs:
license-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: pip install scancode-toolkit
- name: Run license scan
run: |
scancode -clpieu --json-pp scan-results.json .
- name: Check for prohibited licenses
run: |
# 检查是否有 AGPL 许可证
if grep -q '"AGPL"' scan-results.json; then
echo "ERROR: AGPL license detected!"
exit 1
fi
- name: Generate SBOM
run: |
pip install scancode-toolkit
scancode -clpieu --spdx-tv sbom.spdx .
9.6.2 许可证策略配置
// .license-linter.json
{
"allowed": [
"MIT",
"BSD-2-Clause",
"BSD-3-Clause",
"Apache-2.0",
"ISC",
"CC0-1.0",
"Unlicense"
],
"warning": [
"LGPL-2.1",
"LGPL-3.0",
"MPL-2.0"
],
"prohibited": [
"AGPL-3.0",
"SSPL-1.0",
"EUPL-1.1"
],
"review_required": [
"GPL-2.0",
"GPL-3.0",
"EPL-2.0"
]
}
9.6.3 GitHub 的许可证检测
GitHub 内置了许可证检测功能:
- LICENSE 文件检测:自动识别项目根目录的 LICENSE 文件
- 依赖图(Dependency Graph):显示项目的依赖关系
- 许可证警报:当依赖包含限制性许可证时警告
查看方法:
- 进入仓库 → Insights → Dependency graph
- 查看依赖列表及其许可证
9.7 常见问题
Q1:SPDX 许可证标识可以在哪里使用?
SPDX 标识可以在以下场景使用:
- package.json 的
license字段 - pom.xml 的许可证声明
- SBOM 文档
- 源代码头部注释
- README 文件
Q2:如何处理不在 SPDX 列表中的许可证?
选项:
1. 使用 LicenseRef- 前缀
LicenseRef-CustomLicense
2. 提交到 SPDX 许可证列表
https://github.com/spdx/license-list-XML
3. 使用 NOASSERTION
表示无法确定许可证
Q3:扫描工具的准确率如何?
不同工具的准确率不同,建议:
- 使用多个工具交叉验证
- 手动审查关键组件
- 建立许可证白名单
Q4:SBOM 需要包含什么信息?
根据 NTIA 的最小 SBOM 要求:
- 组件名称
- 版本号
- 唯一标识(如 PURL、CPE)
- 供应商
- 依赖关系
- 许可证信息
- 时间戳
9.8 本章小结
| 概念 | 核心要点 |
|---|---|
| SPDX | 标准化的许可证标识和 SBOM 格式 |
| 许可证标识 | 统一的短标识,如 MIT、Apache-2.0 |
| 许可证表达式 | 使用 AND/OR/WITH 组合 |
| SBOM | 软件物料清单,列出所有组件 |
| 扫描工具 | ScanCode、FOSSology、licensee |
| 合规自动化 | 集成到 CI/CD 流程 |
扩展阅读
- SPDX 官网:https://spdx.org/
- SPDX 许可证列表:https://spdx.org/licenses/
- SPDX 规范:https://spdx.github.io/spdx-spec/
- ScanCode 文档:https://scancode-toolkit.readthedocs.io/
- NTIA SBOM 最小元素:https://www.ntia.gov/SBOM
- CycloneDX:https://cyclonedx.org/
- syft:https://github.com/anchore/syft