name: license-compliance
description: 开源许可证合规,包括兼容性分析、义务跟踪和合规工作流
allowed-tools: Read, Glob, Grep, Write, Edit, Task
开源许可证合规
在开发前后提供开源许可证合规的全面指导。
何时使用此技能
- 为新项目评估开源依赖项
- 检查包之间的许可证兼容性
- 理解分发义务
- 创建归属声明和NOTICES文件
- 为您的组织建立许可证政策
许可证类别
宽松许可证
允许使用、修改和分发,限制最少。
| 许可证 |
义务 |
商业使用 |
专利授权 |
| MIT |
归属 |
✓ |
否 |
| BSD-2-Clause |
归属 |
✓ |
否 |
| BSD-3-Clause |
归属,无背书 |
✓ |
否 |
| Apache-2.0 |
归属,声明变更,NOTICE文件 |
✓ |
是 |
| ISC |
归属 |
✓ |
否 |
Copyleft许可证
要求衍生作品使用相同许可证。
| 许可证 |
Copyleft范围 |
SaaS触发 |
分发义务 |
| GPL-2.0 |
强 |
否 |
源代码披露 |
| GPL-3.0 |
强 |
否 |
源代码披露,反Tivo化 |
| LGPL-2.1 |
弱(库) |
否 |
库源代码,允许链接 |
| AGPL-3.0 |
强 + 网络 |
是 |
网络使用时源代码披露 |
| MPL-2.0 |
文件级 |
否 |
修改文件源代码 |
| EPL-2.0 |
模块级 |
否 |
修改模块源代码 |
弱Copyleft vs 强Copyleft
强Copyleft (GPL):
┌──────────────────────────────────────────┐
│ 您的应用程序(变为GPL) │
│ ┌──────────────────────────────────┐ │
│ │ GPL库(链接/包含) │
│ └──────────────────────────────────┘ │
└──────────────────────────────────────────┘
弱Copyleft (LGPL):
┌──────────────────────────────────────────┐
│ 您的应用程序(任何许可证) │
│ ↓ 动态链接 │
│ ┌──────────────────────────────────┐ │
│ │ LGPL库(保持LGPL) │
│ └──────────────────────────────────┘ │
└──────────────────────────────────────────┘
许可证兼容性
兼容性矩阵
输入许可证 → 输出许可证兼容性
FROM ↓ / TO → | MIT | Apache | BSD | LGPL | MPL | GPL | AGPL
---------------|-----|--------|-----|------|-----|-----|------
MIT | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓
Apache-2.0 | ✗ | ✓ | ✗ | ✓ | ✓ | ✓* | ✓*
BSD-3-Clause | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓
LGPL-2.1 | ✗ | ✗ | ✗ | ✓ | ✗ | ✓ | ✓
MPL-2.0 | ✗ | ✗ | ✗ | ✗ | ✓ | ✓ | ✓
GPL-2.0 | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗
GPL-3.0 | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✓
AGPL-3.0 | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓
✓ = 兼容,✗ = 不兼容
* 仅GPL-3.0(Apache-2.0与GPL-2.0不兼容)
常见兼容性问题
| 问题 |
示例 |
解决方案 |
| GPL + 专有 |
在闭源中使用GPL库 |
使用LGPL替代或开源 |
| Apache + GPL-2.0 |
结合Apache-2.0与GPL-2.0 |
升级到GPL-3.0 |
| AGPL + SaaS |
在Web服务中使用AGPL |
开源您的代码或使用替代 |
| 冲突Copyleft |
同一二进制中GPL + EPL |
分离为不同程序 |
按用例的义务分析
仅内部使用
| 许可证类型 |
义务 |
需要跟踪 |
| 宽松 |
无 |
最小 |
| 弱Copyleft |
无 |
最小 |
| 强Copyleft |
无(无分发) |
最小 |
| AGPL |
网络服务时源代码可用 |
是 |
分发(桌面/移动)
| 许可证类型 |
义务 |
| MIT, BSD, ISC |
在分发中包含许可证/版权 |
| Apache-2.0 |
包含许可证、NOTICE文件、声明变更 |
| LGPL |
提供库源代码,允许重新链接 |
| GPL |
提供完整源代码 |
| MPL |
提供修改文件源代码 |
SaaS(无二进制分发)
| 许可证类型 |
义务 |
| 宽松 |
无(无分发) |
| GPL, LGPL |
无(无分发) |
| AGPL |
必须向用户提供源代码 |
许可证合规实施
.NET依赖分析
// 许可证扫描集成
public class LicenseComplianceChecker
{
private readonly IPackageMetadataProvider _packageProvider;
private readonly LicensePolicy _policy;
public async Task<ComplianceReport> AnalyzeProject(
string projectPath,
CancellationToken ct)
{
var packages = await _packageProvider.GetPackages(projectPath, ct);
var report = new ComplianceReport();
foreach (var package in packages)
{
var license = await _packageProvider.GetLicense(package, ct);
var evaluation = _policy.Evaluate(license);
report.Packages.Add(new PackageLicenseInfo
{
PackageId = package.Id,
Version = package.Version,
License = license.SpdxIdentifier,
LicenseUrl = license.Url,
Category = license.Category,
Status = evaluation.Status,
Obligations = evaluation.Obligations,
Issues = evaluation.Issues
});
}
return report;
}
}
public class LicensePolicy
{
private readonly HashSet<string> _approved = new()
{
"MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "ISC"
};
private readonly HashSet<string> _requiresReview = new()
{
"LGPL-2.1", "LGPL-3.0", "MPL-2.0", "EPL-2.0"
};
private readonly HashSet<string> _prohibited = new()
{
"GPL-2.0", "GPL-3.0", "AGPL-3.0"
};
public PolicyEvaluation Evaluate(LicenseInfo license)
{
if (_approved.Contains(license.SpdxIdentifier))
{
return new PolicyEvaluation
{
Status = PolicyStatus.Approved,
Obligations = GetObligations(license.SpdxIdentifier)
};
}
if (_requiresReview.Contains(license.SpdxIdentifier))
{
return new PolicyEvaluation
{
Status = PolicyStatus.RequiresReview,
Obligations = GetObligations(license.SpdxIdentifier),
Issues = new[] { "Copyleft许可证需要法律审查" }
};
}
if (_prohibited.Contains(license.SpdxIdentifier))
{
return new PolicyEvaluation
{
Status = PolicyStatus.Prohibited,
Issues = new[] { "强Copyleft与专有分发不兼容" }
};
}
return new PolicyEvaluation
{
Status = PolicyStatus.Unknown,
Issues = new[] { $"未知许可证: {license.SpdxIdentifier}" }
};
}
}
归属和NOTICES文件
// NOTICES文件生成器
public class NoticeFileGenerator
{
public string GenerateNotice(IEnumerable<PackageLicenseInfo> packages)
{
var sb = new StringBuilder();
sb.AppendLine("第三方软件声明和信息");
sb.AppendLine("=============================================");
sb.AppendLine();
sb.AppendLine("本软件包含以下第三方组件:");
sb.AppendLine();
foreach (var pkg in packages.OrderBy(p => p.PackageId))
{
sb.AppendLine($"## {pkg.PackageId} ({pkg.Version})");
sb.AppendLine($"许可证: {pkg.License}");
sb.AppendLine($"URL: {pkg.LicenseUrl}");
sb.AppendLine();
if (!string.IsNullOrEmpty(pkg.Copyright))
{
sb.AppendLine(pkg.Copyright);
sb.AppendLine();
}
if (!string.IsNullOrEmpty(pkg.LicenseText))
{
sb.AppendLine("许可证文本:");
sb.AppendLine(pkg.LicenseText);
sb.AppendLine();
}
sb.AppendLine("---");
sb.AppendLine();
}
return sb.ToString();
}
}
.NET项目配置
<!-- 在构建中启用许可证元数据 -->
<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<!-- 在包中包含NOTICES文件 -->
<None Include="NOTICES.txt" Pack="true" PackagePath="" />
<!-- 为您的包设置许可证表达式 -->
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<!-- 或基于文件的许可证 -->
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
</ItemGroup>
许可证政策模板
组织许可证政策
# 开源许可证政策
## 1. 目的
本政策管理[组织]产品中开源软件的使用。
## 2. 许可证类别
### 2.1 批准许可证(无需审查)
- MIT
- Apache-2.0
- BSD-2-Clause
- BSD-3-Clause
- ISC
- Unlicense
- CC0-1.0
### 2.2 需要审查
- LGPL-2.1, LGPL-3.0(弱copyleft - 使用上下文重要)
- MPL-2.0, EPL-2.0(文件/模块级copyleft)
- Creative Commons(类型各异)
- 双重许可包
### 2.3 禁止
- GPL-2.0, GPL-3.0(强copyleft - 除非项目是GPL)
- AGPL-3.0(网络copyleft)
- SSPL(服务器端公共许可证)
- 任何有使用领域限制的许可证
- 未经法律审查的未知或自定义许可证
## 3. 流程
### 3.1 新依赖添加
1. 使用`dotnet-license-check`或等效工具检查许可证
2. 如果批准:继续,确保归属
3. 如果需要审查:提交至legal@company.com
4. 如果禁止:寻找替代或请求例外
### 3.2 分发
在发布前:
1. 运行许可证审计
2. 生成NOTICES文件
3. 包含所需归属
4. 为copyleft合规存档源代码
## 4. 例外
例外需要法律和CTO的书面批准。
## 5. 合规验证
- CI/CD管道中的自动扫描
- 季度手动审计
- 年度政策审查
SPDX标识符
常见SPDX标识符
宽松许可证:
MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, Unlicense, CC0-1.0
弱Copyleft:
LGPL-2.1-only, LGPL-2.1-or-later, LGPL-3.0-only, LGPL-3.0-or-later
MPL-2.0, EPL-2.0, OSL-3.0
强Copyleft:
GPL-2.0-only, GPL-2.0-or-later, GPL-3.0-only, GPL-3.0-or-later
AGPL-3.0-only, AGPL-3.0-or-later
复合表达式:
(MIT OR Apache-2.0) - 选择
(LGPL-2.1-only AND MIT) - 两者适用
GPL-2.0-only WITH Classpath-exception-2.0 - 例外
CI/CD集成
许可证扫描管道
# GitHub Actions示例
name: 许可证合规检查
on:
pull_request:
paths:
- '**/*.csproj'
- '**/packages.lock.json'
jobs:
license-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: 设置.NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: 安装许可证检查器
run: dotnet tool install --global dotnet-project-licenses
- name: 检查许可证
run: |
dotnet-project-licenses -i . \
--allowed-license-types "MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause" \
--output license-report.json \
--output-type json
- name: 上传许可证报告
uses: actions/upload-artifact@v4
with:
name: license-report
path: license-report.json
- name: 在禁止许可证时失败
run: |
if grep -q "GPL-" license-report.json; then
echo "::error::检测到禁止许可证"
exit 1
fi
许可证合规检查清单
开发前
- [ ] 为项目定义许可证政策
- [ ] 识别项目分发模型(SaaS/桌面/库)
- [ ] 确定您的代码的输出许可证
- [ ] 建立依赖审查流程
开发期间
- [ ] 在添加每个依赖前检查许可证
- [ ] 在NOTICES文件中维护归属
- [ ] 记录任何例外
- [ ] 在CI中运行许可证扫描
发布前
- [ ] 完成许可证审计
- [ ] 生成最终NOTICES文件
- [ ] 验证所有归属已包含
- [ ] 为copyleft合规存档源代码
- [ ] 如果需要,法律签署
交叉引用
- SBOM:
sbom-management 用于依赖跟踪
- 安全:
security-frameworks 用于安全供应链
- 数据隐私: 考虑依赖项中的数据
资源