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

开源协议精讲 / 第九章: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

输出示例:

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

标准 组织 特点
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):显示项目的依赖关系
  • 许可证警报:当依赖包含限制性许可证时警告

查看方法

  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

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