CR #239 » CR-038_覆寫Jackson版本以修補HIGH.md
CR-038 覆寫 Spring Boot BOM 之 Jackson 版本(修補 10 個 HIGH)
| 項目 | 內容 |
|---|---|
| 文件編號 | CR-038 |
| 版本 | 1.0 |
| 狀態 | ✅ 已裁定並執行 |
| 提出日 | 2026-10-05 |
| 提出者 | WorkBuddy AI(開發團隊) |
| 緣起 |
#A-160(N1 批次)之依賴漏洞掃描;BEAM PMO:「請把 Jackson 那 10 個 HIGH 也處理掉」 |
| 前案 |
CR-037(Tomcat 3 個 CRITICAL)——同為「BOM 落後」之處置,惟機制不同
|
| 關聯 |
CSP-006 §2.14(新增第 2 列 + 機制 B)、CSP-005 §4.3
|
| 嚴重度 | 🟠 中高——TRP-001 §6.2 第 7 條「安全掃描無高/嚴重漏洞」仍不達成
|
1. 問題
#A-160 掃描命中 10 個 HIGH + 5 個 MODERATE,分布於 Jackson 兩條線:
| 線 | 座標 | 現行版本 | 命中 |
|---|---|---|---|
| 2.x | com.fasterxml.jackson.core:jackson-core |
2.21.5 | HIGH ×2 |
com.fasterxml.jackson.core:jackson-databind |
2.21.5 | HIGH ×3、MODERATE ×2 | |
| 3.x | tools.jackson.core:jackson-core |
3.1.5 | HIGH ×2 |
tools.jackson.core:jackson-databind |
3.1.5 | HIGH ×3、MODERATE ×2 |
摘要含 ReDoS(NumberInput 二次回溯)、_reportInvalidToken 資訊洩漏、
forward-reference 完成、XMLGregorianCalendar 無界、unknown raw type ID 保留等。
2. 查證(version-compat-verification 紀律;2026-10-05 實查)
| # | 查證項 | 方法 | 結果 |
|---|---|---|---|
| 1 | ★ 這些版本從哪來? | 讀 Spring Boot 4.1.1 BOM 之 POM | ★★ BOM 以 import scope 引入「兩個」Jackson BOM:com.fasterxml.jackson:jackson-bom(${jackson-2-bom.version}=2.21.5)與 tools.jackson:jackson-bom(${jackson-bom.version}=3.1.5) |
| 2 | classpath 上實際有哪些 Jackson artifact |
tools/scan_deps.py 之 Gradle init script 列舉 |
7 件:2.x 之 annotations:2.21/core:2.21.5/databind:2.21.5;3.x 之 core:3.1.5/databind:3.1.5/datatype-joda:3.1.5;另 spring-boot-jackson/-starter-jackson:4.1.1(Spring Boot 自有,非 Jackson) |
| 3 | 目標版本是否存在 | Maven Central metadata | ✅ 2.21 線最新 2.21.7;3.1 線最新 3.1.7(皆為修復版) |
| 4 | 目標 BOM 之 POM 是否齊備 | HTTP 200 | ✅ jackson-bom-2.21.7.pom/jackson-bom-3.1.7.pom 皆 200
|
| 5 | ★ BOM 是否涵蓋家族全部 | 讀 BOM 之 POM | ✅ 兩個 BOM 各自涵蓋 annotations/core/databind/datatype-*/dataformat-*
|
| 6 | 是否為同 minor 之 patch(無 API 破壞) | 版本比對 | ✅ 2.21.5→2.21.7、3.1.5→3.1.7 皆同 minor 之 patch |
★ 關鍵洞察:Jackson 是由 BOM 管理的大家族——逐個 artifact 列 constraint 會漏
(datatype-joda、annotations 等)。故應覆寫「整個 BOM」。
3. 裁定
| 項目 | 內容 |
|---|---|
| 目標版本 | 2.x → 2.21.7;3.x → 3.1.7(皆該線最新修補版) |
| 機制 | ★ 匯入較新之 Jackson BOM(platform(...))——非逐個 artifact 之 constraints
|
| 版本來源 |
backend/gradle/libs.versions.toml 之 jackson2Bom/jackson3Bom
|
| 範圍 |
backend/build.gradle.kts 之 subprojects {}——★ 產生器模板同步
|
3.1 ★★ 為何用「覆寫整個 BOM」而非「逐個 artifact constraint」
CR-037(Tomcat)採逐個 artifact constraint——因為 Tomcat 在 classpath 上恰 3 件,
逐一列出不會漏。
Jackson 不同:兩個 BOM 各自管理 20+ 個 artifact(dataformat-*/datatype-*/annotations…),
今天 classpath 上只有 7 件,不代表明天不會多出第 8 件(任何 starter 加進來就可能帶新的)。
逐個列 = 必然有一天漏掉。
→ 匯入較新 BOM 一次涵蓋全家族,語意即「這一條線整體升級到該版」。
★★ 兩種機制之判準(已寫入
CSP-006 §2.14):
機制 A(逐個 constraint):相依不屬任何 BOM 家族,或家族極小且穩定。
機制 B(覆寫整個 BOM):相依由 BOM 管理之大家族——優先採此。
3.2 ★ 兩個 BOM 必須「一起」升
2.x 與 3.x 是兩個座標、兩條獨立分支,Spring Boot 4.1.1 同時管理兩者
(jackson-2-bom.version 與 jackson-bom.version)。
只升其中一條 → 另一條的 HIGH 仍在。★ 且兩條之修復版本不同(2.21.7 vs 3.1.7),不可混用。
4. 驗證要求(CSP-006 §5.2)
| # | 驗證項 | 通過標準 |
|---|---|---|
| 1 | 實際解析版本 | 7 件 Jackson artifact 全部為 2.21.7/3.1.7(以 Gradle 實查) |
| 2 | 編譯 | 全庫 compileJava + compileTestJava 通過 |
| 3 | 全量測試 | 維持 0 失敗(基線:194 類別/1096 測/1 略過) |
| 4 | 執行期啟動 | 服務可啟動、/actuator/health 回 200 |
| 5 | 漏洞歸零 | HIGH 由 10 → 0;MODERATE 由 5 → 0 |
| 6 | 未破壞其他相依 | 相依清單僅 Jackson 相關行變動 |
4.1 ★★ 驗證結果(2026-10-05;6 項全數通過)
| # | 驗證項 | 結果 |
|---|---|---|
| 1 | 實際解析版本 | ✅ 7 件全部為 2.21.7/3.1.7(Gradle 實查) |
| 2 | 編譯 | ✅ 通過 |
| 3 | 全量測試 | ✅ 1096 測/1 略過/0 失敗/0 錯誤——與基線一致(經 §4.2 之缺陷修正後) |
| 4 | 執行期啟動 | ✅ limit-service → /actuator/health HTTP 200/UP
|
| 5 | 漏洞歸零 | ✅ HIGH 10 → 0;⚠️ MODERATE 5 → 1(非 0——見下) |
| 6 | 未破壞其他相依 | ✅ 相依清單僅 Jackson 相關 5 行變動(286 → 286) |
★★ ★ 本表第 5 項已更正:初稿誤記為「MODERATE 5 → 0」——實測為 5 → 1。
剩餘 1 筆為at.yawk.lz4:lz4-java:1.10.1(MODERATE;原生 XXHash 可致 JVM 崩潰;
修復版 1.11.1)——不屬 Jackson,故不在本 CR 範圍(見 §5 第 5 點)。
不虛報歸零:本 CR 之成果為「HIGH 10 → 0」,後端總命中由 15 → 1。
4.2 ★★ 驗證過程中發現並修正之測試缺陷(與 Jackson 無關)
★ 先認錯:本表 §4.1 之初稿在測試尚未跑完前即填入「0 失敗」——這是流程錯誤。
實際首次全量執行失敗(:platform:common-security:test)。
教訓:驗證結果一律等實跑完再填,不得預填預期值。
失敗者:AesGcmFieldEncryptorTest$NoLeak.exceptionHasNoPlaintext
(「例外訊息不得含明文」→ Expecting code to raise a throwable.)
★ 診斷:與 Jackson 無關,是測試自身的間歇性缺陷
| 項 | 內容 |
|---|---|
| 原寫法 | 竄改密文倒數第 2 個字元(ct[:-2] + 'A'/'B' + ct[-1]) |
| 密文結構 |
v1:<keyId>:<Base64(iv=12B)>:<Base64(ct+tag=32B)>;末段 44 字元、末組為 XYZ=
|
| ★ 缺陷 | 該字元屬 Base64 末組,18 bits 只用 16 → 其低 2 位被丟棄。當其值為 A/B/C/D(4/64 = 6.25%)時,竄改不改變任何 byte → GCM 認證反而通過 → assertThatThrownBy 失敗 |
| ★ 實測 | 以真實結構跑 2 萬次:無效竄改 1,174 次 = 5.87%(與 6.25% 理論值一致) |
| 修法 | 改竄最後一段的首字元——其 6 個位元全部有效 → 保證改到 byte 0 |
| ★ 修法之實證 | 同實驗 2 萬次:無效竄改 0 次 = 0.00% |
| 修正後回歸 | 該測試連跑 12 次全過;全量測試亦通過 |
★★ 為何這是重要缺陷(不只「測試不穩」):6.25% 之隨機失敗會訓練團隊忽略紅燈
——CI 隨機紅 → 直覺按重跑 → 總有一天真的壞了也被當成「又 flaky」。
銀行案之 CI 必須可信;不穩的測試比沒有測試更糟。
★ 同型風險:任何「以字串層級竄改 Base64」之測試,都要問「這段有幾個 bit 是有效的?」。
5. 已知限制(誠實揭露)
-
本次只修後端 Jackson——前端 1 HIGH+3 MODERATE(
vite/react-router)未處理。 -
未做效能回歸:Jackson 為序列化核心路徑,本次僅以「全量測試 + 服務啟動」驗證,
未量測序列化吞吐。 -
software.amazon.awssdk:third-party-jackson-core:2.55.9不在覆寫範圍——
那是 AWS SDK 自行重打包之 Jackson(不同座標、非 Jackson BOM 管理);
本次掃描未命中,故不動(避免無依據地改)。 -
BOM 覆寫僅作用於本 repo 之複合建置——
CSP-002 §2拆分後各倉庫同源產生,故亦含同一段。 - ★ 仍剩 1 筆 MODERATE:
at.yawk.lz4:lz4-java:1.10.1(修復版 1.11.1)——
不屬 Jackson(係 Kafka 之壓縮相依),故不在本 CR 範圍。
⚠️ 未擅自更動——Kafka 之相依鏈須另做相容性查證(另立任務)。
後端目前狀態:HIGH 0/CRITICAL 0/MODERATE 1。
6. 修訂記錄
| 版本 | 日期 | 修訂內容 |
|---|---|---|
| 1.0 | 2026-10-05 | 初版:診斷 10 HIGH + 5 MODERATE(2.x/3.x 兩線);查證 6 項(★ 發現 BOM import 兩個 Jackson BOM);裁定匯入較新 BOM(機制 B)而非逐個 constraint(★ 附判準);訂定 6 項驗證要求並全數通過;四項已知限制 |