refactor: calculate-version 由 bash 改寫為 Node.js #5

Merged
admin merged 12 commits from ai-review-resolve/develop-20260626-104052 into develop 2026-06-26 03:00:32 +00:00
Member

變更摘要

calculate-version Gitea Action 的版本計算邏輯由原本的 bash + jq 改寫為 Node.js,保留 entrypoint.sh 作為容器進入點,並補齊單元測試與文件。對外契約(action inputs/outputs、版本進位規則、beta 規則)維持不變。

影響範圍

  • 執行環境:容器基底由 alpine 改為 node:lts-alpine(保留 bash 以執行 entrypoint.sh);不再依賴 curl / jq
  • 進入點entrypoint.sh 改為 exec node /app/index.js,行為與引數傳遞不變。
  • 對外行為:版本計算結果與輸出 version 不變(patch 進位上限為 10,beta 形如 <next>-beta.<n>)。

重點檔案/模組

  • app/index.js:進入點 orchestration(參數檢查 → 取得 release → 計算版本 → 寫出 output)。
  • app/config.js:環境變數載入與驗證。
  • app/version.js:版本號計算(穩定版/beta,含進位與比較邏輯)。
  • app/releases.js:Gitea release API 分頁抓取(改用全域 fetch)。
  • app/logger.js / app/output.js:log 輸出與 GITHUB_OUTPUT 寫入。
  • test/:以 node:test 撰寫的 24 個單元測試。
  • README.md:專案列表、功能列表與使用範例。

風險與注意事項

  • 容器基底映像改變(alpinenode:lts-alpine),映像體積與內含工具不同;需確認 runner 端拉取無虞。
  • Dockerfile 仍保留 --no-check-certificate(沿用原設定,建議後續評估是否移除)。
  • Node.js fetch 需 Node 18+;node:lts-alpine 已滿足。

驗證

  • node --test:24 項測試全數通過。
  • 端對端冒煙測試:v1.2.9 → 計算出 1.3.0,寫入 GITHUB_OUTPUT
## 變更摘要 將 `calculate-version` Gitea Action 的版本計算邏輯由原本的 **bash + jq** 改寫為 **Node.js**,保留 `entrypoint.sh` 作為容器進入點,並補齊單元測試與文件。對外契約(action inputs/outputs、版本進位規則、beta 規則)維持不變。 ## 影響範圍 - **執行環境**:容器基底由 `alpine` 改為 `node:lts-alpine`(保留 `bash` 以執行 `entrypoint.sh`);不再依賴 `curl` / `jq`。 - **進入點**:`entrypoint.sh` 改為 `exec node /app/index.js`,行為與引數傳遞不變。 - **對外行為**:版本計算結果與輸出 `version` 不變(patch 進位上限為 10,beta 形如 `<next>-beta.<n>`)。 ## 重點檔案/模組 - `app/index.js`:進入點 orchestration(參數檢查 → 取得 release → 計算版本 → 寫出 output)。 - `app/config.js`:環境變數載入與驗證。 - `app/version.js`:版本號計算(穩定版/beta,含進位與比較邏輯)。 - `app/releases.js`:Gitea release API 分頁抓取(改用全域 `fetch`)。 - `app/logger.js` / `app/output.js`:log 輸出與 `GITHUB_OUTPUT` 寫入。 - `test/`:以 `node:test` 撰寫的 24 個單元測試。 - `README.md`:專案列表、功能列表與使用範例。 ## 風險與注意事項 - 容器基底映像改變(`alpine` → `node:lts-alpine`),映像體積與內含工具不同;需確認 runner 端拉取無虞。 - `Dockerfile` 仍保留 `--no-check-certificate`(沿用原設定,建議後續評估是否移除)。 - Node.js `fetch` 需 Node 18+;`node:lts-alpine` 已滿足。 ## 驗證 - `node --test`:24 項測試全數通過。 - 端對端冒煙測試:`v1.2.9` → 計算出 `1.3.0`,寫入 `GITHUB_OUTPUT`。
jiantw83 added 3 commits 2026-06-26 02:42:34 +00:00
保留 entrypoint.sh 作為容器進入點,改為啟動 app/ 下的 Node.js 程式;
依功能拆分為 logger/config/version/releases/output/index,並更新 Dockerfile 改用 node:lts-alpine。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
以 node:test 為 app/ 模組補上 24 個單元測試,涵蓋版本計算、設定載入與 release 分頁抓取。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs(calculate-version): 新增 README 並補上 entrypoint/CI/CD 註解
CI / AI Code Review (pull_request) Failing after 57s
6f9df6743c
依 doc-funcs 產生 README(專案列表、功能列表、使用範例),並為 CI/CD workflow 補上逐行註解。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

