
查出 Spring Boot 内嵌 Tomcat 的真实版本(pom 里没有),并对每条 2026 年 CVE 同时给出 ASF 官方评级与 GitHub 评级、触发条件、以及这条会不会进 Dependabot 告警 CVE-2026-41293
查出你实际装的 Tomcat 版本,并对每条 2026 年的 CVE 同时给出两套评级和触发条件。
零依赖单 jar,离线跑,不联网、不上传任何东西。
一、Spring Boot 项目的内嵌 Tomcat 版本,你的 pom 里没有。
它由 spring-boot-starter-parent 的 tomcat.version 属性管着,你在 pom 里只看得到
spring-boot-starter-web。所以本工具扫构建产物(jar / fat jar / 安装目录),不读 pom。
二、有些条目进不了 Dependabot 的告警。
截至 2026-10-13,Tomcat 官方安全页上 2026 年的 CVE 有 51 条,其中 34 条因为下面两种机制不会出现在 Spring Boot 项目的告警里(unreviewed 29 条,坐标对不上 5 条):
| 机制 | 说明 |
|---|---|
advisory 是 unreviewed | GitHub 只用 reviewed 的 advisory 做 Dependabot 告警。unreviewed 是 NVD 自动导入、尚未人工确认受影响包的正常流程状态 |
| 坐标对不上 | advisory 挂在 org.apache.tomcat:tomcat-coyote 等坐标下,而 Spring Boot 的依赖树里只有 tomcat-embed-core / -el / -websocket |
实际效果(2026-10-13 用 Central 上的真 jar 实跑):Spring Boot 3.5.16 自带的
tomcat-embed-core 10.1.55 + tomcat-embed-websocket 10.1.55,本工具报出 25 条命中,
其中 22 条进不了 Dependabot 告警;默认配置即暴露的是 1 条,其余都带前提,请逐条看触发条件。
这些数字会变:GitHub 随时可能把
unreviewed的条目转成reviewed。 重新生成判定表(见下文)就能拿到当天的状态。
三、两套评级体系口径不同,而你只看得到其中一套。
| ASF / Tomcat 官方 | GitHub / NVD | |
|---|---|---|
| 分级 | Low / Moderate / Important / Critical | CVSS 数值 |
| 依据 | 默认配置下的实际可利用性 | 向量机械计算,不看你开没开那个功能 |
| 在哪看 | tomcat.apache.org/security-9.html | Dependabot 直接推送给你 |
51 条里有 23 条两套评级差 2 级以上(另有 9 条 GitHub 尚未给出评级,不计入),最极端的 CVE-2026-41293:官方 Low,GitHub critical CVSS 9.8。
这不是谁报错了。 两套体系测量的本来就是不同的东西。 但要判断「该不该现在就升」,你需要同时看到它们 —— 以及你有没有开那个功能。
java -jar tomcat-check.jar <jar 或目录> ...
--all 连「不适用」的条目也列出来
--utf8 Windows 控制台中文乱码时加这个
# Spring Boot fat jar
java -jar tomcat-check.jar target/my-app.jar
# 独立安装的 Tomcat
java -jar tomcat-check.jar /opt/tomcat
# 整个依赖目录
java -jar tomcat-check.jar target/lib
需要 Java 17+。
每条命中给出:两套评级、触发条件、官方描述原文、以及这条会不会出现在你的 Dependabot 告警里。 最后给一个能覆盖全部命中条目的升级目标。
tomcat-embed-websocket 时才算命中。 内嵌形态下 WebSocket 实现
不在 tomcat-embed-core 里;你的应用如果把它排除掉了,这几条会列在「不适用」里并写明原因。unreviewed 是正常流程状态。官方在 9.0.121 / 10.1.58 / 11.0.25 修了一批 10 条,在 9.0.122 / 10.1.60 / 11.0.26 又修了一批 12 条。 其中一条值得单说:
CVE-2026-86350(官方 Important)是修 CVE-2026-41293 时引入的回归。 它的受影响区间是 9.0.118–9.0.121、10.1.55–10.1.59、11.0.22–11.0.25 —— 也就是 41293 的修复版自己。 为了修 41293 升到这些版本的,需要再升一次。触发条件是启用了 HTTP/2。
这两批 22 条里,19 条在 GitHub 上仍是 unreviewed(2026-10-13 读数)。
tools/gen_rules.py 从两个一手源生成 CveTable.java,一行都不手抄:
https://tomcat.apache.org/security-{9,10,11}.html —— 官方评级、标题、描述原文、影响区间type、受影响 Maven 坐标生成过程带 7 条断言,任一不满足就中止、不写文件。其中三条值得单说:
<strong>Moderate: The fix for <a>CVE-2025-66614</a> was incomplete</strong> <a>CVE-2026-32990</a>。
在整块里找「第一个 CVE」会取到标题里嵌的那个旧编号,后果不是少一条,是张冠李戴:
一条 CVE 挂上了另一条的标题和描述,而生成、测试、真实构件复验全部照常通过。Affects: 行紧挨着下一条 CVE 的标题,
极容易配对到错误的那一条 —— 错了会得到一张「看起来完全正常但整体错位一格」的表。
用 GitHub 的 first_patched_version 交叉校验,开发时真的靠它抓到过一次错位。tomcat-embed-* 里。
排除依据必须是实测,不能是「我记得」。重新生成:
python tools/gen_rules.py
mvn clean package # → target/tomcat-check.jar
39 个测试。运行时零依赖,JUnit 仅测试期。
MIT
这个工具回答的是「我中没中」。下面这些它答不了,可以找我做:
📮 [email protected] —— 说清情况,我 24 小时内给你一页书面答复: 能不能做、难在哪、大概多久。这一步免费,也不用你先承诺什么。
| 码 | 含义 |
|---|---|
0 | 扫完了,并且每个文件都真的读进去了,没有需要你动作的发现 |
4 | 有文件读不动 —— 不是 zip、内容截断、或读取失败 |
🔴 4 是 2026-09-09 加的,加它的理由值得说清楚。
在此之前,读不动的文件只在输出里留一行提示,而退出码照旧是 0 ——
留痕是给人看的,CI 和脚本看的是退出码,于是「我没能读它」在自动化里等于「通过」。
一个损坏、加密或下载不全的 jar,会安静地变成一句「没发现问题」。
「我读不动它」和「你是安全的」必须是两句话。
⚠️ 合法的空 jar 不算读不动(一条 22 字节的 EOCD 记录是一个真的空 zip),不会触发 4 ——
假告警多了,真告警就没人看。
其余退出码的含义见上文各判定档位与用法说明。