用 cargo-deny 卡住许可证,CI 里被一个 GPL 依赖绊了两次
项目是 workspace,Rust 1.76,CI 跑在 Linux runner 上。真正触发我去看许可证的是法务的一句话:这个服务要对外交付二进制。当时 workspace 里 7 个 crate,锁文件里直接加间接依赖三百多个,没人说得清里面有没有 GPL 或者更麻烦的。
做法不复杂,装 cargo-deny,在 workspace 根目录建 deny.toml:
[licenses]
allow = ["MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "ISC", "Unicode-3.0", "Zlib"]
confidence-threshold = 0.9
[bans]
multiple-versions = "warn"
CI 里加一步 cargo deny --all-features check licenses bans。第一次跑就红了,报了一个 crate 的 license 字段是 GPL-3.0-or-later,还有一个是 MIT OR Apache-2.0 AND BSD-3-Clause 这种混合表达式,被我原来的 allow 列表漏掉。
这里有个细节值得说:cargo-deny 判断的是 crate 元数据里的 license 表达式,不是仓库里的 LICENSE 文件。有些 crate 的 Cargo.toml 里写的是 MIT/Apache-2.0 这种旧式 slash 写法,confidence 会掉,confidence-threshold = 0.9 一开它就直接报错。我的处理不是把阈值调到 1.0 放过去,而是把这些 crate 单独列进 exceptions,一个 crate 一条,写清为什么接受,这样评审时能看到来源。
代价是维护成本:每加一个依赖,只要它的 license 表达式不在白名单里,CI 就会挡一次。好处也在这里,挡在合入前而不是交付前。我还把 bans 的 multiple-versions 设成 warn 而不是 deny,因为 workspace 里两个 crate 锁了不同 minor 版本的 serde,强行 deny 会逼着我去锁版本,收益不抵成本。
失效的地方我踩过两次。一次是 --all-features 没开的时候,某个只在 feature 下引入的依赖完全没被检查,开了之后才冒出来。另一次是私有 registry 的 crate,license 字段空着,cargo-deny 直接按未知处理,报错信息里只说 missing license,得自己去翻仓库。还有 vendor 目录里的补丁 crate,元数据是改过的,检查结果和上游不一致,这种只能人工确认。
现在我会在 PR 模板里加一行:新增依赖请附上 license 检查结果。
你们那边对 MIT OR Apache-2.0 和 MIT AND Apache-2.0 这种表达式,白名单是按 crate 逐个放,还是按表达式整体放?
0 条评论
还没有评论,写下第一条。