开源许可证给产品团队看的重点

0阅读10分钟

从一次依赖升级说起

上个季度,我在做产品发布前的依赖盘点。一个同事把 analytics 模块从 MIT 许可的库切换到了 AGPL 许可的替代方案——功能更好,benchmark 分数更高,社区也更活跃。他没有注意到许可证的变化。

我在 CI 的许可证扫描报告里发现了这个 AGPL 依赖。如果当时没有拦下来,我们的闭源产品就不得不公开整个服务端的源码。这不是一个「法务之后再看」的问题——它会直接影响产品架构和发布计划。

这篇文章是我在产品团队处理开源依赖这几年积累的经验。目标不是让你变成法务专家,而是帮你在选型、review 和发布流程中,建立对许可证风险的基本判断力。

许可证的分类逻辑

开源许可证有上百种,但产品团队真正需要关注的分类维度就两个:是否传染触发条件

宽松型 vs 互惠型

宽松型许可证(MIT、BSD、Apache 2.0)允许你自由使用、修改、再发布,甚至可以闭源商业化。核心义务是保留版权声明和许可证文本。Apache 2.0 还额外提供专利授权,但也附带专利报复条款——如果你对授权方发起专利诉讼,专利授权自动终止。

互惠型许可证(GPL、AGPL、LGPL、MPL)的核心特征是「传染性」——当你使用或修改了这些代码,你的衍生作品也必须以相同许可证开放。区别在于传染范围和触发条件:

许可证类型传染范围SaaS 是否触发商业产品友好度
MIT宽松
Apache 2.0宽松
BSD 2/3-Clause宽松
LGPL弱互惠仅库本身的修改
MPL 2.0弱互惠文件级——修改过的文件需开源
GPL 3.0强互惠所有链接/组合的衍生作品否(仅分发触发)
AGPL 3.0强互惠同 GPL,但扩展到网络服务极低

「分发」是关键触发点

GPL 的传染义务在「分发」时触发。你把包含 GPL 代码的软件交给用户,就需要提供完整源码。但如果 GPL 代码只运行在你的服务器上,用户通过网络访问——传统意义上不算分发——GPL 的义务就不触发。

AGPL 就是为了堵这个口子。AGPL v3 第 13 节明确规定:如果用户通过网络与软件交互,你必须向这些用户提供源码。对 SaaS 产品来说,这是最危险的许可证。

「SaaS deployment of GPL code generally avoids source code disclosure. AGPL v3 extends copyleft to network use.」 —— Daeryun Law: Open Source Compliance

案例一:传递依赖中的 AGPL 陷阱

场景

一个 B2B SaaS 产品,前端 React + 后端 Node.js。团队需要一个 PDF 生成模块。在 npm 上找到一个 star 数很高的库,功能完全满足需求,许可证写着 MIT。

翻车

上线前两周,安全团队做 SBOM(Software Bill of Materials)扫描时发现:这个 MIT 库的一个间接依赖(dependency tree 第三层)是 AGPL 3.0。具体来说,它依赖了一个 AGPL 许可的 PDF 渲染引擎。

AGPL 的传染性意味着:整个后端服务可能被视为「衍生作品」,所有源码都需要公开。

修复

团队有三条路:

  1. 替换:找一个纯 MIT/Apache 的 PDF 库,重新集成。
  2. 隔离:把 AGPL 组件拆成独立微服务,通过网络 API 通信,在法律层面论证不构成衍生作品。
  3. 接受:将整个后端开源。

考虑到时间和业务保密性,团队选择了方案一。代价是推迟发布三周,用另一个 MIT 库重写了 PDF 模块。

如何避免

在 CI/CD 中集成许可证扫描,拦截高风险许可证进入依赖树。以下是一个 GitHub Actions 的示例:

# ❌ 错误做法:只在 CI 里跑测试,不检查许可证
name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test
# ✅ 正确做法:在 CI 中集成许可证检查,拦截 AGPL/GPL
name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test
 
  license-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      # 使用 license-checker 扫描所有依赖
      - run: npx license-checker --failOn 'AGPL-3.0;GPL-3.0;SSPL-1.0' --summary
        # failOn 参数指定不允许的许可证列表
        # 任何依赖命中这些许可证,CI 直接失败

对于 Node.js 项目,也可以用 license-checker 生成许可证清单,作为发布流程的一部分:

# 生成所有依赖的许可证摘要
npx license-checker --summary --production
 
# 输出示例:
# ├─ MIT: 120
# ├─ ISC: 15
# ├─ BSD-2-Clause: 3
# └─ Apache-2.0: 8
# 如果看到 AGPL 或 GPL,就需要立即排查

案例二:GPL 组件混入闭源 SDK

场景

一个做开发者工具的公司,核心产品是一个 SDK,分发给客户集成到自己的应用中。SDK 一直用 MIT 许可。

一位新来的工程师在实现数据压缩功能时,引入了一个 GPL 许可的 C++ 库,通过 node-addon 桥接到 Node.js。他的理由是:「这个库性能好,而且只是底层绑定,应该不会传染到 JS 层。」

翻车

