go list -m all是最轻量可靠的完整依赖查看方式,输出当前项目所有直接及间接依赖的精确版本列表,含// indirect标记项,并支持-json结构化解析与grep筛选。

用 go list -m all 查清完整依赖树
直接运行 go list -m all 是最轻量、最可靠的起点。它不依赖缓存或本地构建状态,只读取 go.mod 和模块元数据,输出当前项目所有直接+传递依赖的精确版本列表。
常见错误是只看 go.mod 里的 require 块——那只是“声明”,不是“实际加载”。真正参与编译的是 go list -m all 输出的整个树,包括被间接拉进来的 golang.org/x/net、google.golang.org/protobuf 等底层包。
- 加
-json参数可结构化解析:go list -m -json all - 配合
grep快速筛出高危路径:go list -m all | grep "golang.org/x/" - 注意输出中带
// indirect标记的项——它们就是典型的传递性依赖,未被你显式 require,但已被某个依赖拖进来
检查 go.sum 中是否存在未声明却已校验的模块
go.sum 是 Go 模块的“事实清单”:只要某模块出现在这里,就说明它已被下载、校验并参与构建。但它的存在不意味着你在 go.mod 里写了 require ——很多条目是纯传递引入的。
打开 go.sum,找那些没在 go.mod 的 require 区块出现的模块名。例如:
golang.org/x/text v0.14.0 h1:ScX5w+eC03yuyFQ2Zxq2cS8zP7a6kL5jvO89oWdYIbA=
如果 go.mod 里没写这行 require golang.org/x/text v0.14.0,那它就是由某个依赖(比如 rsc.io/quote)带进来的传递依赖。
- 这类模块一旦被上游更新或弃用,你的构建可能突然失败,而你完全没意识到自己“用了它”
- 安全扫描工具(如
govulncheck)会扫描go.sum全量条目,所以隐藏的传递依赖才是漏洞潜伏区 - 不要手动删
go.sum行——用go mod tidy后重新生成才可靠
用 go mod graph 定位谁在偷偷拉入危险依赖
go mod graph 输出的是有向边列表,格式为 A B,表示 “A 依赖 B”。它能直观暴露“谁把谁带进来的”关系链,比纯列表更利于溯源。
典型风险场景:你只引入了 github.com/gin-gonic/gin,但它内部依赖了 golang.org/x/crypto 的某个旧版,而该版本存在 CVE-2023-XXXXX。靠 go list 只能看到“有”,靠 graph 才能确认“是 gin 拉的”。
- 导出后用
awk或脚本快速过滤:go mod graph | awk '$2 ~ /golang.org\/x\// {print $0}' - 发现某低版本库被多个路径引入?说明存在版本冲突风险,需用
replace统一锚定 - 若输出中出现重复模块名但不同版本(如
example.com/lib v1.2.0和example.com/lib v1.5.0),这就是传递依赖失控的明确信号
警惕 indirect 依赖在 CI 中的静默升级
go.mod 里带 // indirect 注释的 require 行,是 Go 工具链自动添加的“兜底声明”。它本身不触发下载,但会在 go mod tidy 时被保留——而这个行为在 CI 流程中容易被忽略。
问题在于:本地开发时你可能刚执行过 tidy,indirect 行是稳定的;但 CI 每次都是干净环境,若 go.mod 被修改(比如某依赖发布新版),tidy 可能悄悄升级一个 indirect 项,导致构建结果与本地不一致。
- CI 中务必在构建前加一步:
go mod tidy -v && git diff --exit-code go.mod go.sum,防止意外变更 - 对关键基础设施依赖(如
crypto/tls相关模块),建议主动提升为显式require并锁定版本,别让它躲在indirect后面 - Go 1.21+ 支持
go mod vendor后校验 vendor 目录完整性,这是隔离传递依赖波动的最硬手段
go.sum 和 go mod graph 的组合,才是定位这类隐性风险的最小可行闭环。











