开源许可证给产品团队看的重点
从一次依赖升级说起
上个季度,我在做产品发布前的依赖盘点。一个同事把 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 的传染性意味着:整个后端服务可能被视为「衍生作品」,所有源码都需要公开。
修复
团队有三条路:
- 替换:找一个纯 MIT/Apache 的 PDF 库,重新集成。
- 隔离:把 AGPL 组件拆成独立微服务,通过网络 API 通信,在法律层面论证不构成衍生作品。
- 接受:将整个后端开源。
考虑到时间和业务保密性,团队选择了方案一。代价是推迟发布三周,用另一个 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 许可开源。
客户在尽职调查中发现了这个问题。合同里有「知识产权无瑕疵」条款,这直接构成违约。
修复
公司做了两件事:
- 短期:把 GPL 库替换为一个 BSD 许可的替代方案,即使性能差 15%。
- 长期:在工程流程中建立了「许可证准入清单」,把 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 的数据库,并且这个依赖是紧密耦合的(比如通过嵌入式方式集成),法律论证的空间就很窄。
修复方案
团队做了架构调整:
- 将数据库交互层抽象为独立服务,通过 REST API 通信
- 在架构文档中记录「数据库作为独立服务运行,不构成衍生作品」的法律论证
- 同时评估了 PostgreSQL(BSD 许可)作为替代方案
关键不是「绝对安全」,而是「有足够的论证空间」。
数据库许可证对比
| 数据库 | 许可证 | SaaS 使用风险 | 是否需要开源你的应用 |
|---|---|---|---|
| PostgreSQL | BSD-like | 极低 | 否 |
| MySQL (Oracle) | GPL | 中(分发时触发) | 如果分发则需开源 |
| MongoDB (4.0+) | SSPL | 高 | 作为服务提供时需开源整个栈 |
| Elasticsearch (7.11+) | Elastic License 2.0 / SSPL | 高 | 作为服务提供时受限 |
| Redis (7.4+) | RSALv2 / SSPLv1 | 高 | 作为服务提供时受限 |
| CockroachDB | BSL → Apache 2.0 (2026) | 低(已转 Apache) | 否 |
许可证决策流程图
产品团队在选型时,可以按以下流程判断许可证风险:
宽松许可证的义务清单
即使是 MIT 和 Apache 这样的宽松许可证,也不是「拿来就用,什么都不用做」。产品团队至少要满足以下义务:
| 义务 | MIT | Apache 2.0 | BSD 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 ls或license-checker检查间接依赖) - 如果许可证不在「允许清单」中,已提交审批流程
开发阶段
- 新增依赖前,检查许可证是否被 CI 拦截
- 不使用「复制粘贴」方式引入代码片段——来源不明的代码没有许可证保障
- Fork 开源项目时,保留原始 LICENSE 和 NOTICE 文件
- 修改 Apache 2.0 许可的代码时,在修改过的文件中标注变更说明
发布前
- 运行 SBOM 生成工具,输出完整的依赖清单(推荐 SPDX 格式)
- 对比许可证清单与准入策略,确认无违规项
- 所有宽松许可证的版权声明已收集,准备放入产品「关于」页面或文档
- Apache 2.0 依赖的 NOTICE 文件已汇总保留
- 法务(如有)已完成最终审核
持续维护
- 每半年用 SCA 工具重新扫描依赖树,捕获新增的许可证变化
- 关注上游项目的许可证变更(MongoDB → SSPL、Elasticsearch → Elastic License 都是先例)
- 升级主要依赖版本时,重新确认许可证是否变化
- 团队新人入职时,进行开源许可证培训
工具推荐
| 工具 | 类型 | 适用场景 | 集成方式 |
|---|---|---|---|
| license-checker | npm 包 | Node.js 项目许可证扫描 | CLI / CI |
| FOSSA | SaaS | 企业级许可证合规 + 安全扫描 | 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 的版本和半年后的版本可能许可证完全不同。上游变更时,你的依赖不会自动跟着变——但你的合规义务取决于你实际使用的版本。
总结
产品团队对开源许可证的态度应该是「不恐惧,但要知道边界」。
核心就三件事:
- 选型时看一眼许可证——不要等 CI 报了才知道。花 30 秒打开 LICENSE 文件,比花 3 周替换依赖便宜得多。
- 维护一份许可证准入清单——什么能用、什么要审批、什么不能用,写清楚,放到 CI 里自动拦截。
- 发布前跑一次 SBOM 扫描——确认没有意外。生成一份 SPDX 格式的清单,法务和工程都能看懂。
如果你所在的团队刚刚开始建立开源合规流程,建议的优先级是:
- 先做 CI 许可证拦截(成本最低,效果最直接)
- 再做 SBOM 生成和存档(满足尽调和审计需求)
- 最后建立跨部门审批流程(适合依赖数量多、团队规模大的场景)
开源合规不是阻碍使用开源。它是让你和团队清楚知道产品的边界在哪里。当你需要向客户、投资人或合作方解释「我们的代码是干净的」时,有一份完整的依赖清单比任何口头承诺都有说服力。
参考资料
- Daeryun Law: Open Source Compliance - GPL, MIT, Apache
- Promise Legal: Open Source License Traps for SaaS Businesses
- MindCTO: How AGPL License Risk Can Destroy Your Startup's Valuation
- Jimmy Song: 开源软件合规实践
- 君泽君律师事务所: 开源许可证的传染性条款之探析
- Linux Foundation: Fulfilling Open Source License Obligations - Can Checklists Help?
- 汉坤律师事务所: SaaS 应用场景下的开源合规
- choosealicense.com