SDK 是要「分发」给客户的。GPL 义务在分发时触发。而且 GPL 不区分语言层——通过 FFI、addon 或动态链接,只要你的程序和 GPL 代码构成一个「组合作品」(combined work),整个 SDK 就需要以 GPL 许可开源。

客户在尽职调查中发现了这个问题。合同里有「知识产权无瑕疵」条款,这直接构成违约。

修复

公司做了两件事:

  1. 短期:把 GPL 库替换为一个 BSD 许可的替代方案,即使性能差 15%。
  2. 长期:在工程流程中建立了「许可证准入清单」,把 GPL/AGPL 列为禁止自动引入的许可证。

许可证准入清单的实现

用一个 JSON 文件维护许可证策略,集成到 CI 中:

// ❌ 错误做法:没有许可证策略,开发者自行判断
// 完全靠人工 review,容易漏掉传递依赖
// ✅ 正确做法:维护一份 license-policy.json,CI 自动校验
{
  "allowedLicenses": [
    "MIT",
    "Apache-2.0",
    "BSD-2-Clause",
    "BSD-3-Clause",
    "ISC",
    "0BSD",
    "Unlicense"
  ],
  "reviewRequired": [
    "LGPL-2.1",
    "LGPL-3.0",
    "MPL-2.0",
    "EPL-2.0"
    // 弱互惠型需要技术负责人 + 法务双重审批
  ],
  "blocked": [
    "GPL-2.0",
    "GPL-3.0",
    "AGPL-3.0",
    "SSPL-1.0",
    "EUPL-1.2"
    // 强互惠型直接拦截,除非获得法务特批
  ]
}

配合 license-checker 使用:

# 基于策略文件自动校验
npx license-checker --failOn 'AGPL-3.0;GPL-3.0;SSPL-1.0' --excludePrivatePackages

案例三:SaaS 后端的 AGPL 数据库

场景

一个 SaaS 产品使用 MongoDB(当时还是 AGPL)作为主数据库。后端服务通过官方 driver 连接 MongoDB,一切运行在自有云服务器上。团队认为「又没有分发软件,AGPL 管不到我们」。

翻车

2018 年 MongoDB 切换到 SSPL(Server Side Public License),比 AGPL 更严格。SSPL 要求:如果你把数据库作为服务提供给第三方,整个服务栈——包括管理工具、监控、备份——都必须以 SSPL 开源。

虽然 SSPL 主要针对数据库提供者,但这个案例说明一个问题:SaaS 产品的许可证风险不止来自你自己的代码,还来自你使用的基础设施组件。如果你的产品依赖了 AGPL/SSPL 的数据库,并且这个依赖是紧密耦合的(比如通过嵌入式方式集成),法律论证的空间就很窄。

修复方案

团队做了架构调整:

  1. 将数据库交互层抽象为独立服务,通过 REST API 通信
  2. 在架构文档中记录「数据库作为独立服务运行,不构成衍生作品」的法律论证
  3. 同时评估了 PostgreSQL(BSD 许可)作为替代方案

关键不是「绝对安全」,而是「有足够的论证空间」。

数据库许可证对比

数据库许可证SaaS 使用风险是否需要开源你的应用
PostgreSQLBSD-like极低
MySQL (Oracle)GPL中(分发时触发)如果分发则需开源
MongoDB (4.0+)SSPL作为服务提供时需开源整个栈
Elasticsearch (7.11+)Elastic License 2.0 / SSPL作为服务提供时受限
Redis (7.4+)RSALv2 / SSPLv1作为服务提供时受限
CockroachDBBSL → Apache 2.0 (2026)低(已转 Apache)

许可证决策流程图

产品团队在选型时,可以按以下流程判断许可证风险:

流程图画布 · 115%
Mermaid 流程图加载中...

宽松许可证的义务清单

即使是 MIT 和 Apache 这样的宽松许可证,也不是「拿来就用,什么都不用做」。产品团队至少要满足以下义务:

义务MITApache 2.0BSD 3-Clause
保留版权声明
包含许可证全文
标注修改过的文件
NOTICE 文件(如果上游有 NOTICE 文件)
专利授权
专利报复条款(起诉即终止)
不得使用原作者名字推广(BSD 3-Clause 特有)

Apache 2.0 的 NOTICE 义务经常被忽略。如果被依赖的项目包含 NOTICE 文件,你必须在分发时保留其中的归属信息。这不只是「放个 LICENSE 文件」那么简单。

# ❌ 错误做法:只复制了 LICENSE 文件,忽略了 NOTICE
cp node_modules/some-lib/LICENSE ./dist/
# 如果 some-lib 有 NOTICE 文件,你没有复制,就违反了 Apache 2.0 义务
# ✅ 正确做法:LICENSE 和 NOTICE 都要保留
cp node_modules/some-lib/LICENSE ./dist/
cp node_modules/some-lib/NOTICE ./dist/
# 在产品「关于」页面或文档中列出使用的开源组件及许可证
 
