截至 2026-05-10T17:04:50Z UTC,GitHub API 仍然把 Trivy 显示为一个活跃而且被广泛使用的项目:34,925 个 stars、356 个 forks、256 个 open issues,最近一次 push 时间是 2026-05-08T23:24:04Z。[5] 它当前最新的稳定版是 v0.70.0,发布时间为 2026-04-17。[4] 这组事实的重要性在于,2026 年再看 Trivy,最差的读法是“3 月之前是好扫描器,3 月之后就不可信了”。更准确的读法窄一些,也更硬一些:Trivy 依旧是覆盖面很广的安全扫描器,但 2026 年 3 月的事故把另一层边界完整暴露出来了,扫描覆盖面与发布信任面,本来就是两件不同的事。[1][3][4]
官方 advisory 把故障形态写得非常直白。2026 年 3 月 19 日,攻击者利用被攻破的凭证发布了恶意 Trivy v0.69.4,并把 aquasecurity/trivy-action 的 77 个版本标签里 76 个强行改写,又把 aquasecurity/setup-trivy 的 7 个标签全部替换成恶意提交。[1] 暴露窗口很短,却足够危险:恶意 trivy 发布大约持续 3 小时,trivy-action 大约 12 小时,setup-trivy 大约 4 小时,而 3 月 22 日 的恶意 Docker Hub 镜像 v0.69.5 与 v0.69.6 又持续了大约 10 小时。[1] 这正是它值得被写成一篇 OSS 事故读解的原因。问题不在于 Trivy 忽然不会查漏洞了,问题在于安全工具自己也站在发布渠道与 CI 引用路径里,而很多团队此前一直把这层信任面看得比实际更软。[1][2]
配图说明:题图使用 Itay Shakury 的真实 GitHub 头像,因为这篇文章的中心不在界面输出,而在维护与恢复程序。2026 年 3 月的关键问题,是谁掌握发布路径、哪些标签被修复、消费端在 CI 里到底钉住了什么。[2][7]
真正失守的是哪一层
官方 advisory 指向两层连在一起的失守。[1] 第一层是被攻破的凭证,让攻击者可以把恶意产物送进正常分发渠道。第二层是可变引用,把这次事故从“一份坏二进制”扩大成了“多个常用入口一起失守”。攻击者做的,不只是向某个边缘镜像站塞进一份有毒文件;他们动的是团队平时安装 Trivy、在 GitHub Actions 里接入 Trivy、以及把“足够新的”扫描行为带进流水线的那些标准入口。[1]
advisory 里的 root cause 说明尤其关键。它写得很清楚,3 月 19 日 这次攻击,是 2026 年 2 月下旬那一轮供应链事件的延续,因为凭证轮换不是原子式完成的:并不是所有 secrets 都在同一时刻被撤销,于是新一轮轮换期间又留下了外泄空间。[1] 这个细节会把事件的含义从“一把凭证泄露”推成“恢复程序本身也是产品边界的一部分”。安全扫描器和其他交付系统一样,遇到事故时,只要封堵不是一次收紧,发布流水线就仍然处在爆炸半径之内。
把这层失守与 Trivy 平常的产品形态并排放着看,反差就更清楚。项目 README 仍然把它定义成一种覆盖面很宽的扫描器:在目标侧,它可以扫描容器镜像、文件系统、远端 Git 仓库、虚拟机镜像与 Kubernetes;在问题侧,它可以识别带有 SBOM 视角的软件包、漏洞、配置错误、敏感信息与许可证问题。[3] 2026 年 3 月没有坏掉的是这套能力,真正出问题的是再往上一层,也就是团队到底信任哪一个二进制、哪一个容器镜像、哪一个 Action 引用。[1][3]
可变标签为什么才是最危险的陷阱
若把这次事件只读成一次“坏发布”,读法就太浅了。官方 advisory 和 GitHub 自己的 Actions 安全文档,指向的是同一个更尖锐的结论。[1][6] 在 GitHub Actions 里,只有把 action 钉到完整 commit SHA,引用才是不可变的。[6] 很多团队知道这条规则,却还是继续用版本标签,因为它可读、好记、看起来省事。Trivy 事件把这个“省事”真正翻译成了运维负债:一旦攻击者可以挪动标签,方便本身就会变成攻击路径。
因此,trivy-action 这一段事故,和恶意 v0.69.4 本体一样重要。[1] 一个运行在 CI 里的扫描器,常常贴着 registry credentials、cloud credentials、SARIF 上传 token,或者其他一旦外泄就够麻烦的 secrets。只要 action 引用可以被改写,“我们只是把它放在 CI 里跑”就不再像一句安全说明,它更像是在承认:这个扫描器确实坐在一台有权限的机器上。[1][6]
advisory 给出的修复版本号,把恢复路径写得很实:setup-trivy 升到 0.2.6,trivy-action 升到 0.35.0,恶意的独立 Trivy 发布则集中在 v0.69.4 这一版。[1] 这当然是一条立刻可用的检查清单。更长的一层结论却更严格:只要团队还在用浮动标签消费第三方 Actions,本质上就并非把不可变性交给了代码,而是把不可变性交给了一种 GitHub 已经明说并不不可变的引用格式。[6]
清理之后,项目为什么仍然有意义
若读解停在“安全工具被黑了,所以这个工具应该换掉”,这篇文章就没有站稳。Trivy 之所以在 2026 仍然有现实价值,恰恰因为很多团队确实需要一个扫描器,把容器镜像、文件系统、代码仓库、虚拟机与 Kubernetes 放进同一套操作词汇里,同时检查 CVE、配置错误、secret、许可证与包级清单。[3] v0.70.0 在 2026-04-17 发布,当前仓库活跃度也说明项目在 3 月事故后仍在公开推进。[4][5]
更合适的结论是,Trivy 需要被放进一套更紧的信任模型里,而不是被放进一种浪漫化的信任里。使用它,是因为扫描面宽、运维手感好;不要因为它被叫作“安全工具”,就默认它天然高于供应链纪律。2026 年 3 月这次事故,没有证明 Trivy 失去可用性,证明的是另一件事:扫描器本身就是供应链依赖,所以它也应当像其他高权限构建组件一样,被精确钉版本、验证来源,并放在隔离得更清楚的 runner 边界里。[1][3][6]
2026 年更安全的使用姿势
真正有用的运行模型并不复杂,关键在于字面执行。
- 把
aquasecurity/trivy-action和aquasecurity/setup-trivy都钉到完整 commit SHA,而不是会移动的标签,并确认这个 SHA 来自上游仓库本身。[1][6] - 若直接安装 Trivy 的独立二进制或镜像,就钉住明确版本,不要让高权限 CI 任务去拉未经审计的 “latest”。[1][4]
- 让扫描任务远离部署凭证、签名密钥与大范围 cloud secrets。扫描 job 不应同时拥有发布生产产物的权限。
- 把事故恢复视为依赖治理的一部分:若扫描器维护方要求重新钉版本、重新拉取、重新核验,就应当把发布路径本身视为仍在怀疑区内,直到这些动作全部完成。[1][2]
最后这一条最容易被跳过,因为它听上去像程序管理。可在这次事件里,程序管理就是边界本身。advisory 提供的,不只有版本层面的修复建议,还有审计线索、exfiltration artifacts 的搜索提示,以及把 Actions 钉到不可变 SHA 的明确提醒。[1] 也就是说,维护者自己也在告诉使用者:真正失守的,不只是代码内容,更是交付机制。
最适合的边界
Trivy 仍然最适合那些需要一套开源扫描器去覆盖多种制品类型、同时愿意自己承担版本钉住与 CI 卫生纪律的团队。[3][4] 若你的组织本来就有清楚的依赖接入流程,而且希望扫描能力贴着构建链条运行,它依旧是很好用的工具。
真正不合适的地方,出现在团队把“安全工具”误读成“上游已经替你解决了信任问题”。2026 年 3 月的事故说明,这种误读代价很高。若你需要更强的集中控制、被策展过的分发保证,或者更薄的自管 runner 暴露面,就必须在 Trivy 外层再补这些控制,或者去别处购买它们。Trivy 的扫描广度是真的,这一点并没有消失;只是扫描广度从来不是发布路径免疫力的同义词。[1][3][6]
来源
- GitHub Security Advisory,《Trivy ecosystem supply chain temporarily compromised》——用于事故窗口、受影响组件、修复版本、根因说明与缓解清单。
- Trivy GitHub Discussion #10425,《Trivy Security incident 2026-03-19》——用于维护者事故线程与后续运维更新。
- aquasecurity/trivy README——用于当前扫描目标范围与扫描类型。
- Trivy
v0.70.0release 页面——用于事故后的当前稳定版与发布时间。 aquasecurity/trivy的 GitHub API 快照——用于文章创建时的 stars、forks、open issues 与最近 push 时间。- GitHub Docs,《Secure use reference》——用于“只有完整 commit SHA 才是不可变 Action 引用”的安全规则。
- Itay Shakury 的 GitHub 主页——用于本文题图所用头像来源。