跳轉到

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 data race
go test -race ./internal/modules/player/...
// 修復 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 漏洞

  1. 修復 lastCleanupTime data race — 可用 go test -race 立即驗證,改用 atomic.Valuesync.Mutex 保護
  2. 修復 GDPR atomic transaction — soft-delete 與 deletion task 必須在同一 DB transaction,資料一致性風險最高

Priority 2 — 建立規範與防護機制

  1. mapError 規範化 — 每新增 domain error 必須同步更新對應 mapError case,建議加入 linter 或 code review checklist
  2. sync.Once gamedata index — 評估是否需要 hot-reload 支援,若需要改用帶版本號的 RWMutex 方案

Priority 3 — 持續改進

  1. 定期執行 JIT 靜態分析 — 納入 CI pipeline,尤其在新增 module 時

本文檔由 Semi-Brain 自動生成

Session ID: 10eef6a0-3a55-4d63-981c-6bc79aff1880

分析信心度: 88%