
Scans Java artifacts and source for Spring/Tomcat CVEs, compares vendor vs NVD CVSS scores, verifies exploitability conditions, and checks if fix versions exist on Maven Central.
覆盖 15 条 Spring / Tomcat 官方安全公告(2026-08-20 七条 · 2026-06-08 五条 · 2025-09-15 一条 · Tomcat 2026-08-25 两条)。
其中 8 条,接 NVD 的扫描器报 CRITICAL 9.1~9.8,而厂商官方评的是 LOW / MEDIUM。
另外 7 条两边评级完全一致 —— 差别只有一个:厂商有没有自己给 CVE 记录提交 CVSS 分数。
这个工具回答四个问题:
Spring 5.3 线的开源版停在 5.3.39,之后的版本官方只给商业客户。但在 Maven Central 上搜
spring-core:5.3.4*,能搜到一个:net.xdob.springframework(2025-03 发布,23 个构件)。
逐项实测(2026-09-14):
另外,它给 Spring MVC 的 PathResourceLookupFunction.isInvalidPath 留了一行调试输出
System.out.println("path = " + path) —— 用函数式路由 RouterFunctions.resources() 发静态资源时,每个请求路径都会打到标准输出。
== 字节码核验:版本号说修了,补丁在不在 ==
[对不上] spring-context 5.3.41
版本号 5.3.41 按官方公告应已修 CVE-2024-38820,但 DataBinder 里**找不到这个补丁**
[对不上] spring-webmvc 5.3.41
带 net.xdob 第三方重编指纹:PathResourceLookupFunction.isInvalidPath 里有一行 System.out.println("path = " + path)
判据不看版本号,看补丁本身:官方 38820 的补丁有两种字节码形态(6.1.14 起 toLowerCase(Locale.ROOT),
6.1.20 起 simpleMatchIgnoreCase)。发布前扫遍 Central 上 5.3.365.3.39、6.0.206.0.23、6.1.10~6.1.21 的全部开源版:
修复前每一版两种都没有,修复后每一版至少有一种,零误报。只对 5.3 / 6.0 / 6.1 三条线下结论,6.2 以后不判。
⚠️ 它的 38819 补丁和官方写法不一样(官方 6.1.13 / 6.1.14 加的是 isInvalidEncodedInputPath),两者是否等效,本工具没有验证。
唯一一条两边一致的,正是厂商自己给 NVD 提交了 CVSS 的那条。
成因不是谁在瞒谁:厂商没提交评分时,由 CISA-ADP 按最坏情况自动打分,而多数 SCA 工具取的是 NVD 那个数。
这不是 Bug,是两套评分体系各自的口径 —— 但你的应急流程是按 9.8 走的。
实测(带阳性对照,每次生成规则表时重跑):
spring-web 6.2.19 = 200 ← 上一版在 Central 上
spring-web 6.2.20 = 404 ← 官方叫你升的这个,没有
spring-security-core 6.5.11 = 200
spring-security-core 6.5.12 = 404
官方的 Fix version 表里,这些版本标着 Enterprise Support Only —— 只给买了商业支持的客户。
所以如果你真的中了,你的选择是:跨大版本升级,或者买商业支持。
java -jar spring-cvss-check.jar <jar|war|目录>... [--src <源码目录>]
# 最常见:扫构建产物定版本,扫源码定触发条件
java -jar spring-cvss-check.jar target/ --src src/main
# 只有一个 fat jar / war 也行
java -jar spring-cvss-check.jar app.war
需要 JDK 17+。运行时零依赖,不联网。
退出码:0 版本没命中(且每个文件都真的读进去了) · 2 版本命中但没找到触发条件 · 3 触发条件也成立 · 4 有文件读不动(见文末「退出码」一节)。
== 扫到的版本 ==
Spring Framework 6.2.19 .../spring-core-6.2.19.jar
Spring Security 6.5.11 .../spring-security-core-6.5.11.jar
Apache Tomcat 9.0.37 .../tomcat-embed-core-9.0.37.jar
== 版本命中 8 条 ==
-- CVE-2026-47884 Spring Framework Improper Path Limitation in XsltView
产品 Spring Framework 受影响 6.2.0 - 6.2.19
[差异] 官方 **MEDIUM** <-> NVD **CRITICAL 9.8**
NVD 上那个分不是厂商给的,是 CISA-ADP 打的。
触发条件 仅当用了 XsltView,且存在会走视图渲染的 "/**" 映射,且视图名没有显式指定
[命中] 在你的代码/配置里找到了:
src/main/java/demo/ReportView.java <- XsltView(第 2 行)
[升不了] 官方叫你升 6.2.20 —— **Maven Central 上没有这个版本(404)**,官方标注 Enterprise Support Only
== 汇总 ==
你的扫描器可能把这 8 条里的 7 条报成 CRITICAL;
而**厂商官方**评的是:3 条 LOW / 4 条 MEDIUM / 1 条 CRITICAL。
这 8 条里,6 条未在你的源码里找到触发条件,2 条找到了。
[!] 其中 7 条,官方给的修复版**在 Maven Central 上根本不存在**
上面那张表容易让人以为「NVD 总是乱打分」。不是的。 同一批数据里还有 7 条, 官方评级和 NVD 评级完全一致:
两个方向都没有反例,这是本工具唯一敢下的因果判断:
[email protected](厂商自己提交的)CISA-ADP(厂商没提交,由第三方按最坏情况自动打)生成脚本用两条断言分别盯住这两个方向(ASSERT4 / ASSERT7),任一出现反例就拒绝出表。
🔑 为什么要验两个方向:「一致的都是厂商自评」这一条,并不排除「有些不一致的也是厂商自评」。 真出现那种条目,因果就有了反例,而只验一个方向的话整表照样生成、照样全绿。 单向断言证明不了因果。
spring-core-5.3.39.jar 跑,会命中 11 条,而这 11 条官方给的修复版
在公开 Maven Central 上一个都不存在(5.3.45 / 5.3.49 / 5.3.50,全标 Enterprise Support Only)。判定表由 tools/gen_rules.py 从四个一手源生成,不手抄:
重跑一次就是重新复核:
python tools/gen_rules.py
脚本里有六条断言,不过就拒绝出表,其中三条直接盯着本工具的主张:
ASSERT2 官方⟷NVD 不一致的条数 —— 归零就说明主张失效了,当场停下ASSERT3 Central 探测带阳性对照 —— 对照不过,那一批 404 全部作废ASSERT4 一致的那条,评分来源必须是厂商自评 —— 这是「成因」的唯一证据Apache-2.0
🔴 4 是 2026-09-10 加的,加它的理由值得说清楚。
在此之前本工具的失效更彻底:内部两处 catch (IOException ignored) 把异常吞掉,
而「扫了几个」的计数加在打开归档之前 —— 于是一个根本打不开的 jar 会让它输出
「扫了 1 个 jar/war,没找到 Spring Framework / Spring Security / Tomcat 的版本」并返回 0。
它连自己没读进去都不知道:既没留痕,也没退出码,「看见了」被当成了「读进去了」。
现在这两处都会留痕并计数,报告也会写明「其中 N 个没能读进去,真正读到的只有 M 个」。
「我读不动它」和「你是安全的」必须是两句话。
⚠️ 合法的空 jar 不算读不动(一条 22 字节的 EOCD 记录是一个真的空 zip),不会触发 4 ——
假告警多了,真告警就没人看。
🔴 发现真问题时不降级:本工具原有的各档退出码优先于 4,4 只在
「没有需要你动作的发现,但有文件没能读进去」时出现。
其余退出码的含义见上文各判定档位与用法说明。
| 项 | 读数 | 怎么核 |
|---|
| 来源 | POM 的 scm 指向 github.com/dibyang/spring-framework(官方仓库的 fork),developers 仍写官方维护者 | 打开 Central 上的 .pom |
| 源码 | 官方 5.3.x 分支头就是 v5.3.39;fork 在其上 6 个提交,只有 1 个改 Java:fix CVE-2024-38819 | gh api repos/spring-projects/spring-framework/compare/5.3.x...dibyang:spring-framework:5.3.x |
| 字节码 | spring-core 978 个类、spring-context 892 个类与官方 5.3.39 逐字节相同;spring-webmvc / spring-webflux 各只差 1 个类 | 两个 jar 逐类 SHA-256 |
| 官方 5.3.41 修了什么 | CVE-2024-38819 和 CVE-2024-38820 两条 | spring.io/security/cve-2024-38819、-38820 |
| 这个包修了什么 | 只有 38819。38820 的补丁在 DataBinder,而它的 DataBinder 与 5.3.39 一模一样 | 本工具的字节码核验 |
| CVE | 组件 | 厂商官方 | NVD | NVD 上的分谁给的 |
|---|
| CVE-2026-59313 | Spring Framework | LOW | 9.8 CRITICAL | CISA-ADP |
| CVE-2026-47890 | Spring Framework | LOW | 9.8 CRITICAL | CISA-ADP |
| CVE-2026-59283 | Spring Framework | MEDIUM | 9.1 CRITICAL | CISA-ADP |
| CVE-2026-47891 | Spring Framework | MEDIUM | 9.8 CRITICAL | CISA-ADP |
| CVE-2026-47884 | Spring Framework | MEDIUM | 9.8 CRITICAL | CISA-ADP |
| CVE-2026-47892 | Spring Framework | MEDIUM | 9.8 CRITICAL | CISA-ADP |
| CVE-2026-65637 | Apache Tomcat | Moderate | 9.8 CRITICAL | CISA-ADP |
| CVE-2026-65905 | Apache Tomcat | Low | 9.8 CRITICAL | CISA-ADP |
| CVE-2026-59270 | Spring Security | CRITICAL | 9.4 CRITICAL | 厂商自评 ✅ 一致 |
| CVE | 组件 | 厂商官方 | NVD | NVD 上的分谁给的 |
|---|
| CVE-2026-59270 | Spring Security | CRITICAL | 9.4 CRITICAL | 厂商自评 |
| CVE-2026-41843 | Spring Framework | MEDIUM | 5.9 MEDIUM | 厂商自评 |
| CVE-2026-41844 | Spring Framework | MEDIUM | 4.2 MEDIUM | 厂商自评 |
| CVE-2026-41846 | Spring Framework | MEDIUM | 5.9 MEDIUM | 厂商自评 |
| CVE-2026-41853 | Spring Framework | MEDIUM | 5.3 MEDIUM | 厂商自评 |
| CVE-2026-41848 | Spring Framework | LOW | 3.7 LOW | 厂商自评 |
| CVE-2025-41249 | Spring Framework | HIGH | 7.5 HIGH | 厂商自评 |
| 源 | 拿什么 |
|---|
| A | spring.io/security/<cve> | 官方评级 / 影响区间 / 修复版(含 OSS ⟷ Enterprise 标记) |
| B | tomcat.apache.org/security-{9,10,11}.html | 同上 |
| C | NVD REST API | NVD 评级 与评分来源 |
| D | repo1.maven.org(HEAD) | 官方叫你升的版本,Central 上到底有没有 |
| 码 | 含义 |
|---|
0 | 扫完了,并且每个文件都真的读进去了,没有需要你动作的发现 |
4 | 有文件读不动 —— 不是 zip、内容截断、或读取失败 |
2 | (v0.3.0 起也包括)版本号落在所有公告区间外,但字节码核验发现「版本号说修了、补丁不在」 —— 那句 [OK] 是被版本号骗出来的,不许配 0 |