chore: update ai-review findings [ai-review-bot][success]
CI / 1. BUILD (pull_request) Successful in 2s
CI / 2. TEST (pull_request) Successful in 2s
CI / 3. RESULT (pull_request) Successful in 1s

This commit is contained in:
AI Review Bot
2026-08-07 08:55:58 +00:00
parent d21e2f0e12
commit 605d557455
2 changed files with 157 additions and 22 deletions
+12
View File
@@ -9,5 +9,17 @@
"endLine": 3,
"problem": "這份 README 已經長成機械化的 API 編目,還把時間戳與大量硬編碼連結一起寫進來,讓主文件變得又厚又脆,讀者很難快速抓到重點。",
"reason": "此專案的 README 本身就是生成式 API 參考文件,維持完整索引與連結有助於內部使用,屬於文件取捨而非功能性缺陷。"
},
{
"location": "src/main.js:59",
"role": "Bard",
"original_finding": "刪掉這種會隨流程變動而失真的數量型描述;若真要提醒收尾差異,改成更穩定的概念性說明即可。",
"reason": "AI 對話收斂判定為誤報(問題在最新程式碼中不成立或不適用)"
},
{
"location": "entrypoint.sh:2",
"role": "Bard",
"original_finding": "移除這種會過期的時間戳註解,只保留真正需要提醒讀者的簡短說明即可。",
"reason": "AI 對話收斂判定為誤報(問題在最新程式碼中不成立或不適用)"
}
]
+144 -21
View File
@@ -1,23 +1,146 @@
{
"generatedAt": "2026/08/07 16:47:53",
"commitSha": "5e9bd86bbcb827415e25dcc1dea2fea2a4f07a58",
"prNumber": 4,
"tool": {
"name": "ai-review-bot",
"version": "unknown",
"model": "unknown"
},
"findings": [],
"excluded": [
[
{
"id": "F001",
"reviewer": "Bard",
"severity": "警告",
"file": "readme.md",
"startLine": 3,
"endLine": 3,
"problem": "這份 README 已經長成機械化的 API 編目,還把時間戳與大量硬編碼連結一起寫進來,讓主文件變得又厚又脆,讀者很難快速抓到重點。",
"suggestion": "把 README 收斂成專案摘要、安裝方式與使用入口;細部 API 文件另放獨立文件或改成可生成的 docs,避免主文件膨脹成資料堆。"
"level": "warning",
"role": "Assassin",
"location": "action.yml:14",
"problem": "action.yml 中新增的 inputs.model 沒有在 GitHub Actions 層面進行輸入驗證。雖然描述寫著「僅允許英數字、點、底線與連字號」,但使用者可以提供任意字符(如 `gpt-4; rm -rf /`),這些惡意輸入會先被寫入環境變數 `CLI_PROXY_API_MODEL`,才在 Node.js 代碼中被驗證。違反了「最小信任原則」。",
"suggestion": "在 action.yml 中的 inputs.model 新增驗證限制(GitHub Actions 層面無原生驗證機制,但可在文檔中強調風險,並確保 Node.js 驗證實作完備)。或改為使用 `choices` 列表限制可選值。目前的 Node.js 驗證雖然有效,但應在 GitHub Actions 文檔中明確說明:只有英數字、點、底線、連字號的 model 值才會被接受,其他值會被拒絕並導致工作流失敗。",
"is_new": true
},
{
"level": "warning",
"role": "Bard",
"problem": "`postFindingsReview` 這段 JSDoc 太像流程筆記,不像 API 說明。`@param`、`@remarks`、`使用情境` 與多層降級敘事一路堆疊,重點被枝節埋掉,閱讀節奏很不乾淨。",
"suggestion": "把註解壓縮回最必要的契約說明:用途、參數、回傳與例外即可;降級順序和測試注入細節留給實作內的短註解。",
"location": "src/comments.js:235",
"is_new": false
},
{
"level": "warning",
"role": "Bard",
"location": "action.yml:15",
"problem": "把 Docker Action 的 image 參照改成小寫 `dockerfile`,讓原本業界慣用的 `Dockerfile` 檔名失去辨識度;這種大小寫改動會讓人讀配置時多停一下,也讓專案風格顯得不一致。",
"suggestion": "把檔名與 `action.yml` 的 `runs.image` 都改回慣用的 `Dockerfile`,維持 Docker 生態的標準寫法。",
"is_new": true
},
{
"level": "warning",
"role": "Bard",
"location": "readme.md:1",
"problem": "新文件採用小寫 `readme.md`,和倉庫中常見的 `README.md` 命名慣例不合。這種只差大小寫的命名,最容易在查找與瀏覽時破壞一致感。",
"suggestion": "改名為 `README.md`,讓入口文件維持一眼可辨的標準名稱。",
"is_new": true
},
{
"level": "warning",
"role": "Bard",
"location": "src/comments.js:314",
"problem": "這段 JSDoc 連到不存在的 `newFindingsOnly`,斷鏈的 `{@link}` 會讓文件閱讀時突然失聲;同時還把判定差異寫得過於旁白化,讓主註解變得冗長。",
"suggestion": "把 cross-reference 換成實際存在的符號,或直接刪掉;差異說明則濃縮成一句話,保留重點即可。",
"is_new": true
},
{
"level": "warning",
"role": "Mage",
"location": "src/comments.js:37",
"problem": "buildTable 函式在呼叫 findings.map() 前無防呆檢查,若 findings 為 null/undefined 會拋出 TypeError。文件已提及此問題但函式本體未修正",
"suggestion": "在 .map() 呼叫前加入 `if (!Array.isArray(findings)) findings = [];` 或改用可選鏈語法,確保即使傳入無效值也能優雅降級",
"is_new": true
},
{
"level": "warning",
"role": "Mage",
"location": "src/comments.js:90",
"problem": "inlineCommentBody 函式若 f.role 或 f.suggestion 為 undefined,會直接內嵌 undefined 字樣到輸出字串,產生 '**等級**:xxx\\n**審查員**:undefined\\n**建議**:undefined' 的破損註解",
"suggestion": "在組字前加檢查:`const role = f.role || 'AI Review'; const suggestion = f.suggestion || '';` 確保回傳值不含 undefined 字面值",
"is_new": true
},
{
"level": "warning",
"role": "Mage",
"location": "src/findings.js:658",
"problem": "applyExclusions 的比對邏輯在 (locationMatches && roleMatches && (textMatches || ...)) 中,若排除規則只指定 filePath 不指定 role,會產生「該檔案內所有角色的問題都被排除」的非預期行為;若只指定 role 不指定 filePath,則「該角色所有檔案的問題都被排除」。此為對稱性缺陷",
"suggestion": "重新檢視比對邏輯意圖:若欲實現「指定 filePath 時自動不檢查 role」的設計,需在文件中明確說明此為刻意設計;若非刻意,應改為 (locationMatches || !exclusion.filePath) && (roleMatches || !exclusion.role) && (textMatches || ...),確保每個維度皆能獨立篩選",
"is_new": true
},
{
"level": "warning",
"role": "Mage",
"location": "src/llm.js:161",
"problem": "summarizeApiError 函式內存取 e.stderr 與 e.stdout 時未使用可選鏈,若 e 為 null 或 undefined,會拋出 TypeError 而非優雅容錯",
"suggestion": "改用可選鏈:`const stderr = e?.stderr || ''` 與 `const stdout = e?.stdout || ''`,或在函式開頭加入 `if (!e) return String(e);` 早期退出",
"is_new": true
},
{
"level": "warning",
"role": "Rogue",
"location": "src/log.js:7",
"problem": "formatTimestamp() 每次調用都新建 Intl.DateTimeFormat 實例,加上 formatToParts() 與 Object.fromEntries() 轉換,高頻日誌場景下重複成本大;而日誌函式會在 section/step/line/input/output/result/ok/warn/error 等多處調用,累積開銷明顯",
"suggestion": "將 Intl.DateTimeFormat 快取為模組層級單例(const formatter = new Intl.DateTimeFormat(...)),或改用更輕量的時間格式化方式(例如直接用 Date 方法),避免每條日誌都重複實例化",
"is_new": true
},
{
"level": "info",
"role": "Assassin",
"location": "src/config.js:46",
"problem": "正則表達式 `MODEL_NAME_RE = /^[A-Za-z0-9._-]+$/` 不允許 `/` 字符。某些合法的模型名稱格式(如 `openrouter/openai/gpt-4o` 或 `providers/openai/models/gpt-4o`)會被拒絕,導致功能受限。雖然不是直接的安全漏洞,但可能造成合法請求被誤判為異常。",
"suggestion": "評估是否需要在正則表達式中允許 `/` 字符。若允許,應同時確保不會引入新的安全風險(例如路徑穿越攻擊)。改為 `/^[A-Za-z0-9._/-]+$/` 並增加單元測試確認邊界情況。",
"is_new": true
},
{
"level": "info",
"role": "Assassin",
"location": "src/log.js:19",
"problem": "formatTimestamp 函數依賴 Intl.DateTimeFormat.formatToParts 的實現細節。若回應結構不符預期,`map.year`、`map.month` 等會是 `undefined`,導致日誌中顯示 `undefined` 字樣。雖然不影響安全性,但可能造成日誌混亂及除錯困難。",
"suggestion": "加強容錯處理。在存取 `map.year` 等屬性前先驗證其存在性;或改用更穩定的日期格式化方式(如 `new Date().toISOString()`)。同時增加單元測試,確保在異常情況下(例如不同的語言環境或舊版本瀏覽器)仍能產生正確的日誌格式。",
"is_new": true
},
{
"level": "info",
"role": "Bard",
"location": "src/llm.js:31",
"problem": "`mapWithConcurrency` 內部工作者 `run()` 的註解太像設計文件,對 `cursor`、`Promise.all` 行為、背景工作都展開長篇解釋,視覺重量遠超過程式本身。",
"suggestion": "把這段縮成一兩句重點註解,保留「限制併發、保序寫入」即可,其餘執行細節交回外層函式說明。",
"is_new": true
},
{
"level": "info",
"role": "Mage",
"location": "src/findings.js:349",
"problem": "mergeFindings 用 suggestion 前 50 字作為 key 的一部分進行去重。若兩個 findings 的 role 與 location 相同但 suggestion 在第 50 字之後才出現差異,會被誤判為重複而遭移除",
"suggestion": "考慮是否改用完整 suggestion 或增加其他識別字段(如 problem)來組成 key,確保去重不會誤刪本質不同的問題",
"is_new": true
},
{
"level": "info",
"role": "Mage",
"location": "src/findings.js:363",
"problem": "sortByLevel 使用 LEVELS.indexOf() 排序,級別不在 ['critical','warning','info'] 中的項目因 indexOf 回傳 -1 而被排到 critical 之前(最前面),此邊界行為是否為預期設計不明確",
"suggestion": "在文件或代碼中明確說明未知級別項目的預期排序位置,或改用顯式的條件判斷以提升代碼可讀性",
"is_new": true
},
{
"level": "info",
"role": "Mage",
"location": "src/resolve.js:98",
"problem": "groupConversations 在設置 botFinding 時用 `botFindings[0]`,若該對話的 botFindings 陣列為空,botFinding 會為 undefined。此設計雖有文件說明是為相容舊邏輯,但下游代碼仍需確保可安全處理 undefined 值",
"suggestion": "在文件中明確註記 botFinding 可為 undefined,並在此函式或其呼叫端加入明確的 null 檢查,或改用 `botFinding: botFindings.length > 0 ? botFindings[0] : null` 以更清晰地表達意圖",
"is_new": true
},
{
"level": "info",
"role": "Rogue",
"location": "src/llm.js:154",
"problem": "runProxyAPI() 中 body 物件先建立後再條件性添加 model 屬性;若此函式在併發量大的場景反覆呼叫,每次都會新建完整物件結構",
"suggestion": "改用 Object.assign() 或 const body = { messages: [...], temperature: 0, stream: false, ...(model && { model }) },減少不必要的中間物件建立步驟",
"is_new": true
},
{
"level": "info",
"role": "Rogue",
"location": "src/log.js:18",
"problem": "formatToParts() 後用 Object.fromEntries(parts.map(...)) 進行雙次陣列與物件轉換,再拼字串;格式化操作偏複雜,對日誌輸出這種高頻操作成本偏高",
"suggestion": "改用 reduce() 直接在一次遍歷內組出 map 物件,或改寫為單一模板字符串拼接,避免中間陣列轉換",
"is_new": true
}
]
}
]