不能只靠go-licenses扫描完就交差,必须将其嵌入CI构建链路,配合白名单与人工兜底机制;因其仅列出依赖并复制许可证,不判断冲突(如GPLv3与Apache-2.0),也不检测子模块引入的AGPL库,且无法覆盖跨语言依赖(如cgo、Node.js工具)的许可证穿透风险。

直接上结论:不能只靠 go-licenses 扫描完就交差,必须把许可证检查嵌进 CI 的构建链路里,并配合白名单 + 人工兜底机制,否则合规审查就是纸面功夫。
为什么 go-licenses 单独运行不等于合规
go-licenses 只做两件事:列出依赖、复制许可证文件。它不判断 GPLv3 和你项目用的 Apache-2.0 是否冲突,也不管子模块是否偷偷引入了 AGPL 库——这些都得靠人或额外规则来拦。
- 常见错误现象:
go-licenses save .成功执行,但go list -m all | grep agpl仍能搜出违规包,因为go-licenses不过滤,只搬运 - 使用场景:适合生成归档材料(如发布时打包
.licenses/目录),但不能当准入卡点 - 性能影响:对 100+ 依赖的项目,
go-licenses csv .耗时约 1–3 秒,可接受;但若加解析逻辑(比如匹配白名单),建议用缓存或预生成报告
CI 中必须阻断的三个检查点
光扫描没用,得让构建失败来倒逼开发者处理问题。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 在
go mod tidy后立即跑:go list -m all | awk '{print $1}' | xargs -I{} sh -c 'go-licenses csv {} 2>/dev/null | grep -q "License:.*AGPL\|SSPL\|GPLv3" && echo "BLOCK: {} violates license policy" && exit 1 || true' - 用
go mod graph检查传递依赖是否绕过根模块约束:go mod graph | grep -E "(evil|untrusted)/.*@v" | head -n1,有输出就阻断 - 禁止
replace被覆盖:在 CI 中加校验go mod edit -json | jq -r '.Replace[]?.New.Path' | grep -v "^github.com/trusted-fork/",非空则失败
白名单机制怎么落地才有效
别用“允许所有 MIT/Apache”这种模糊策略,要精确到具体模块和版本范围。
立即学习“go语言免费学习笔记(深入)”;
- 维护一个
allowed-licenses.json文件,形如:{"github.com/sirupsen/logrus": ["MIT"], "golang.org/x/net": ["BSD-3-Clause"]} - 写脚本比对
go list -m -json all输出和白名单,缺失项报错并提示升级路径(比如 “github.com/evil/pkgv1.2.0 需替换为github.com/trusted-fork/pkgv1.0.0”) - 注意
indirect依赖:go list -m -json all | jq 'select(.Indirect==true) | .Path'也要纳入白名单检查,很多漏洞来自间接依赖
最容易被忽略的坑:跨语言混合项目中的许可证穿透
Go 模块里调用了 Node.js 的 CLI 工具,或者用 cgo 链接了 GPL C 库——这时 Go 项目的许可证义务可能被外部依赖触发,go-licenses 完全看不见。
- 必须人工梳理所有非 Go 依赖(如 shell 脚本调用的二进制、Dockerfile 中
apt install的包、cgo 引入的 .a/.so) - 对含
cgo的模块,额外跑go list -f '{{if .CgoPkg}} {{.ImportPath}} {{end}}' all,逐个确认其 C 依赖许可证 - 多语言项目统一用
FOSSA或Snyk做全局扫描,go-licenses只负责 Go 生态部分

















