
Checker offline per CVE-2026-40477 / CVE-2026-41901 di Thymeleaf — ti dice a quale delle due vulnerabilità SSTI con CVSS 9.0 sei esposto e se la tua linea di versione dispone di una correzione (3.0.x: non la ha).
Strumento di verifica offline per CVE-2026-40477 / CVE-2026-41901 di Thymeleaf —— zero dipendenze, singolo jar, senza connessione di rete.
Due bypass SSTI con CVSS 9.0, con due versioni di correzione rilasciate nell'arco di nove giorni. Prima risponde a «quali di queste mi riguardano», poi risponde alla domanda più scomoda:
Su questa linea di versione, esiste una versione corretta a cui aggiornare?
Per 3.1.x, sì (basta aggiornare a 3.1.5.RELEASE).
Per 3.0.x e precedenti, no —— e quella è proprio la fascia con la maggiore base installata.
L'intervallo interessato delle due advisory, in OSV, è in entrambi i casi introduced: 0:
curl -s https://api.osv.dev/v1/vulns/GHSA-r4v4-5mwr-2fwr | jq '.affected[0].ranges'
# [{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.1.4.RELEASE"}]}]
也就是说 3.0 线整条在内,逐版本清单里 3.0.15.RELEASE 一直到 1.0.0 全都列着。
而 Maven Central 上 3.0 线止于 3.0.15.RELEASE,之后再无发布:
curl -s https://repo1.maven.org/maven2/org/thymeleaf/thymeleaf/maven-metadata.xml \
| grep -o '<version>3\.0\.[^<]*</version>' | tail -1
# <version>3.0.15.RELEASE</version>
Le versioni corrette di entrambe le CVE sono sulla linea 3.1. Quindi gli utenti 3.0.x non hanno l'opzione «cambiare il numero di versione»,
ma solo la migrazione attraverso la linea di versione maggiore — e la 3.1 è una modifica incompatibile esplicitamente elencata dalla documentazione ufficiale (copiata voce per voce dalla sezione 3.1.0.M1 del ChangeLog.txt ufficiale):
- Removed web-API based expression security objects (#request, #response, #session, #servletContext).
- Removed support for Spring 3.x and Spring 4.x.
- Set minimum JDK compatibility level to JDK 8 project-wide (JDK 17 for thymeleaf-spring6).
模板里写过 ${#request.…} / ${#session.…} 的,升上去会直接报错,这些取值得改由 Controller 放进 Model。
这是一次要排期的迁移,不是改个版本号。
deps.dev 的依赖数(2026-09-04 直读,任何人可复算):
| 版本 | 依赖数 | 中哪几条 | 能同线升吗 |
|---|---|---|---|
| 3.0.11.RELEASE | 1124 | 两条 | ❌ 3.0 线无修复版 |
| 3.0.15.RELEASE | 678 | 两条 | ❌ 同上,且它已是该线终点 |
| 3.0.12.RELEASE | 641 | 两条 | ❌ |
| 3.1.3.RELEASE | 826 | 两条 | ✅ 升 3.1.5 |
| 3.1.2.RELEASE | 677 | 两条 | ✅ |
| 3.1.4.RELEASE | 70 | 只中 CVE-2026-41901 | ✅ |
| 3.1.5.RELEASE | 590 | — | 已安全 |
装机面最大的单个版本是 3.0.11.RELEASE,比 3.1 线最高的 3.1.3 还高 36%,而它没有同线出路。
java -jar thymeleaf-check.jar <目录或 jar/war 路径> [--utf8|--gbk]
$ java -jar thymeleaf-check.jar ./target
[CRITICAL] ./target/myapp.war :: BOOT-INF/lib/thymeleaf-3.0.11.RELEASE.jar
构件:thymeleaf 版本 3.0.11.RELEASE(3.0 线,依据: MANIFEST(Implementation-Version))
命中: CVE-2026-40477 + CVE-2026-41901
受影响,且 3.0 线在 Maven Central 上没有任何修复版(该线止于 3.0.15.RELEASE)。
Codice di uscita: 2 = esiste una linea di versione contenente il componente senza una versione corretta sulla stessa linea · 1 = interessato ma aggiornabile sulla stessa linea / non determinabile · 0 = non rilevato · 3 = errore di utilizzo o di percorso.
Può essere collegato direttamente alla CI.
Scansiona gli artefatti di build (target/*.jar, *.war), non le directory del codice sorgente.
Nella stragrande maggioranza dei casi Thymeleaf viene introdotto transitivamente da spring-boot-starter-thymeleaf, e lo starter stesso non porta con sé una versione —
nel pom non è assolutamente visibile il numero di versione di Thymeleaf. In questo caso lo strumento dichiara esplicitamente «impossibile determinarlo leggendo il pom», invece di considerarlo inesistente.
Tre criteri di identificazione, in ordine di affidabilità:
Implementation-Version nel MANIFEST — riconoscibile anche se il jar è stato rinominato da strumenti di repackaging.
(Nel jar ufficiale di Thymeleaf, sotto META-INF/ ci sono solo MANIFEST.MF, nessuna directory maven/, quindi per esso questo è il criterio più solido.)pom.propertiesIn un fat jar / war, BOOT-INF/lib/ e WEB-INF/lib/ vengono scompattati e analizzati uno per uno, e viene letto anche il MANIFEST dei jar incorporati.
CVE-2026-40477 | CVE-2026-41901 | |
|---|---|---|
| GHSA | GHSA-r4v4-5mwr-2fwr | GHSA-c9ph-gxww-7744 |
| CVSS | 9.0 | 9.0 |
| Affected | <= 3.1.3.RELEASE | <= 3.1.4.RELEASE |
| Fixed | 3.1.4.RELEASE | 3.1.5.RELEASE |
| Disclosed | 2026-04-15 | 2026-05-04 |
| Official statement | "fails to properly restrict the scope of accessible objects" | "fails to properly neutralize specific constructs", occurring in a sandboxed (restricted) context |
L'intervallo interessato della seconda include 3.1.4, quindi chi ha aggiornato a 3.1.4 seguendo la prima advisory ricade nella seconda.
Ma non leggerlo come «l'ufficiale non ha corretto del tutto» — le due advisory descrivono meccanismi diversi,
3.1.4 (2026-04-12) e 3.1.5 (2026-04-21) sono due versioni ufficiali distanti nove giorni,
e nessun testo ufficiale afferma che la correzione precedente fosse incompleta. Questo strumento si limita a mettere i fatti uno accanto all'altro, senza trarre questa conclusione al posto tuo.
Ogni numero in RuleTable.java può essere ricontrollato:
python tools/gen_rules.py --show
它拿两个一手源核对判定表 —— OSV 的受影响区间与逐版本清单、Maven Central 的
maven-metadata.xml —— 对不上就非零退出。
三条约定:
mvn package # JDK 17;target/thymeleaf-check-0.1.0.jar
mvn test # 19 个用例
L'assenza di dipendenze a runtime è intenzionale: lo strumento di analisi deve poter essere eseguito direttamente in qualsiasi ambiente.
Apache-2.0
| Codice | Significato |
|---|---|
0 | Scansione completata, e ogni file è stato effettivamente letto, senza rilevamenti che richiedano un tuo intervento |
4 | Ci sono file che non è stato possibile leggere — non è uno zip, contenuto troncato, o lettura fallita |
🔴 Il codice 4 è stato aggiunto il 2026-09-09, e il motivo vale la pena di essere spiegato.
Prima di allora, i file non leggibili lasciavano solo una riga di avviso nell'output, mentre il codice di uscita restava 0 —
la traccia è per le persone, ma CI e script guardano il codice di uscita, quindi «non sono riuscito a leggerlo» in automazione equivale a «superato».
Un jar corrotto, cifrato o scaricato in modo incompleto diventa silenziosamente un «nessun problema rilevato».
«Non riesco a leggerlo» e «sei al sicuro» devono essere due frasi distinte.
⚠️ Un jar vuoto legittimo non conta come non leggibile (un record EOCD di 22 byte è uno zip vuoto reale), e non attiva 4 —
troppi falsi allarmi e nessuno guarderà più quelli veri.
Per il significato degli altri codici di uscita, vedere le varie soglie di verdetto e le note d'uso sopra.