Files
release-cleanup/.gitea/ai-review/findings.json
T

147 lines
9.0 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
[
{
"level": "critical",
"role": "Assassin",
"location": "app/config.js:21",
"problem": "GITEA_SERVER_URL 缺乏嚴格的格式驗證與協定強制。攻擊者若控制此環境變數,可將其設定為任意惡意伺服器(例如 http://attacker.com),引發 SSRF 攻擊或在沒有 HTTPS 的情況下,導致傳輸過程中 GITEA_TOKEN 被竊取。",
"suggestion": "應使用 Node.js 的 URL 類別驗證 GITEA_SERVER_URL 格式是否合法,並強制使用 HTTPS 協定。若可能,建議實作 allowed host 的白名單機制。",
"is_new": true
},
{
"level": "critical",
"role": "Assassin",
"location": "app/config.js:39",
"problem": "GITEA_REPOSITORY 變數直接被拼接進 API URL 中,卻未進行任何消毒。攻擊者可利用 Path Traversal(例如設定為 ../../other-repo)繞過權限,導致 API 呼叫目標被竄改,進而執行未授權的動作。",
"suggestion": "必須對 GITEA_REPOSITORY 進行格式驗證,僅允許合法的 Repository 名稱字元(例如英數字、`-`、`_`、`/`),且必須拒絕包含 `..` 或其他路徑穿越字元的輸入。",
"is_new": true
},
{
"level": "critical",
"role": "Mage",
"location": "app/gitea-client.js:31",
"problem": "在 `fetchAllPages` 的 `while` 迴圈中直接使用 `await fetch` 而沒有設定逾時(timeout)。若 Gitea API 伺服器因負載過重導致回應緩慢或掛起,該容器進程將會永久卡死,無法釋放資源或失敗重試。",
"suggestion": "建議使用 `AbortController` 設定合理的逾時時間(例如:`{ signal: AbortSignal.timeout(30000) }`),確保網路呼叫在預期時間內未完成時能主動拋出例外。",
"is_new": true
},
{
"level": "critical",
"role": "Mage",
"location": "app/index.js:26",
"problem": "當 `main().catch(...)` 捕捉到錯誤並執行 `process.exit(1)` 時,僅輸出 `error.message`。若錯誤是由 `fetch` 網路層拋出(例如 DNS 解析失敗或 connection refused),Node.js 的 `Error` 物件可能不包含足夠的上下文訊息,導致使用者難以區分是 API 回傳錯誤還是程式碼執行期錯誤。",
"suggestion": "建議在 `catch` 區塊中,若錯誤是 `Error` 物件,輸出 `error.stack` 或至少記錄錯誤類型,並區分不同層級的例外(例如:驗證錯誤 vs. API 請求錯誤)。",
"is_new": true
},
{
"level": "critical",
"role": "Maya",
"location": "app/releases.js:21",
"problem": "cleanupReleases 是執行刪除舊成品的核心函式,但目前缺乏針對 API 呼叫失敗(如 GET 失敗、DELETE 失敗)的測試,無法驗證錯誤處理邏輯是否如預期運作。",
"suggestion": "使用測試框架搭配 mock 伺服器回應,補齊 cleanupReleases 的測試案例,特別是驗證當 deleteResource 回傳非 204 狀態碼時,系統是否正確記錄錯誤並繼續或中斷。",
"is_new": true
},
{
"level": "critical",
"role": "Maya",
"location": "app/tags.js:35",
"problem": "cleanupOrphanTags 涉及多次 API 交互,目前缺乏測試驗證邏輯,無法確保在 API 請求失敗或標籤分類錯誤時系統的行為一致性。",
"suggestion": "補齊 cleanupOrphanTags 的測試案例,需模擬 fetchAllPages 回傳內容,並驗證對於不同 actionkeep/delete/skip)的處理流程是否正確。",
"is_new": true
},
{
"level": "warning",
"role": "Leo",
"location": "app/releases.js:14",
"problem": "使用 `new Date(release.created_at)` 進行排序時,若 `created_at` 格式非預期或為空,會導致 `NaN` 並造成排序異常,可能無法正確刪除舊版本。",
"suggestion": "建議在排序邏輯中增加對 `created_at` 的有效性檢查,若無效則給予預設值(例如:`new Date(0)`),確保排序結果可預期。",
"is_new": true
},
{
"level": "warning",
"role": "Leo",
"location": "app/gitea-client.js:25",
"problem": "HTTP 請求 (fetch) 未設定逾時時間,若 API 伺服器回應緩慢或網路阻塞,可能導致此 Actions 執行過程長時間卡死,造成除錯與排程上的困難。",
"suggestion": "建議使用 `AbortController` 為所有 `fetch` 請求設定合理的逾時限制(例如 30 秒),並正確處理 `AbortError`。",
"is_new": true
},
{
"level": "warning",
"role": "Mage",
"location": "app/gitea-client.js:28",
"problem": "在 `fetchAllPages` 中未檢查 API 回傳的內容是否符合預期格式(除了 Array 檢查)。若 API 回傳非 JSON 格式的內容(例如 HTML 錯誤頁面),`res.json()` 會拋出 SyntaxError,且未被目前邏輯中的 `try-catch` 明確攔截處理,會導致程式在 catch 區塊中直接結束並輸出錯誤訊息,缺乏更細緻的除錯資訊。",
"suggestion": "在解析 JSON 前,應先判斷 `res.headers.get('content-type')` 是否包含 `application/json`,若非 JSON,應將 `res.text()` 的內容一併在錯誤訊息中輸出,方便排查。",
"is_new": true
},
{
"level": "warning",
"role": "Mage",
"location": "app/releases.js:46",
"problem": "在 `cleanupReleases` 迴圈中執行 DELETE 請求時,未針對網路不穩定或暫時性服務錯誤(如 502, 503, 504)實作重試機制。若刪除過程中發生瞬間網路中斷,該 release 將不會被刪除,且當前流程會因為失敗呼叫 `fail` 並繼續執行,可能導致後續刪除邏輯的不一致。",
"suggestion": "對於特定的 HTTP 狀態碼(502, 503, 504),建議引入簡單的指數退避重試機制(Exponential Backoff),而不是直接宣告刪除失敗。",
"is_new": true
},
{
"level": "warning",
"role": "Mage",
"location": "app/tags.js:56",
"problem": "在 `cleanupOrphanTags` 中,雖然先重新 fetch 了 release 清單,但 `cleanupReleases` 和 `cleanupOrphanTags` 是非同步執行,且中間無確保一致性的機制。若在 `cleanupReleases` 刪除完成後到 `cleanupOrphanTags` 執行期間,Gitea 上有新的 release 被建立,則 `releaseTagNames` 的快照將會過時,導致正在使用的 tag 被錯誤刪除。",
"suggestion": "考慮在兩個 cleanup 步驟之間,確保 API 狀態的一致性,或者在刪除 tag 前再次檢查該 tag 是否真的未被任何現存 release 使用。",
"is_new": true
},
{
"level": "warning",
"role": "Maya",
"location": "app/config.js:11",
"problem": "loadConfig 函式雖有呼叫驗證邏輯,但缺乏針對環境變數異常情境(如必填欄位缺失、KEEP_COUNT 非整數)的單元測試,無法確保配置載入流程的穩定性。",
"suggestion": "補齊 app/test/config.test.js,測試當 process.env 缺少必要參數或 KEEP_COUNT 為無效數字時,loadConfig 是否會正確拋出錯誤。",
"is_new": true
},
{
"level": "warning",
"role": "Maya",
"location": "app/index.js:17",
"problem": "main 函式作為整個流程的入口,缺乏端對端(E2E)或整合測試,無法確保各模組整合後在失敗情境下(如 process.exit(1) 的觸發)是否能正確運作。",
"suggestion": "針對 main 函式建立整合測試,至少驗證在拋出例外時,logger.fail 是否有被呼叫且程序是否以錯誤代碼結束。",
"is_new": true
},
{
"level": "warning",
"role": "Rogue",
"location": "app/releases.js:43",
"problem": "在刪除舊成品時使用了序列化的 `for...of` 迴圈搭配 `await`,導致刪除請求一個個排隊等待 API 回應,浪費了寶貴的 I/O 等待時間。",
"suggestion": "改用 `Promise.all` 搭配 `map` 將刪除請求並行化,讓所有請求同時發送,瞬間縮短總執行時間。",
"is_new": true
},
{
"level": "warning",
"role": "Rogue",
"location": "app/tags.js:46",
"problem": "同樣在刪除孤立 tag 時使用了序列化的迴圈,導致同樣的阻塞問題,造成不必要的總執行時間拉長。",
"suggestion": "同樣改用 `Promise.all` 將刪除請求並行化,加快清理速度。",
"is_new": true
},
{
"level": "info",
"role": "Bard",
"location": "Dockerfile:13",
"problem": "注释中的冒号后面缺少空格,阅读节奏感稍显拥挤。",
"suggestion": "请在冒号后面加上一个空格,让注释读起来更舒畅:`# 基底映像: Node.js 20 的 Alpine 版本 (體積小)。`",
"is_new": true
},
{
"level": "info",
"role": "Bard",
"location": "Dockerfile:16",
"problem": "注释中括号内的说明与前文缺少空格区隔,排版不够优雅。",
"suggestion": "在括号前面增加一个空格:`# 複製 Node.js 應用程式 (不含測試)`",
"is_new": true
},
{
"level": "info",
"role": "Bard",
"location": "app/config.js:31",
"problem": "日志信息的键值对缺少空格,排版不够整齐统一。",
"suggestion": "建议加上空格以提升易读性:`info('GITEA_TOKEN = [redacted]')`",
"is_new": true
}
]