强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

开源协议精讲 / 第九章: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 LicenseMIT
BSD 2-ClauseBSD-2-Clause
BSD 3-ClauseBSD-3-Clause
Apache License 2.0Apache-2.0
GNU GPL v2.0 onlyGPL-2.0-only
GNU GPL v2.0 or laterGPL-2.0-or-later
GNU GPL v3.0 onlyGPL-3.0-only
GNU GPL v3.0 or laterGPL-3.0-or-later
GNU LGPL v2.1LGPL-2.1-only
GNU LGPL v3.0LGPL-3.0-only
GNU AGPL v3.0AGPL-3.0-only
Mozilla Public License 2.0MPL-2.0
Eclipse Public License 2.0EPL-2.0
Creative Commons Zero 1.0CC0-1.0
The UnlicenseUnlicense
ISC LicenseISC
Boost Software License 1.0BSL-1.0
zlib LicenseZlib

完整列表: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.0Classpath 例外GPL-2.0
GCC-exception-3.1GCC 编译器例外GPL-3.0
Font-exception-2.0字体例外GPL-2.0
LLVM-exceptionLLVM 例外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 ToolkitPython许可证/版权检测准确度高、SPDX 输出
FOSSologyC/C++全面合规分析功能强大、有 Web UI
licenseeRuby许可证检测GitHub 官方工具
askalonoRust许可证文本匹配速度快
scancode.ioPythonWeb 界面的 ScanCode易用

9.4.2 商业工具

工具厂商功能
Black DuckSynopsys全面 SCA 平台
WhiteSource/MendMend开源安全与合规
SnykSnyk安全扫描与合规
FOSSAFOSSA许可证合规自动化
JFrog XrayJFrog制品扫描

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

输出示例:

my-project@1.0.0
├─ MIT: express@4.18.2
├─ MIT: body-parser@1.20.1
├─ Apache-2.0: mongodb@5.0.0
├─ ISC: semver@7.5.4
└─ GPL-2.0: some-gpl-lib@1.0.0  ← 警告!

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 标准

标准组织特点
SPDXLinux 基金会ISO 标准、全面
CycloneDXOWASP安全导向
SWID TagsISO/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):显示项目的依赖关系
  • 许可证警报:当依赖包含限制性许可证时警告

查看方法

  1. 进入仓库 → Insights → Dependency graph
  2. 查看依赖列表及其许可证

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 流程

扩展阅读

  1. SPDX 官网:https://spdx.org/
  2. SPDX 许可证列表:https://spdx.org/licenses/
  3. SPDX 规范:https://spdx.github.io/spdx-spec/
  4. ScanCode 文档:https://scancode-toolkit.readthedocs.io/
  5. NTIA SBOM 最小元素:https://www.ntia.gov/SBOM
  6. CycloneDX:https://cyclonedx.org/
  7. syft:https://github.com/anchore/syft

上一章:许可证兼容性与冲突 下一章:合规实务