本 PR 將 ai-code-review action 分支的完整成果併入 develop,主要含四塊:
develop
create-issue: 'true'
action.yml
src
readme.md
.gitea/workflows/ci.yaml
src/index.js
main()
src/lib/gitea.js
addLabelsToIssue
src/lib/templates.js
issueLinkComment
src/lib/review.js
postSevereToIssue
createIssueWithFindings
sortFindingsForIssue
src/lib/gitrepo.js
.gitea/workflows/readme.md
2026/07/17 18:49:58
本問題由 AI Code Review 依 PR #6 的審查結果自動建立,問題明細見下方留言。
codex
codex-cli 0.144.6
e37a96433df84cd542d6c93c3b407873e873fa14
flowchart LR A[整理 git diff] --> B[⚔️ 攻擊方找問題] B --> C[🛡️ 防守方裁決] C --> D[保存 findings] D --> E[留言到 PR]
src/lib/agents.js
src/lib/roles.js
共 9 個檔案納入審查;另有 4 個檔案依 .reviewignore 排除。
.reviewignore
位置:src/index.js 第 221–223 行
問題描述
舊留言在本回合確認能完成審查前就被標記為過時。最小重現:PR 已有上一回合有效 findings,但 runner 未安裝任何 AI CLI;步驟 2 先清除舊留言,接著 detectTool() 回傳 null 並以失敗結束,PR 最後既沒有有效的舊結果,也沒有新的審查結果。後續任一 AI、檔案系統或 API 例外也會造成相同狀態。
detectTool()
修改建議
將 resolveOldComments 延後到新一輪審查結果已成功產生、且即將發布新結果時執行;至少也應在工具偵測、diff 整理及攻防裁決成功後才使舊結果失效。
resolveOldComments
位置:src/lib/review.js 第 39–42 行
攻擊者可刻意讓 AI CLI 失敗,並誘使其在 stderr/stdout 回顯提示內容、環境資訊、權杖或 PR diff 中的機密;此處未經遮罩便擷取最多 500 字,後續直接寫入 CI log。即使截斷仍足以洩漏 token、PII 或原始碼祕密,而 CI 記錄通常會被長期保存並供多人讀取。
不要將 AI CLI 的原始 stdout 寫入日誌;stderr 也只保留固定錯誤分類與 exit code。若確實需要診斷文字,應先移除換行與控制字元,套用 token、Authorization header、URL credential、常見金鑰格式及 PII 的遮罩,並以明確的除錯開關控制詳細輸出。
建議寫法
function agentFailureDetail(res) { const err = res && res.error; const parts = []; if (err && err.killed) parts.push('已逾時終止'); if (err && typeof err.code === 'number') parts.push(`exit ${err.code}`); else if (err && err.signal) parts.push(`signal ${err.signal}`); return parts.join('|') || 'AI CLI 執行失敗(詳細輸出已隱藏)'; }
ensureIssueCreated
issue
issueBuffer
kept=[]
{ labels }
undefined
null
{ labels: [...] }
resolveMergeBase
tryGit
--unshallow
--deepen=1000
baseRef
cause
merge-base
ok
listLabels
selectLabels
labelIds
gitea.createIssue
runAttackers
runDefenders
fillPurposes
res
focusLabel
rolesComment
已依嚴重度逐條處理議題 #7 的審查問題,修復已提交並推送至分支 ai-review-resolve/develop-20260717-185330(納入既有的 PR #6 → develop,未另建重複 PR)。
ai-review-resolve/develop-20260717-185330
統計:✅ 已解決 7 條、⏭️ 待人工處理 5 條、🚫 誤報 0 條。 exclusions.json 無新增。
exclusions.json
currentRunCommentIds
e44a8ed
agentFailureDetail
ACTIONS_STEP_DEBUG=true
5c83329
35887f1
createIssue
dbeb565
⏭️ 的 5 條皆繞不開「專案目前無測試框架/devDependencies/可測性結構」或「對未經 CI 驗證的核心流程做功能級改動」,屬架構決策,依規範不逕自硬加,避免對核心流程造成回歸。導入測試框架(例如 Node 內建 node:test)與可測性重構經人工確認方向後可另行處理。
node:test
postOthersToIssue
仍有 5 條待人工處理,本議題暫不關閉。
再次以嚴重度逐條複核議題 #7 的審查問題。7 條已解決項目已在目前分支 ai-review-resolve/develop-20260717-185330 的程式碼中逐一確認到位;其餘 5 條仍屬架構決策,本回合未硬改,並已寫回 findings 目錄供後續追蹤。
統計:✅ 已解決 7 條、⏭️ 待人工處理 5 條、🚫 誤報 0 條。 exclusions.json 本回合無新增。
redactSecrets
5 條待人工處理問題已寫成 findings wrapper 檔 .gitea/ai-review/findings/2026-07-20-15:20:13.json(F001–F005),供後續 review/resolve 追蹤,不會因關閉議題而遺失待辦。
.gitea/ai-review/findings/2026-07-20-15:20:13.json
F001
F005
這 5 條皆繞不開「專案目前無測試框架/devDependencies/可測性結構」或「對未經 CI 驗證的核心流程做功能級冪等重構」,屬架構決策,依規範不逕自硬加,避免對核心流程造成回歸。導入測試框架(例如 Node 內建 node:test)與可測性重構經人工確認方向後可另行處理。
問題已全部分流:✅ 已修復(程式碼確認)/⏭️ 待人工處理(寫回 findings 追蹤)/🚫 誤報 0。依 resolve 流程收尾,關閉本議題。
再次以議題模式複核議題 #7,問題判定與上一則進度(見上方留言)一致,無新增審查問題:✅ 已解決 7 條、⏭️ 待人工處理 5 條、🚫 誤報 0 條。
本回合新進展:
create-issue=true
create-issue=false
othersComment
docs(review)
註:resolve 收尾嘗試以 API 關閉本議題時回傳 HTTP 403(目前 token 無變更議題狀態之權限),需由具權限者手動關閉;待辦已在 findings 追蹤,關閉不會遺失。
No dependencies set.
The note is not visible to the blocked user.
變更摘要
本 PR 將 ai-code-review action 分支的完整成果併入
develop,主要含四塊:create-issue: 'true'時,審查留言(工具/diff/角色/嚴重問題/警告+建議)全部改發到追蹤 issue、不再張貼到 PR;不執行「舊留言標過時/resolve」;issue 延後到「確定有保留問題」才建立;無保留問題或無可審查變更則不建 issue、PR 也完全不留言(靜默通過);有問題時最後在 PR 回貼一則 issue 連結並補掛標籤,形成雙向關聯。action.yml/src各模組文件、重建readme.md,新增.gitea/workflows/ci.yaml(多工具 code review 驗證)並啟用建問題模式。影響範圍
src/index.jsmain()依模式分流:留言去向、跳過 resolveOldComments、issue 延後建立、severe/others 導向 issue、有問題才在 PR 回貼連結src/lib/gitea.js新增addLabelsToIssuesrc/lib/templates.js新增issueLinkCommentsrc/lib/review.js新增postSevereToIssue,移除createIssueWithFindings、sortFindingsForIssuesrc/lib/gitrepo.js補抓淺層 checkout 的 base 歷史readme.md、action.yml、.gitea/workflows/ci.yaml、.gitea/workflows/readme.md風險與注意事項
readme.md尚未反映「建問題模式導向 issue」的最新行為與新/移除的函式,建議後續以 doc-funcs 重建文件。2026/07/17 18:49:58,未刷新。🤖 AI Code Review|審查工具
codexcodex-cli 0.144.6e37a96433df84cd542d6c93c3b407873e873fa14📋 變更摘要(送審 git diff)
action.ymlreadme.mdsrc/index.jssrc/lib/agents.jssrc/lib/gitea.jssrc/lib/gitrepo.jssrc/lib/review.jssrc/lib/roles.jssrc/lib/templates.js⚔️ 攻擊方登場
🛡️ 防守方登場
🔴 嚴重|🔮 Mage
位置:
src/index.js第 221–223 行問題描述
舊留言在本回合確認能完成審查前就被標記為過時。最小重現:PR 已有上一回合有效 findings,但 runner 未安裝任何 AI CLI;步驟 2 先清除舊留言,接著
detectTool()回傳 null 並以失敗結束,PR 最後既沒有有效的舊結果,也沒有新的審查結果。後續任一 AI、檔案系統或 API 例外也會造成相同狀態。修改建議
將
resolveOldComments延後到新一輪審查結果已成功產生、且即將發布新結果時執行;至少也應在工具偵測、diff 整理及攻防裁決成功後才使舊結果失效。🔴 嚴重|🗡️ Assassin
位置:
src/lib/review.js第 39–42 行問題描述
攻擊者可刻意讓 AI CLI 失敗,並誘使其在 stderr/stdout 回顯提示內容、環境資訊、權杖或 PR diff 中的機密;此處未經遮罩便擷取最多 500 字,後續直接寫入 CI log。即使截斷仍足以洩漏 token、PII 或原始碼祕密,而 CI 記錄通常會被長期保存並供多人讀取。
修改建議
不要將 AI CLI 的原始 stdout 寫入日誌;stderr 也只保留固定錯誤分類與 exit code。若確實需要診斷文字,應先移除換行與控制字元,套用 token、Authorization header、URL credential、常見金鑰格式及 PII 的遮罩,並以明確的除錯開關控制詳細輸出。
建議寫法
🟠 其他問題(警告+建議,共 10 條)
src/index.jsensureIssueCreated同時建立 issue、修改外層issue狀態、逐筆清空issueBuffer,但整段流程沒有可重入或冪等機制。若 issue 建立成功後,寫入其中一則暫存留言時失敗,主流程會中止;重跑後又會建立另一個 issue,留下內容不完整的孤兒 issue。這種依賴閉包可變狀態的半完成狀態,半年後要加入重試、續傳或測試失敗情境都會很痛苦。src/index.jsissue尚未建立時解參考的回歸。kept=[]時不建立 issue 且不留言;僅嚴重問題;僅警告/建議;混合問題;建立 issue 或寫入暫存留言失敗;標籤查詢、AI 選標籤及補掛標籤失敗時仍回貼 issue 連結。除了呼叫次數,也應斷言 API 呼叫順序、目標 issue 編號及留言內容。src/lib/gitea.jsaddLabelsToIssue沒有對應測試,尚未驗證空值捷徑與實際 API 請求格式。這個函式位於新建問題流程的收尾路徑;若 endpoint、HTTP method 或{ labels }payload 不符預期,追蹤 issue 將無法取得標籤,而空陣列是否真的不發出請求也未被保護。undefined、null、空陣列及多個 label id;斷言前三者回傳null且完全不呼叫 API,多個 id 時以 POST 呼叫正確的 owner/repo/issue endpoint 並傳送{ labels: [...] },另驗證 API 拋錯會原樣往上傳遞。src/lib/gitrepo.jsresolveMergeBase新增多階段 fetch 與淺層 checkout 修復邏輯,但沒有看到測試驗證成功、降級與最終失敗路徑。尤其初次 merge-base 失敗後,淺層與非淺層 repository 會走不同路徑,且多個tryGit失敗會被刻意吞掉;若參數、refspec 或重試順序有誤,只會在實際 CI checkout 深度不足時才暴露。--unshallow後成功;--unshallow失敗但--deepen=1000後成功;非淺層首次失敗後重試成功;所有策略失敗時拋出含baseRef且保留原始cause的錯誤。並斷言 base/head fetch 的 refspec 與執行順序。src/lib/gitrepo.jsresolveMergeBase連續呼叫tryGit,卻完全忽略每次 fetch 的成功與失敗資訊;tryGit又吞掉原始例外,最後只把第一次merge-base失敗設為 cause。未來遇到權限、refspec、遠端或 Git 版本差異時,維護者只會看到籠統的「歷史不足」,無法判斷是哪個補抓策略失敗,也難以針對各分支編寫單元測試。ok、策略名稱與原始錯誤的結果,並由resolveMergeBase以資料驅動方式依序執行補抓策略;最終失敗時彙整各次診斷資訊。如此新增或調整 fetch 策略時不必再複製控制流,也能在測試中驗證每個降級路徑。src/lib/gitrepo.js--unshallow成功後仍無條件再執行兩次--deepen=1000fetch;每次淺層 checkout 都可能多付 2 次遠端往返與重複物件協商,大型存取庫還會浪費明顯網路流量、CPU 與 CI 時間。--unshallow是否成功;成功後直接重試merge-base,只有失敗時才進一步 deepen。若要補抓 base 與 HEAD,也應每次 fetch 後先嘗試merge-base,成功就停止。src/index.jssrc/index.jsaddLabelsToIssue;每個有問題的 PR 固定浪費 1 次遠端 API 往返。listLabels與selectLabels,再把選出的labelIds直接傳給既有的gitea.createIssue。標籤挑選失敗時傳空陣列即可維持目前的降級行為,並可移除補掛標籤請求。src/lib/review.jsrunAttackers/runDefenders/fillPurposes測試間接斷言 log。至少覆蓋 killed、數字 exit code、字串 code、signal、stderr 與 stdout 同時存在、超過 500 字、完全無資訊,以及res為 null/undefined 的案例。src/lib/templates.jsfocusLabel的使用情境仍標成「步驟 5/6」,但角色登場已調整為攻擊方步驟 5、防守方步驟 7;同檔rolesComment的文件也已採用「5/7」。步驟編號前後走調。🔧 AI Code Review 問題處理進度
已依嚴重度逐條處理議題 #7 的審查問題,修復已提交並推送至分支
ai-review-resolve/develop-20260717-185330(納入既有的 PR #6 →develop,未另建重複 PR)。統計:✅ 已解決 7 條、⏭️ 待人工處理 5 條、🚫 誤報 0 條。
exclusions.json無新增。src/index.jsresolveOldComments延後到審查成功產生結果、發布問題留言前才執行,並以currentRunCommentIds排除本回合新留言(commite44a8ed)src/lib/review.jsagentFailureDetail預設只輸出 exit code/signal;僅ACTIONS_STEP_DEBUG=true時輸出經遮罩+去控制字元的片段(commit5c83329)src/lib/gitrepo.jsresolveMergeBase改資料驅動策略、彙整各策略成敗診斷、保留首次錯誤 cause(commit35887f1)src/lib/gitrepo.js--unshallow成功不再多做 deepen(commit35887f1)src/index.jse44a8ed)src/index.jscreateIssue一次帶入,移除事後補掛的多餘 API 往返(commite44a8ed)src/lib/templates.jsdbeb565)src/index.jssrc/index.jssrc/lib/gitea.jsaddLabelsToIssuesrc/lib/gitrepo.jsresolveMergeBase需可注入 git 執行器方能穩定測試各降級路徑,屬可測性重構+測試架構決策src/lib/review.js待人工處理共同原因
⏭️ 的 5 條皆繞不開「專案目前無測試框架/devDependencies/可測性結構」或「對未經 CI 驗證的核心流程做功能級改動」,屬架構決策,依規範不逕自硬加,避免對核心流程造成回歸。導入測試框架(例如 Node 內建
node:test)與可測性重構經人工確認方向後可另行處理。一併納入的兩項調整
postOthersToIssue),不再彙整成單一表格,讓每條問題都能被個別回覆。resolveOldComments時序:延後執行時排除本回合剛發的工具/diff/角色留言,不會把新產生的留言誤標為過時。🔧 AI Code Review 問題處理進度(複核收尾)
再次以嚴重度逐條複核議題 #7 的審查問題。7 條已解決項目已在目前分支
ai-review-resolve/develop-20260717-185330的程式碼中逐一確認到位;其餘 5 條仍屬架構決策,本回合未硬改,並已寫回 findings 目錄供後續追蹤。統計:✅ 已解決 7 條、⏭️ 待人工處理 5 條、🚫 誤報 0 條。
exclusions.json本回合無新增。src/index.jsresolveOldComments延後到審查成功、發布問題留言前才執行,並以currentRunCommentIds排除本回合新留言(已確認於src/index.js)src/lib/review.jsagentFailureDetail預設只輸出 exit code/signal;僅ACTIONS_STEP_DEBUG=true時輸出經redactSecrets遮罩+去控制字元的片段(已確認於src/lib/review.js)src/lib/gitrepo.jsresolveMergeBase改資料驅動策略、彙整各策略成敗診斷、保留首次錯誤 cause(commit35887f1)src/lib/gitrepo.js--unshallow成功不再多做 deepen(commit35887f1)src/index.jssrc/index.js)src/index.jscreateIssue一次帶入,移除事後補掛的多餘 API 往返src/lib/templates.jssrc/lib/templates.js)src/index.jsexclusions.json(Paladin 判為重複/已知排除)src/index.jssrc/lib/gitea.jsaddLabelsToIssuesrc/lib/gitrepo.jsresolveMergeBase需可注入 git 執行器方能穩定測試各降級路徑,屬可測性重構+測試架構決策src/lib/review.js⏭️ 待人工處理已寫回 findings 追蹤
5 條待人工處理問題已寫成 findings wrapper 檔
.gitea/ai-review/findings/2026-07-20-15:20:13.json(F001–F005),供後續 review/resolve 追蹤,不會因關閉議題而遺失待辦。待人工處理共同原因
這 5 條皆繞不開「專案目前無測試框架/devDependencies/可測性結構」或「對未經 CI 驗證的核心流程做功能級冪等重構」,屬架構決策,依規範不逕自硬加,避免對核心流程造成回歸。導入測試框架(例如 Node 內建
node:test)與可測性重構經人工確認方向後可另行處理。🔧 AI Code Review 複核補述
再次以議題模式複核議題 #7,問題判定與上一則進度(見上方留言)一致,無新增審查問題:✅ 已解決 7 條、⏭️ 待人工處理 5 條、🚫 誤報 0 條。
本回合新進展:
create-issue=true時嚴重/警告/建議皆逐條發成獨立留言可個別回覆(postSevereToIssue+postOthersToIssue);create-issue=false時嚴重逐條獨立留言、警告+建議彙整單一表格(othersComment)。議題 #7 早期的「其他問題」單一表格是舊版 action 產出,現行程式碼已為逐條。src/lib/review.js的postSevereToIssuedocstring 原殘留「警告+建議以othersComment表格發到 issue」的舊敘述,已改為與現行postOthersToIssue逐條行為一致(docs(review))。.gitea/ai-review/findings/2026-07-20-15:20:13.json(F001–F005)追蹤,本輪未重複建檔。