🤖 AI Code Review 團隊

👤 角色 🎯 面向 🧠 個性
🗡️ Assassin security 多疑偏執、以攻擊者視角看世界,假設每筆輸入都是惡意的,每個信任都會被濫用
🎼 Bard style 唯美龜毛、追求優雅,把可讀性與一致性當作旋律,最受不了走調的命名與排版
🧰 Leo maintainability 有遠見、重視長期維護成本,凡事先問「六個月後的自己還看得懂嗎?」,討厭把債留給未來
🔮 Mage logic 嚴謹冷靜、滴水不漏,凡事推演到最壞情況,深信「沒驗證過的假設都是 bug」
🧪 Maya testing 對測試覆蓋率有執念,深信「沒有測試的程式碼等於沒寫完」,溫和但堅持,最在意邊界與失敗路徑
Rogue efficiency 急性子、講求速度,最痛恨被浪費的 CPU 週期與記憶體,凡事先問「這能不能更快、更省」

🔍 服務:opencode 模型:gemini-2.5-flash

## 🤖 AI Code Review 團隊 | 👤 角色 | 🎯 面向 | 🧠 個性 | |--------|--------|--------| | **🗡️ Assassin** | security | 多疑偏執、以攻擊者視角看世界,假設每筆輸入都是惡意的,每個信任都會被濫用 | | **🎼 Bard** | style | 唯美龜毛、追求優雅,把可讀性與一致性當作旋律,最受不了走調的命名與排版 | | **🧰 Leo** | maintainability | 有遠見、重視長期維護成本,凡事先問「六個月後的自己還看得懂嗎?」,討厭把債留給未來 | | **🔮 Mage** | logic | 嚴謹冷靜、滴水不漏,凡事推演到最壞情況,深信「沒驗證過的假設都是 bug」 | | **🧪 Maya** | testing | 對測試覆蓋率有執念,深信「沒有測試的程式碼等於沒寫完」,溫和但堅持,最在意邊界與失敗路徑 | | **⚡ Rogue** | efficiency | 急性子、講求速度,最痛恨被浪費的 CPU 週期與記憶體,凡事先問「這能不能更快、更省」 | > 🔍 服務:opencode 模型:gemini-2.5-flash
gitea-actions bot reviewed 2026-06-26 02:43:30 +00:00
gitea-actions bot left a comment

AI Code Review 統計

類型 🔴 嚴重 🟡 警告 🔵 建議 無法標示
新問題 4 筆 4 筆 2 筆 0 筆
舊問題 0 筆 0 筆 0 筆 0 筆

🤖 AI 助理使用量

本次審查(opencode / gemini-2.5-flash,共 18 次呼叫)

提示 token 回應 token 合計
176,361 3,715 372,798

剩餘可用

剩餘可用:無法計算百分比(自架服務,無帳號額度概念)

## AI Code Review 統計 | 類型 | 🔴 嚴重 | 🟡 警告 | 🔵 建議 | ⚪ 無法標示 | | --- | --- | --- | --- | --- | | 新問題 | 4 筆 | 4 筆 | 2 筆 | 0 筆 | | 舊問題 | 0 筆 | 0 筆 | 0 筆 | 0 筆 | ## 🤖 AI 助理使用量 **本次審查**(opencode / gemini-2.5-flash,共 18 次呼叫) | 提示 token | 回應 token | 合計 | | --- | --- | --- | | 176,361 | 3,715 | 372,798 | **剩餘可用** 剩餘可用:無法計算百分比(自架服務,無帳號額度概念)
Dockerfile Outdated
@@ -5,1 +10,4 @@
# entrypoint.sh 以 bash 撰寫,Alpine 預設無 bash 需另行安裝
# --no-cache 不保留 apk 索引快取以縮小映像;--no-check-certificate 略過憑證檢查 (需人工確認:略過憑證驗證有安全風險)。
RUN apk add --no-cache --no-check-certificate bash

嚴重等級🔴 嚴重
審查員:Assassin
問題:使用了 --no-check-certificate 參數下載軟體包,這會停用 TLS 憑證檢查。攻擊者若能進行中間人攻擊(MITM),即可攔截連線並將惡意的套件注入到映像檔中。
建議:移除 --no-check-certificate。若確實有無法解決的憑證問題,應排查來源網域的信任鏈或安裝正確的憑證,而不是直接關閉安全機制。

