MatchRPG Server 全面測試 + JIT Blind Spot 漏洞分析¶
文檔資訊
- 分類: debugging
- 難度: advanced
- 預估閱讀時間: 8 分鐘
- 標籤:
go,security,testing,unit-test,e2e,smoke-test,race-condition,data-consistency,twirp,jit-analysis
摘要¶
對 MatchRPG Server 執行完整測試套件(Unit/E2E/Smoke 全部通過),並由獨立 agent 進行 JIT 盲點分析,發現 6 個 P1 + 5 個 P2 漏洞,涵蓋 race condition、data consistency、error handling 與安全性問題。
關鍵學習¶
-
mapError 必須涵蓋所有 domain error,否則會 fallthrough 為 generic internal error 而非有意義的 Twirp 錯誤碼
-
AdminRemoveItem 等需讀寫同一筆資料的操作,item read 必須在 transaction 內部,避免 stale snapshot
-
lastCleanupTime 等 package-level time.Time 跨 goroutine 存取需要 sync.Mutex 或 atomic.Value 保護
-
useInBattleItem 扣除背包道具必須與 UpdateItemUsageLog 同在一個 transaction,否則失敗後無法 rollback
-
GDPR soft-delete 與 deletion task 建立必須在同一個 DB transaction,否則產生孤立 soft-delete
-
sync.Once 建立的 gamedata index 在 hot-reload 後不會刷新,需改用 config observer 或版本檢查機制
-
mapError 回傳錯誤訊息應使用固定字串而非 err.Error(),避免 wrap error 洩漏內部細節
-
continueBattle 的 continue_count 應從 DB 讀取而非依賴 Redis 快取,避免 best-effort 更新造成計費錯誤
技術細節¶
JIT 分析 agent 掃描全專案 Go 程式碼,找出以下類別問題:
P1 (6個): 1. player_twirp.go:47 — mapError 缺少 ErrUnsupportedCurrency/ErrHeroNotOwned/ErrEmptyHeroIDs 2. player_inventory_service.go:649 — AdminRemoveItem 在 tx 外讀取 item.Quantity(stale snapshot) 3. player_inventory_service.go:470 — SellPrice.Parameter 未驗證 > 0,負值會扣除玩家金幣 4. player_cache_redis.go:81 — lastCleanupTime 全域變數無鎖同步(data race) 5. dungeon_service_items.go:33 — useInBattleItem 扣背包與 DB log 不在同一 tx 6. teach_service.go:582 — GiveGift CAS retry 可能重複扣道具
P2 (5個): 7. admin_twirp.go:294 — audit goroutine 持有 request context 參考 8. gdpr_service.go:204 — soft-delete 與 deletion task 非 atomic 9. player_twirp.go:61 — mapError 洩漏 err.Error() 給 client 10. dungeon_service_continue.go:13 — continueBattle 使用可能 stale 的 Redis continue_count 11. teach_service.go:126 — sync.Once configIndex hot-reload 後不刷新
What Changed¶
執行了完整的測試套件(Unit tests、Publisher E2E、Smoke tests 均 exit code 0 通過)。同時啟動獨立的 JIT 分析 agent 掃描全專案,發現 6 個 P1 漏洞(資料一致性、race condition)和 5 個 P2 漏洞(安全性、resource leak)。
So What¶
所有功能測試通過但靜態分析仍發現嚴重漏洞,說明 unit/e2e/smoke test 無法覆蓋 race condition 和 data consistency 問題。這 11 個漏洞在 production 規模下可能造成資料遺失、計費錯誤或安全資訊洩漏。
Trade-offs¶
JIT 分析為靜態分析,部分發現(如 GiveGift double-consume)需實際驗證 transaction propagation 才能確認是否真的有問題。修復部分問題(如 AdminRemoveItem 將 read 移入 tx)可能增加 tx 持有時間,需評估效能影響。
Try It Fast¶
// 修復 lastCleanupTime
var (
lastCleanupTime atomic.Value // stores time.Time
)
func recordCleanupSuccess() {
lastCleanupTime.Store(time.Now())
}
func UpdateCleanupLag() {
t, _ := lastCleanupTime.Load().(time.Time)
lag := time.Since(t)
// update metric...
}
Recommendation¶
Priority 1 — 立即修復 P1 漏洞¶
- 修復 lastCleanupTime data race — 可用
go test -race立即驗證,改用atomic.Value或sync.Mutex保護 - 修復 GDPR atomic transaction — soft-delete 與 deletion task 必須在同一 DB transaction,資料一致性風險最高
Priority 2 — 建立規範與防護機制¶
- mapError 規範化 — 每新增 domain error 必須同步更新對應 mapError case,建議加入 linter 或 code review checklist
- sync.Once gamedata index — 評估是否需要 hot-reload 支援,若需要改用帶版本號的 RWMutex 方案
Priority 3 — 持續改進¶
- 定期執行 JIT 靜態分析 — 納入 CI pipeline,尤其在新增 module 時