chore: update ai-review findings [ai-review-bot][failure]

This commit is contained in:
AI Review Bot
2026-06-26 05:56:45 +00:00
parent 7b742ed85e
commit 1bc6ce6d8c
2 changed files with 60 additions and 37 deletions
+12
View File
@@ -124,5 +124,17 @@
"role": "Bard",
"original_finding": "categorizeTags 內部判斷邏輯稍複雜,建議拆分為更小的判斷函式以提高可讀性。",
"reason": "不採納。categorizeTags 僅為 skip/keep/delete 三分支的單層 map,語意已清楚且具完整測試;為三個簡單條件再抽出微函式只會增加跳轉與閱讀成本,可讀性無實質提升。"
},
{
"location": "app/tags.js:19",
"role": "Bard",
"original_finding": "建議將邏輯拆分為更小的判斷函式,提高可讀性。",
"reason": "AI 對話收斂判定為誤報(問題在最新程式碼中不成立或不適用)"
},
{
"location": "app/tags.js:62",
"role": "Assassin",
"original_finding": "除了 encodeURIComponent 外,應在 config.js 或 tags.js 中對 tag.name 進行嚴格的白名單格式驗證(例如限制為英數字、點、破折號,並禁止 .. 或 /),這比單純編碼更安全。",
"reason": "AI 對話收斂判定為誤報(問題在最新程式碼中不成立或不適用)"
}
]
+48 -37
View File
@@ -1,4 +1,44 @@
[
{
"level": "critical",
"role": "Maya",
"problem": "Action 的核心邏輯 cleanupReleases 與 cleanupOrphanTags 被直接呼叫,但缺少針對清理流程的整合測試或端對端測試,僅有單元測試無法保證整個「清理 -> 再清理 tag」的完整路徑是否會因環境設定或 API 回應產生非預期的行為。",
"suggestion": "建議補上一個整合測試 (app/test/integration.test.js),模擬完整的 API 回應序列(如:先列出舊版本 -> 刪除舊版本 -> 重新列出 release -> 列出 tag -> 刪除孤立 tag),驗證所有 API 呼叫順序與參數皆符合預期。",
"location": "app/index.js:21",
"is_new": false
},
{
"level": "critical",
"role": "Mage",
"location": "app/config.js:33",
"problem": "在 loadConfig 函式中,對 GITEA_SERVER_URL 的 replace(//+$/, '') 操作假設了輸入一定是 string。雖然隨後有 requireValue 和 requireUrl 的驗證,但如果 env.GITEA_SERVER_URL 是非字串(例如數字、陣列、物件),這裡的 replace 可能會拋出 TypeError。",
"suggestion": "應在 replace 之前確保其型別為 string,或使用 optional chaining 以及更嚴格的類型防護。例如: `typeof rawServerUrl === 'string' ? rawServerUrl.replace(//+$/, '') : rawServerUrl` 已經做了檢查,但後續的 `requireValue` 檢查順序應確保傳入的是處理後的字串。",
"is_new": true
},
{
"level": "critical",
"role": "Maya",
"location": "app/config.js:63",
"problem": "在 `loadConfig` 中,`serverUrl`、`repository`、`token` 以及 `keepCount` 雖有呼叫驗證函式,但這些驗證函式(如 `requireValue`、`requireInteger`)在失敗時會直接拋出錯誤,且沒有相對應的單元測試去驗證這些設定錯誤的情境,尤其是當環境變數輸入惡意或無效字串時的系統行為。",
"suggestion": "補上針對 `config.js` 的完整錯誤處理測試,特別是模擬 `process.env` 各項缺失或異常時,確認是否確實會拋出預期的 Error,並確保清理流程在設定錯誤時能安全停止。",
"is_new": true
},
{
"level": "critical",
"role": "Maya",
"location": "app/releases.js:45",
"problem": "在 `cleanupReleases` 函數中,儘管對 `id` 進行了 `isEmptyOrNull` 檢查,但對於 `releases` 陣列中可能存在的 `null`、`undefined` 或非預期結構的 `release` 物件,測試覆蓋僅止於 `id` 為空的情況,缺少對 `release` 本身結構異常的測試案例。",
"suggestion": "增加 `cleanupReleases` 的測試案例,模擬傳入包含異常結構(例如缺少 `tag_name` 或其他必要欄位)的 `release` 物件,確保在處理過程中不會拋出未捕捉的異常。",
"is_new": true
},
{
"level": "critical",
"role": "Maya",
"location": "app/tags.js:56",
"problem": "在 `cleanupOrphanTags` 中,雖有 `categorizeTags` 的邏輯測試,但對於 `cleanupOrphanTags` 本身與 `GiteaClient` 的整合互動,例如在 API 回傳異常 tag 列表時的處理,缺乏失敗路徑(如 API 請求失敗、JSON 解析失敗)的測試。",
"suggestion": "補上 `cleanupOrphanTags` 的整合測試,模擬 `client.fetchAllPages` 拋出例外的情境,確認清理流程是否會優雅地處理或拋出預期的錯誤。",
"is_new": true
},
{
"level": "warning",
"role": "Mage",
@@ -6,43 +46,15 @@
"problem": "清理流程對網路請求依賴強,若 API 呼叫失敗,整個 main 流程中斷,無法確保後續清理的一致性與部分成功重試。",
"suggestion": "引入更細緻的錯誤處理(如錯誤閾值機制)或部分清理成功後的重試策略。",
"status": "deferred",
"defer_reason": "錯誤處理/重試策略的設計取捨。目前單筆刪除失敗會記錄並繼續、讀取失敗則中止屬合理保守行為;是否引入閾值/重試保留待人工評估。"
"defer_reason": "錯誤處理/重試策略的設計取捨。目前單筆刪除失敗會記錄並繼續、讀取失敗則中止屬合理保守行為;是否引入閾值/重試保留待人工評估。",
"is_new": false
},
{
"level": "warning",
"role": "Maya",
"location": "app/releases.js:46",
"problem": "cleanupReleases 中若 fetchAllPages 拋錯,cleanupOrphanTags 不會執行;缺乏部分失敗後的清理與回報機制。",
"suggestion": "考慮在 cleanupReleases 加入 try/catch,僅該步驟失敗時記錄並仍嘗試 cleanupOrphanTags,或明確標示部分清理狀態。",
"status": "deferred",
"defer_reason": "目前採 fail-fast:無法取得 release 清單時不應依過時資料刪除 tag,中止較安全;且 fetchAllPages 的錯誤訊息已含失敗的 GET URL,可辨識中斷步驟。是否改為跨步驟續行屬設計取捨,保留待人工評估。"
},
{
"level": "warning",
"role": "Assassin",
"location": "app/gitea-client.js:66",
"problem": "錯誤路徑以 res.text() 讀取整個回應主體,未限制大小,惡意伺服器可回傳極大內容導致記憶體耗盡(DoS)。",
"suggestion": "限制讀取的回應大小,例如檢查 Content-Length 或以串流方式設定讀取上限。",
"status": "deferred",
"defer_reason": "風險低:目標為已通過 URL 驗證的受信任 Gitea 實例,且每請求已有 30 秒逾時。正確修法需串流逐段讀取並設位元組上限,屬較大改動,保留待人工評估。"
},
{
"level": "warning",
"role": "Assassin",
"location": "app/gitea-client.js:77",
"problem": "成功路徑以 res.json() 解析整個回應,未限制大小,惡意伺服器可回傳極大 JSON 導致記憶體耗盡(DoS)。",
"suggestion": "對 API 回應設定明確大小上限,超過時拒絕解析並拋出異常。",
"status": "deferred",
"defer_reason": "與 app/gitea-client.js:66 同類:受信任目標、已有逾時與 MAX_PAGES 約束,風險低;正確修法需串流讀取並設上限,屬較大改動,保留待人工評估。"
},
{
"level": "warning",
"role": "Assassin",
"location": "app/gitea-client.js:106",
"problem": "將 items 直接放入 all 陣列,若 API 回傳異常巨大的 JSON 陣列,可能導致容器記憶體耗盡(DoS)。",
"suggestion": "增加最大總項目數限制,並考慮以 AsyncGenerator 串流式處理降低記憶體佔用。",
"status": "deferred",
"defer_reason": "總量已受 MAX_PAGES(1000 頁)間接約束;改為 AsyncGenerator 串流處理屬較大架構改動,對 release/tag 數量有限的清理任務效益不高,保留待人工評估。"
"problem": "cleanupReleases 中若 fetchAllPages 拋錯,cleanupOrphanTags 不會執行;缺乏部分失敗後的清理與回報機制。且當 fetchAllPages 成功取得列表後,若後續刪除操作發生異常(例如網路中斷、API 回應逾時),會拋出錯誤並導致整個 cleanupReleases 流程終止,無法保證「已讀取到的所有舊 release」都被嘗試清理。",
"suggestion": "考慮在 cleanupReleases 加入 try/catch,並將逐筆刪除的迴圈包覆在 try-catch 中,即使單步驟或單筆刪除失敗,也應記錄錯誤後繼續嘗試執行後續步驟或刪除下一筆,僅該步驟失敗時記錄並仍嘗試 cleanupOrphanTags或明確標示部分清理狀態。"
},
{
"level": "warning",
@@ -51,15 +63,14 @@
"problem": "cleanupOrphanTags 分兩次 API 呼叫取得 releases 與 tags,兩次之間若 Gitea 狀態變更,categorizeTags 可能基於不一致狀態而誤刪。",
"suggestion": "考量原子性需求,或至少在日誌標示兩次取得資料的時間間距以利除錯。",
"status": "deferred",
"defer_reason": "Gitea API 無交易機制;現行已在 cleanupOrphanTags 開頭重新抓取最新 release 作為主要緩解,殘餘競態窗極小且屬排程任務可接受範圍。是否再加每筆刪除前複查屬一致性/成本取捨,保留待人工評估。"
"defer_reason": "Gitea API 無交易機制;現行已在 cleanupOrphanTags 開頭重新抓取最新 release 作為主要緩解,殘餘競態窗極小且屬排程任務可接受範圍。是否再加每筆刪除前複查屬一致性/成本取捨,保留待人工評估。",
"is_new": false
},
{
"level": "warning",
"role": "Maya",
"location": "app/gitea-client.js:115",
"problem": "deleteResource 僅回傳狀態碼,對於 401/403/404 等錯誤未做區分處理,呼叫方策略較鬆散。",
"suggestion": "在 deleteResource 內針對常見錯誤碼拋出更有意義的例外,讓呼叫方可採取跳過/重試/中止等對應策略。",
"status": "deferred",
"defer_reason": "現行刻意讓 deleteResource 單純回傳狀態碼、由呼叫端記錄 HTTP code 後續行;依狀態碼採取不同策略(如遇 401 中止)與重試/閾值同屬錯誤處理設計取捨,與 app/releases.js:38 一併保留待人工評估。"
"problem": "deleteResource 僅回傳狀態碼對於 401/403/404 等錯誤未做區分處理呼叫方策略較鬆散,且對於非 204 的處理顯得較為鬆散。",
"suggestion": "在 deleteResource 內針對常見錯誤碼拋出更有意義的例外讓呼叫方可採取跳過/重試/中止等對應策略。"
}
]