**嚴重等級**:🔴 嚴重 **審查員**:Assassin **問題**:使用了 `--no-check-certificate` 參數下載軟體包,這會停用 TLS 憑證檢查。攻擊者若能進行中間人攻擊(MITM),即可攔截連線並將惡意的套件注入到映像檔中。 **建議**:移除 `--no-check-certificate`。若確實有無法解決的憑證問題,應排查來源網域的信任鏈或安裝正確的憑證,而不是直接關閉安全機制。
Ghost marked this conversation as resolved
app/index.js Outdated
@@ -0,0 +12,4 @@
* 流程印出參數檢查區段並呼叫 loadConfig() 載入設定失敗則以 logger.fail
* 回報並結束印出設定值token *** 未提供顯示組出 release URL
* fetchReleases() 取得 release 清單失敗則以 logger.fail 回報並結束
* calculateVersion() 計算最新版與新版號最後以 writeOutput('version', version)

嚴重等級🟡 警告
審查員:Bard
問題:此函式註解過於詳盡描述執行流程(如 logger.fail、writeOutput),應專注於描述函式功能而非具體實作步驟。
建議:簡化為:/** Action 進入點:執行版本計算並寫入輸出。 */

**嚴重等級**:🟡 警告 **審查員**:Bard **問題**:此函式註解過於詳盡描述執行流程(如 logger.fail、writeOutput),應專注於描述函式功能而非具體實作步驟。 **建議**:簡化為:/** Action 進入點:執行版本計算並寫入輸出。 */
Ghost marked this conversation as resolved
app/index.js Outdated
@@ -0,0 +15,4 @@
* calculateVersion() 計算最新版與新版號最後以 writeOutput('version', version)
* 寫出 version 輸出
*
* Side effects透過 logger 輸出至 stdout/stderr失敗時使 process 以失敗狀態

嚴重等級🔴 嚴重
審查員:Maya
問題:主邏輯函式 main 處理了 loadConfigfetchReleases 的例外情境,但缺乏針對這些失敗路徑的整合測試,無法確保錯誤發生時流程能正確終止。
建議:建議編寫整合測試,透過 mock 相關依賴(如 loadConfigfetchReleases)來模擬錯誤,並驗證 main 是否正確觸發錯誤處理機制。

**嚴重等級**:🔴 嚴重 **審查員**:Maya **問題**:主邏輯函式 `main` 處理了 `loadConfig` 與 `fetchReleases` 的例外情境,但缺乏針對這些失敗路徑的整合測試,無法確保錯誤發生時流程能正確終止。 **建議**:建議編寫整合測試,透過 mock 相關依賴(如 `loadConfig`、`fetchReleases`)來模擬錯誤,並驗證 `main` 是否正確觸發錯誤處理機制。
Ghost marked this conversation as resolved
@@ -0,0 +38,4 @@
logger.section('取得版本資料');
const releaseUrl = `${config.serverUrl}/api/v1/repos/${config.repository}/releases`;