# 批量检查哪些 Apache 2.0 依赖有 NOTICE 文件
find node_modules -name "NOTICE" -path "*/LICENSE*" 2>/dev/null
# 或者更精确:先筛出 Apache 2.0 的包,再检查 NOTICE
npx license-checker --json | jq 'to_entries[] | select(.value.licenses | contains("Apache-2.0")) | .key'

开源合规检查清单

以下是我在产品团队中使用的检查清单,按开发生命周期分阶段:

选型阶段

  • 新依赖的许可证类型已确认(不只是看仓库标签,要核实 LICENSE 文件原文)
  • 许可证与产品形态兼容(分发 vs SaaS vs 嵌入式)
  • 传递依赖的许可证已扫描(npm lslicense-checker 检查间接依赖)
  • 如果许可证不在「允许清单」中,已提交审批流程

开发阶段

  • 新增依赖前,检查许可证是否被 CI 拦截
  • 不使用「复制粘贴」方式引入代码片段——来源不明的代码没有许可证保障
  • Fork 开源项目时,保留原始 LICENSE 和 NOTICE 文件
  • 修改 Apache 2.0 许可的代码时,在修改过的文件中标注变更说明

发布前

  • 运行 SBOM 生成工具,输出完整的依赖清单(推荐 SPDX 格式)
  • 对比许可证清单与准入策略,确认无违规项
  • 所有宽松许可证的版权声明已收集,准备放入产品「关于」页面或文档
  • Apache 2.0 依赖的 NOTICE 文件已汇总保留
  • 法务(如有)已完成最终审核

持续维护

  • 每半年用 SCA 工具重新扫描依赖树,捕获新增的许可证变化
  • 关注上游项目的许可证变更(MongoDB → SSPL、Elasticsearch → Elastic License 都是先例)
  • 升级主要依赖版本时,重新确认许可证是否变化
  • 团队新人入职时,进行开源许可证培训

工具推荐

工具类型适用场景集成方式
license-checkernpm 包Node.js 项目许可证扫描CLI / CI
FOSSASaaS企业级许可证合规 + 安全扫描SaaS / CI
ScanCode Toolkit开源工具深度许可证 + 版权检测CLI / CI
Syft (anchore)开源工具SBOM 生成(SPDX/CycloneDX)CLI / CI
Open Source Review Board 模板流程模板跨部门审批流程手动 / 飞书/Notion

常见误解纠正

「开源 = 免费,所以可以随便用」——开源不等于放弃权利。许可证是法律协议,违反许可证 = 侵犯版权。

「我们只是 SaaS,不分发,所以没事」——对 GPL 来说基本成立,但对 AGPL 和 SSPL 不成立。AGPL 明确覆盖网络服务场景。这个误解在实际中造成的事故最多——很多团队把 GPL 的「不分发就不触发」经验直接套到 AGPL 上,直到收到律师函。

「间接依赖不算,我们控制不了」——法律上你可能仍然有责任。法院通常看的是「你的产品是否包含了受保护的代码」,而不是「你知不知道」。所以 SBOM 扫描和 CI 拦截是必要的。npm 上一个包平均有 80+ 个传递依赖,你不扫描就不知道里面藏着什么。

「许可证只是法务的事」——等法务发现的时候,通常是产品发布前或者收到律师函的时候。到那时替换组件的成本远高于早期选型时注意一下的成本。一个替换核心依赖的平均工程成本是 2-4 周,如果在合同签约后被要求替换,成本可能乘以 10。

「小项目不用管」——小项目被收购时,许可证问题会变成尽职调查中的地雷。一个真实的案例:某创业公司在融资尽调中发现核心产品里有一个 AGPL 依赖,估值直接砍了 1000 万美元。投资方的逻辑很简单——如果核心代码必须开源,那护城河在哪里?

「用 GitHub stars 多的库就没问题」——Stars 多不代表许可证干净。一些知名的开源项目经历过许可证变更(Redis、Elasticsearch、MongoDB),你今天 fork 的版本和半年后的版本可能许可证完全不同。上游变更时,你的依赖不会自动跟着变——但你的合规义务取决于你实际使用的版本。

总结

产品团队对开源许可证的态度应该是「不恐惧,但要知道边界」。

核心就三件事:

  1. 选型时看一眼许可证——不要等 CI 报了才知道。花 30 秒打开 LICENSE 文件,比花 3 周替换依赖便宜得多。
  2. 维护一份许可证准入清单——什么能用、什么要审批、什么不能用,写清楚,放到 CI 里自动拦截。
  3. 发布前跑一次 SBOM 扫描——确认没有意外。生成一份 SPDX 格式的清单,法务和工程都能看懂。

如果你所在的团队刚刚开始建立开源合规流程,建议的优先级是:

  1. 先做 CI 许可证拦截(成本最低,效果最直接)
  2. 再做 SBOM 生成和存档(满足尽调和审计需求)
  3. 最后建立跨部门审批流程(适合依赖数量多、团队规模大的场景)

开源合规不是阻碍使用开源。它是让你和团队清楚知道产品的边界在哪里。当你需要向客户、投资人或合作方解释「我们的代码是干净的」时,有一份完整的依赖清单比任何口头承诺都有说服力。

参考资料

评论 0

0 / 1000