嚴重等級🔴 嚴重
審查員:Assassin
問題:Action 未對 GITEA_SERVER_URL 進行格式驗證,攻擊者若能控制 CI/CD 環境變數,即可將該值設定為惡意 URL,進而誘使 Action 將 GITEA_TOKEN 傳送至攻擊者伺服器,導致敏感憑證外洩。
建議:在 app/config.jsloadConfig 中,對 GITEA_SERVER_URL 進行嚴格驗證,確保其格式正確(例如開頭必須為 https://)且符合預期的網域白名單(若適用)。

**嚴重等級**:🔴 嚴重 **審查員**:Assassin **問題**:Action 未對 `GITEA_SERVER_URL` 進行格式驗證,攻擊者若能控制 CI/CD 環境變數,即可將該值設定為惡意 URL,進而誘使 Action 將 `GITEA_TOKEN` 傳送至攻擊者伺服器,導致敏感憑證外洩。 **建議**:在 `app/config.js` 的 `loadConfig` 中,對 `GITEA_SERVER_URL` 進行嚴格驗證,確保其格式正確(例如開頭必須為 `https://`)且符合預期的網域白名單(若適用)。
Ghost marked this conversation as resolved
@@ -0,0 +31,4 @@
* 輸出錯誤層級的 log 訊息至標準錯誤輸出stderr並以狀態碼 1
* 立即終止整個行程用於發生無法復原的錯誤時中止執行
*
* 注意此函式呼叫 `process.exit(1)`正常情況下不會回傳給呼叫端

嚴重等級🔵 建議
審查員:Bard
問題:註解提及 process.exit(1) 後續程式碼不會執行,此為程式語言基本常識,屬於冗餘註解。
建議:移除此行註解,讓程式碼更精簡。

**嚴重等級**:🔵 建議 **審查員**:Bard **問題**:註解提及 process.exit(1) 後續程式碼不會執行,此為程式語言基本常識,屬於冗餘註解。 **建議**:移除此行註解,讓程式碼更精簡。
Ghost marked this conversation as resolved
@@ -0,0 +38,4 @@
* @returns {never} 不會正常回傳行程會被終止
*/
function fail(message) {
process.stderr.write(`[error] ${message}\n`);

嚴重等級🔴 嚴重
審查員:Leo
問題:fail 函數在內部直接呼叫 process.exit(1),這會導致單元測試或呼叫此函數的程式無法攔截錯誤進行復原,且會直接終止整個 Node.js 行程,測試時會導致測試 runner 直接崩潰。
建議:建議將 fail 函數改為只負責輸出錯誤訊息並拋出例外(throw Error),由最外層的 main 函數負責攔截並決定

**嚴重等級**:🔴 嚴重 **審查員**:Leo **問題**:fail 函數在內部直接呼叫 process.exit(1),這會導致單元測試或呼叫此函數的程式無法攔截錯誤進行復原,且會直接終止整個 Node.js 行程,測試時會導致測試 runner 直接崩潰。 **建議**:建議將 fail 函數改為只負責輸出錯誤訊息並拋出例外(throw Error),由最外層的 main 函數負責攔截並決定
Ghost marked this conversation as resolved
@@ -0,0 +46,4 @@
if (!response.ok) {
throw new Error(`release API 請求失敗 (page=${page})`);
}

嚴重等級🟡 警告
審查員:Rogue
問題:浪費 CPU 週期在手動處理 JSON 解析。response.text()JSON.parse() 的效能比 response.json() 慢得多,且造成不必要的字串記憶體分配。
建議:直接使用 await response.json()

**嚴重等級**:🟡 警告 **審查員**:Rogue **問題**:浪費 CPU 週期在手動處理 JSON 解析。`response.text()` 再 `JSON.parse()` 的效能比 `response.json()` 慢得多,且造成不必要的字串記憶體分配。 **建議**:直接使用 `await response.json()`。
Ghost marked this conversation as resolved
app/releases.js Outdated
@@ -0,0 +49,4 @@
const text = await response.text();
// 空字串或 null 代表已無更多資料

嚴重等級🟡 警告
審查員:Mage
問題:fetch 的 response.text() 方法可能因連線中斷等原因拋出例外,且目前未被包裹在 try-catch 區塊中,若發生錯誤將無法提供明確的 API 頁碼上下文。
建議:將 response.text() 以及後續的 JSON.parse 邏輯整合進現有的 try-catch 區塊,確保錯誤處理能準確捕捉並包含頁碼 (page) 資訊。

**嚴重等級**:🟡 警告 **審查員**:Mage **問題**:fetch 的 response.text() 方法可能因連線中斷等原因拋出例外,且目前未被包裹在 try-catch 區塊中,若發生錯誤將無法提供明確的 API 頁碼上下文。 **建議**:將 response.text() 以及後續的 JSON.parse 邏輯整合進現有的 try-catch 區塊,確保錯誤處理能準確捕捉並包含頁碼 (page) 資訊。
Ghost marked this conversation as resolved
@@ -0,0 +74,4 @@
function nextReleaseVersion(latest) {
const parts = String(latest).split('.').map((part) => Number(part) || 0);
let [major = 0, minor = 0, patch = 0] = parts;

嚴重等級🔵 建議
審查員:Bard
問題:String(latest) 呼叫顯得冗贅,因為 latest 在此處已明確為字串型別。
建議:直接使用 latest.split('.') 即可。

**嚴重等級**:🔵 建議 **審查員**:Bard **問題**:String(latest) 呼叫顯得冗贅,因為 latest 在此處已明確為字串型別。 **建議**:直接使用 latest.split('.') 即可。
Ghost marked this conversation as resolved
@@ -0,0 +125,4 @@
const version = isBeta
? `${next}-beta.${nextBetaNumber(releases, next)}`
: next;

嚴重等級🟡 警告
審查員:Rogue
問題:重複遍歷資料!在 calculateVersion 中,先呼叫 latestStableVersion 遍歷一次 release 清單,隨後又呼叫 nextBetaNumber 再遍歷一次。這在資料量大時是完全不必要的 O(N) 浪費。
建議:應先在 calculateVersion 內將 releases 解析並篩選一次,將結果傳遞給後續函式,避免重複遍歷。

**嚴重等級**:🟡 警告 **審查員**:Rogue **問題**:重複遍歷資料!在 `calculateVersion` 中,先呼叫 `latestStableVersion` 遍歷一次 release 清單,隨後又呼叫 `nextBetaNumber` 再遍歷一次。這在資料量大時是完全不必要的 O(N) 浪費。 **建議**:應先在 `calculateVersion` 內將 `releases` 解析並篩選一次,將結果傳遞給後續函式,避免重複遍歷。
Ghost marked this conversation as resolved
gitea-actions bot added 1 commit 2026-06-26 02:43:32 +00:00
jiantw83 added 4 commits 2026-06-26 02:52:26 +00:00
- Dockerfile 移除 --no-check-certificate,恢復 TLS 憑證檢查以防中間人攻擊
- config 對 GITEA_SERVER_URL 加入 http/https URL 格式驗證
- logger.fail 改為 logger.error(僅輸出不終止行程),退出移至 index 頂層處理
- index.main 改用相依注入並於頂層攔截錯誤後 exit 1,避免 library 內呼叫 process.exit
- releases 將 response.text() 包入 try-catch 並附帶頁碼

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
新增 config 的 URL 格式/協定驗證測試,以及以相依注入覆蓋 main 成功與 loadConfig/fetchReleases 失敗路徑的整合測試。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
logger.fail 改為 logger.error、新增 index.main 公開函式說明,並更新對應行號與使用範例。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chore(ai-review 狀態): 更新 findings.json 與 exclusions.json
CI / AI Code Review (pull_request) Failing after 44s
43601350b0
已解決 6 條(critical x4、warning x1、info x1),4 條判定為誤報寫入 exclusions(warning x3、info x1),findings 清空。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

🤖 AI Code Review 團隊

👤 角色 🎯 面向 🧠 個性
🗡️ Assassin security 多疑偏執、以攻擊者視角看世界,假設每筆輸入都是惡意的,每個信任都會被濫用
🎼 Bard style 唯美龜毛、追求優雅,把可讀性與一致性當作旋律,最受不了走調的命名與排版
🧰 Leo maintainability 有遠見、重視長期維護成本,凡事先問「六個月後的自己還看得懂嗎?」,討厭把債留給未來
🔮 Mage logic 嚴謹冷靜、滴水不漏,凡事推演到最壞情況,深信「沒驗證過的假設都是 bug」
🧪 Maya testing 對測試覆蓋率有執念,深信「沒有測試的程式碼等於沒寫完」,溫和但堅持,最在意邊界與失敗路徑
Rogue efficiency 急性子、講求速度,最痛恨被浪費的 CPU 週期與記憶體,凡事先問「這能不能更快、更省」

🔍 服務:opencode 模型:gemini-2.5-flash

## 🤖 AI Code Review 團隊 | 👤 角色 | 🎯 面向 | 🧠 個性 | |--------|--------|--------| | **🗡️ Assassin** | security | 多疑偏執、以攻擊者視角看世界,假設每筆輸入都是惡意的,每個信任都會被濫用 | | **🎼 Bard** | style | 唯美龜毛、追求優雅,把可讀性與一致性當作旋律,最受不了走調的命名與排版 | | **🧰 Leo** | maintainability | 有遠見、重視長期維護成本,凡事先問「六個月後的自己還看得懂嗎?」,討厭把債留給未來 | | **🔮 Mage** | logic | 嚴謹冷靜、滴水不漏,凡事推演到最壞情況,深信「沒驗證過的假設都是 bug」 | | **🧪 Maya** | testing | 對測試覆蓋率有執念,深信「沒有測試的程式碼等於沒寫完」,溫和但堅持,最在意邊界與失敗路徑 | | **⚡ Rogue** | efficiency | 急性子、講求速度,最痛恨被浪費的 CPU 週期與記憶體,凡事先問「這能不能更快、更省」 | > 🔍 服務:opencode 模型:gemini-2.5-flash
gitea-actions bot reviewed 2026-06-26 02:53:10 +00:00
gitea-actions bot left a comment

AI Code Review 統計

類型 🔴 嚴重 🟡 警告 🔵 建議 無法標示
新問題 0 筆 3 筆 1 筆 0 筆
舊問題 2 筆 0 筆 0 筆 0 筆

🤖 AI 助理使用量

本次審查(opencode / gemini-2.5-flash,共 15 次呼叫)

提示 token 回應 token 合計
163,767 3,521 301,356

剩餘可用

剩餘可用:無法計算百分比(自架服務,無帳號額度概念)

## AI Code Review 統計 | 類型 | 🔴 嚴重 | 🟡 警告 | 🔵 建議 | ⚪ 無法標示 | | --- | --- | --- | --- | --- | | 新問題 | 0 筆 | 3 筆 | 1 筆 | 0 筆 | | 舊問題 | 2 筆 | 0 筆 | 0 筆 | 0 筆 | ## 🤖 AI 助理使用量 **本次審查**(opencode / gemini-2.5-flash,共 15 次呼叫) | 提示 token | 回應 token | 合計 | | --- | --- | --- | | 163,767 | 3,521 | 301,356 | **剩餘可用** 剩餘可用:無法計算百分比(自架服務,無帳號額度概念)
@@ -0,0 +1,42 @@
'use strict';
// 主分隔線與次分隔線寬度(沿用原 entrypoint.sh 的視覺樣式)
const LINE = '='.repeat(50);

嚴重等級🔵 建議
審查員:Leo
問題:日誌的分隔線寬度與符號直接硬編碼在模組內。如果未來需要調整輸出風格,需要修改多個地方,且容易造成視覺不一致。
建議:將分隔線寬度與符號定義為設定檔或在模組頂層集中管理,並考慮提供一個通用函數來產生這些分隔線。

**嚴重等級**:🔵 建議 **審查員**:Leo **問題**:日誌的分隔線寬度與符號直接硬編碼在模組內。如果未來需要調整輸出風格,需要修改多個地方,且容易造成視覺不一致。 **建議**:將分隔線寬度與符號定義為設定檔或在模組頂層集中管理,並考慮提供一個通用函數來產生這些分隔線。
Ghost marked this conversation as resolved
@@ -0,0 +13,4 @@
*/
function writeOutput(name, value, file = process.env.GITHUB_OUTPUT) {
if (!file) {
throw new Error('GITHUB_OUTPUT 未設定,無法寫入輸出');

嚴重等級🟡 警告
審查員:Assassin
問題:存在潛在的「任意檔案寫入」與「路徑穿越」風險。函式 writeOutput 直接接收 file 路徑並使用 fs.appendFileSync 進行寫入,且預設來源為環境變數 GITHUB_OUTPUT。若攻擊者能透過惡意配置篡改 CI/CD 環境變數,即可將任意資料寫入系統內的敏感檔案(如 /etc/passwd 或 SSH 授權金鑰),導致系統被入侵。
建議:應對 file 路徑增加嚴格的驗證機制(Sanitization),確保其位於合法的臨時目錄或 CI/CD 授權的輸出路徑內,嚴禁寫入任意系統檔案路徑。

**嚴重等級**:🟡 警告 **審查員**:Assassin **問題**:存在潛在的「任意檔案寫入」與「路徑穿越」風險。函式 `writeOutput` 直接接收 `file` 路徑並使用 `fs.appendFileSync` 進行寫入,且預設來源為環境變數 `GITHUB_OUTPUT`。若攻擊者能透過惡意配置篡改 CI/CD 環境變數,即可將任意資料寫入系統內的敏感檔案(如 /etc/passwd 或 SSH 授權金鑰),導致系統被入侵。 **建議**:應對 `file` 路徑增加嚴格的驗證機制(Sanitization),確保其位於合法的臨時目錄或 CI/CD 授權的輸出路徑內,嚴禁寫入任意系統檔案路徑。
Ghost marked this conversation as resolved
@@ -0,0 +65,4 @@
} catch {
throw new Error(`release API 回傳資料無法解析 (page=${page})`);
}

嚴重等級🟡 警告
審查員:Maya
問題:release API 解析 JSON 失敗時的例外路徑(Malformed JSON)未在測試中驗證,無法確認錯誤處理機制是否如預期運作。
建議:在 test/releases.test.js 中新增一個測試案例,模擬 response.text() 回傳無法解析為 JSON 的字串,並驗證是否拋出預期的錯誤。

**嚴重等級**:🟡 警告 **審查員**:Maya **問題**:release API 解析 JSON 失敗時的例外路徑(Malformed JSON)未在測試中驗證,無法確認錯誤處理機制是否如預期運作。 **建議**:在 test/releases.test.js 中新增一個測試案例,模擬 response.text() 回傳無法解析為 JSON 的字串,並驗證是否拋出預期的錯誤。
Ghost marked this conversation as resolved
@@ -0,0 +1,140 @@
'use strict';
// 每個版本號區段的進位上限:patch / minor 達到 10 即向上進位
const SEGMENT_LIMIT = 10;

嚴重等級🟡 警告
審查員:Leo
問題:版本號區段的進位上限(10)被直接硬編碼在程式中。若未來業務需求需要調整進位規則,需要深入程式碼核心修改,容易遺漏或出錯。
建議:建議將進位上限設為一個配置常數,或將其參數化,讓版本計算邏輯與進位策略分離。

**嚴重等級**:🟡 警告 **審查員**:Leo **問題**:版本號區段的進位上限(10)被直接硬編碼在程式中。若未來業務需求需要調整進位規則,需要深入程式碼核心修改,容易遺漏或出錯。 **建議**:建議將進位上限設為一個配置常數,或將其參數化,讓版本計算邏輯與進位策略分離。
Ghost marked this conversation as resolved
gitea-actions bot added 1 commit 2026-06-26 02:53:11 +00:00
jiantw83 added 2 commits 2026-06-26 02:59:39 +00:00
新增 fetchReleases 收到無法解析 JSON 時拋出「回傳資料無法解析」的測試案例。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chore(ai-review 狀態): 更新 findings.json 與 exclusions.json
CI / AI Code Review (pull_request) Successful in 38s
6f95a3fbe6
已解決 1 條(warning:補 JSON 解析失敗測試),其餘 5 條判定為誤報寫入 exclusions(critical x2 重複項、warning x2、info x1),findings 清空。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

🤖 AI Code Review 團隊

👤 角色 🎯 面向 🧠 個性
🗡️ Assassin security 多疑偏執、以攻擊者視角看世界,假設每筆輸入都是惡意的,每個信任都會被濫用
🎼 Bard style 唯美龜毛、追求優雅,把可讀性與一致性當作旋律,最受不了走調的命名與排版
🧰 Leo maintainability 有遠見、重視長期維護成本,凡事先問「六個月後的自己還看得懂嗎?」,討厭把債留給未來
🔮 Mage logic 嚴謹冷靜、滴水不漏,凡事推演到最壞情況,深信「沒驗證過的假設都是 bug」
🧪 Maya testing 對測試覆蓋率有執念,深信「沒有測試的程式碼等於沒寫完」,溫和但堅持,最在意邊界與失敗路徑
Rogue efficiency 急性子、講求速度,最痛恨被浪費的 CPU 週期與記憶體,凡事先問「這能不能更快、更省」

🔍 服務:opencode 模型:gemini-2.5-flash

## 🤖 AI Code Review 團隊 | 👤 角色 | 🎯 面向 | 🧠 個性 | |--------|--------|--------| | **🗡️ Assassin** | security | 多疑偏執、以攻擊者視角看世界,假設每筆輸入都是惡意的,每個信任都會被濫用 | | **🎼 Bard** | style | 唯美龜毛、追求優雅,把可讀性與一致性當作旋律,最受不了走調的命名與排版 | | **🧰 Leo** | maintainability | 有遠見、重視長期維護成本,凡事先問「六個月後的自己還看得懂嗎?」,討厭把債留給未來 | | **🔮 Mage** | logic | 嚴謹冷靜、滴水不漏,凡事推演到最壞情況,深信「沒驗證過的假設都是 bug」 | | **🧪 Maya** | testing | 對測試覆蓋率有執念,深信「沒有測試的程式碼等於沒寫完」,溫和但堅持,最在意邊界與失敗路徑 | | **⚡ Rogue** | efficiency | 急性子、講求速度,最痛恨被浪費的 CPU 週期與記憶體,凡事先問「這能不能更快、更省」 | > 🔍 服務:opencode 模型:gemini-2.5-flash
gitea-actions bot reviewed 2026-06-26 03:00:16 +00:00
gitea-actions bot left a comment

AI Code Review 統計

類型 🔴 嚴重 🟡 警告 🔵 建議 無法標示
新問題 0 筆 3 筆 0 筆 0 筆
舊問題 0 筆 0 筆 0 筆 0 筆

🤖 AI 助理使用量

本次審查(opencode / gemini-2.5-flash,共 12 次呼叫)

提示 token 回應 token 合計
151,746 3,857 258,708

剩餘可用

剩餘可用:無法計算百分比(自架服務,無帳號額度概念)

## AI Code Review 統計 | 類型 | 🔴 嚴重 | 🟡 警告 | 🔵 建議 | ⚪ 無法標示 | | --- | --- | --- | --- | --- | | 新問題 | 0 筆 | 3 筆 | 0 筆 | 0 筆 | | 舊問題 | 0 筆 | 0 筆 | 0 筆 | 0 筆 | ## 🤖 AI 助理使用量 **本次審查**(opencode / gemini-2.5-flash,共 12 次呼叫) | 提示 token | 回應 token | 合計 | | --- | --- | --- | | 151,746 | 3,857 | 258,708 | **剩餘可用** 剩餘可用:無法計算百分比(自架服務,無帳號額度概念)
@@ -0,0 +68,4 @@
function loadConfig(env = process.env) {
const serverUrl = requireEnv('GITEA_SERVER_URL', env.GITEA_SERVER_URL);
assertHttpUrl('GITEA_SERVER_URL', serverUrl);
const repository = requireEnv('GITEA_REPOSITORY', env.GITEA_REPOSITORY);

嚴重等級🟡 警告
審查員:Assassin
問題:對 GITEA_REPOSITORY 環境變數缺乏輸入驗證。由於此值會直接拼接於 API URL 中(見 app/index.js:37),若攻擊者傳入特殊字元或路徑穿越字元(如 ../),可能導致 API 請求路徑異常,甚至造成非預期的 API 端點存取。
建議:增加格式驗證機制,使用嚴格的正則表達式限制 GITEA_REPOSITORY 格式(例如確保只包含合法的 repo 名稱字元:^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$),拒絕任何不符合規範的輸入。

**嚴重等級**:🟡 警告 **審查員**:Assassin **問題**:對 GITEA_REPOSITORY 環境變數缺乏輸入驗證。由於此值會直接拼接於 API URL 中(見 app/index.js:37),若攻擊者傳入特殊字元或路徑穿越字元(如 `../`),可能導致 API 請求路徑異常,甚至造成非預期的 API 端點存取。 **建議**:增加格式驗證機制,使用嚴格的正則表達式限制 GITEA_REPOSITORY 格式(例如確保只包含合法的 repo 名稱字元:`^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$`),拒絕任何不符合規範的輸入。
@@ -0,0 +1,42 @@
'use strict';

嚴重等級🟡 警告
審查員:Maya
問題:整個 logger.js 模組完全沒有測試,無法確保 section、info 與 error 函式是否正確將訊息格式化並寫入標準輸出與標準錯誤。
建議:為 app/logger.js 新增測試,模擬 process.stdout 與 process.stderr,驗證輸出的字串格式是否符合預期(例如分隔線寬度、前綴是否正確)。

**嚴重等級**:🟡 警告 **審查員**:Maya **問題**:整個 logger.js 模組完全沒有測試,無法確保 section、info 與 error 函式是否正確將訊息格式化並寫入標準輸出與標準錯誤。 **建議**:為 app/logger.js 新增測試,模擬 process.stdout 與 process.stderr,驗證輸出的字串格式是否符合預期(例如分隔線寬度、前綴是否正確)。
@@ -0,0 +74,4 @@
throw new Error(`release API 回傳非陣列資料 (page=${page})`);
}
const count = pageJson.length;

嚴重等級🟡 警告
審查員:Leo
問題:JSON.parse 失敗時,僅拋出通用錯誤訊息,未來除錯時無法得知具體回傳內容,將導致除錯時浪費大量時間追查。
建議:建議將錯誤訊息擴充,納入部分的 response body 內容,以利於快速定位回傳格式異常的確切原因。

**嚴重等級**:🟡 警告 **審查員**:Leo **問題**:JSON.parse 失敗時,僅拋出通用錯誤訊息,未來除錯時無法得知具體回傳內容,將導致除錯時浪費大量時間追查。 **建議**:建議將錯誤訊息擴充,納入部分的 response body 內容,以利於快速定位回傳格式異常的確切原因。
gitea-actions bot added 1 commit 2026-06-26 03:00:19 +00:00
admin merged commit 8846b6d386 into develop 2026-06-26 03:00:32 +00:00
admin deleted branch ai-review-resolve/develop-20260626-104052 2026-06-26 03:00:33 +00:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: old-docker-actions/calculate-version#5