test: 整併測試至 app/test 並解決 AI 審查 findings #8
@@ -59,13 +59,18 @@ function assertHttpUrl(name, value) {
|
||||
const MAX_REPO_NAME_LENGTH = 100;
|
||||
const REPO_SEGMENT_PATTERN = /^[A-Za-z0-9._-]+$/;
|
||||
|
||||
// 驗證 repository 為 owner/repo 格式(拒絕 . 與 .. 路徑穿越段、空段、過長或含非法字元的段)
|
||||
// 單一 repository 區段是否合法:長度在範圍內、僅含允許字元,且非 . 或 .. 路徑穿越段
|
||||
|
Ghost marked this conversation as resolved
Outdated
|
||||
function isValidRepoSegment(part) {
|
||||
|
Ghost marked this conversation as resolved
Outdated
gitea-actions
commented
嚴重等級:🔴 嚴重 **嚴重等級**:🔴 嚴重
**審查員**:Mage
**問題**:在 `assertRepository` 函數中,`part !== '.' && part !== '..'` 的檢查雖然能防止路徑穿越,但正規表達式 `^[A-Za-z0-9._-]+$` 仍然允許 `.` 開頭或結尾的檔案名稱(例如 `.foo` 或 `foo.`),這在某些檔案系統中可能導致意外行為或被視為隱藏檔案,且未明確禁止僅由 `.` 組成的名稱(儘管後面有手動排除)。
**建議**:建議調整正規表達式,禁止名稱以點號開頭或結尾,例如使用 `/^[A-Za-z0-9_-]+([.][A-Za-z0-9_-]+)*$/` 來確保點號只能存在於中間。
gitea-actions
commented
嚴重等級:🟡 警告 **嚴重等級**:🟡 警告
**審查員**:Leo
**問題**:在 `assertRepository` 函式中使用了硬編碼的 `100` 作為長度限制,這是一個魔術數字,難以維護且無法從環境配置中調整。
**建議**:建議將 `100` 抽離為常數(例如 `MAX_REPO_NAME_LENGTH`),提高可維護性。
gitea-actions
commented
嚴重等級:🔵 建議 **嚴重等級**:🔵 建議
**審查員**:Bard
**問題**:正規表達式 `/^[A-Za-z0-9._-]+$/` 描述稍顯冗長,且未利用字元類別的特性,略顯不夠優雅。
**建議**:考慮簡化寫法,或確保該格式驗證邏輯與專案其他地方一致並抽取為常數。
gitea-actions
commented
嚴重等級:🔵 建議 **嚴重等級**:🔵 建議
**審查員**:Leo
**問題**:驗證邏輯 parts.every 內包含過多條件,邏輯稍顯複雜,閱讀起來需要花點時間拆解。
**建議**:將 parts.every 的回呼函數提取為具名函數(例如 isValidPart),以提升代碼的可讀性與未來維護的清晰度。
gitea-actions
commented
嚴重等級:🔵 建議 **嚴重等級**:🔵 建議
**審查員**:Leo
**問題**:在 `assertGiteaRepositoryFormat` 函式中,當 `value.split('/')` 的長度不為 2 時,拋出的錯誤訊息僅籠統地說「格式錯誤」。若使用者輸入了包含多個斜線或完全沒有斜線的字串,這類訊息對修正環境變數幫助有限。
**建議**:建議區分「格式不符」與「內容不符」的錯誤細節,例如提示「必須為 owner/repo 格式,包含一個斜線」。
|
||||
return part.length > 0
|
||||
&& part.length <= MAX_REPO_NAME_LENGTH
|
||||
|
Ghost marked this conversation as resolved
Outdated
gitea-actions
commented
嚴重等級:🟡 警告 **嚴重等級**:🟡 警告
**審查員**:Rogue
**問題**:在 `every` 迴圈中重複宣告與編譯 Regex `/^[A-Za-z0-9._-]+$/`,若該函數被頻繁呼叫,將造成無謂的 CPU 與記憶體浪費。
**建議**:將 Regex 移至函數外層宣告為常數。
gitea-actions
commented
嚴重等級:🟡 警告 **嚴重等級**:🟡 警告
**審查員**:Bard
**問題**:函數 `assertRepository` 命名較為通用,但其實際行為僅在驗證 Gitea 的 `owner/repo` 格式。命名未能直接體現其檢查邏輯與該領域規則。
**建議**:建議重新命名為 `assertGiteaRepositoryFormat`,讓開發者一眼就能看出該函數在檢查特定的 Gitea 倉庫格式規範。
|
||||
&& REPO_SEGMENT_PATTERN.test(part)
|
||||
&& part !== '.' && part !== '..';
|
||||
}
|
||||
|
||||
// 驗證 repository 為 owner/repo 格式(恰兩段,且每段皆為合法區段)
|
||||
function assertRepository(name, value) {
|
||||
const parts = value.split('/');
|
||||
const isRepoFormatValid = parts.length === 2
|
||||
&& parts.every((part) => part.length > 0 && part.length <= MAX_REPO_NAME_LENGTH
|
||||
&& REPO_SEGMENT_PATTERN.test(part)
|
||||
&& part !== '.' && part !== '..');
|
||||
const isRepoFormatValid = parts.length === 2 && parts.every(isValidRepoSegment);
|
||||
if (!isRepoFormatValid) {
|
||||
throw new Error(`${name} 格式錯誤,必須為 owner/repo`);
|
||||
}
|
||||
|
||||
@@ -66,8 +66,10 @@ async function fetchReleases(baseUrl, options = {}) {
|
||||
try {
|
||||
pageJson = JSON.parse(text);
|
||||
|
Ghost marked this conversation as resolved
Outdated
gitea-actions
commented
嚴重等級:🔵 建議 **嚴重等級**:🔵 建議
**審查員**:Leo
**問題**:硬編碼了截斷字串長度 `200`,這是一個魔術數字,若未來需要調整截斷長度以容納更多偵錯資訊或減少輸出,維護者需要直接修改此處邏輯。
**建議**:建議定義一個具名的常數(例如 `API_ERROR_SNIPPET_LENGTH`),讓此數字具備語意,且未來調整時只需改動一處。
|
||||
} catch {
|
||||
|
Ghost marked this conversation as resolved
Outdated
gitea-actions
commented
嚴重等級:🔵 建議 **嚴重等級**:🔵 建議
**審查員**:Assassin
**問題**:將未經清洗的 API 回應片段直接納入 `Error` 物件,若 API 返回敏感資訊(內部路徑、堆疊追蹤、使用者資料),可能導致敏感資訊外洩。
**建議**:錯誤報告應僅包含概括性的描述,若必須包含內容片段,務必先對片段進行內容脫敏或格式化處理。
|
||||
// 以字元(而非 UTF-16 碼元)截斷回傳內容片段,避免拆分多位元組字元造成亂碼
|
||||
const contentSnippet = Array.from(text).slice(0, API_ERROR_SNIPPET_LENGTH).join('');
|
||||
// 先以 UTF-16 長度粗略上限截斷(避免將可能極大的回應整個陣列化),
|
||||
|
Ghost marked this conversation as resolved
Outdated
gitea-actions
commented
嚴重等級:🟡 警告 **嚴重等級**:🟡 警告
**審查員**:Maya
**問題**:雖然實作了針對多位元組字元的截斷處理邏輯,但目前的測試案例僅使用 ASCII 字元('x'),缺乏對於包含多位元組字元(如 Emoji 或特殊符號)的真實邊界情境驗證,無法確保在截斷邊界處不會產生亂碼或非預期的行為。
**建議**:請在 app/test/releases.test.js 中新增一個測試案例,使用包含多位元組字元(例如 Emoji 或代理對字元)的長字串作為 fetch 回應內容,並驗證截斷後的內容片段是否完整且無亂碼。
|
||||
// 再以字元(而非 UTF-16 碼元)精準截斷,避免拆分多位元組字元造成亂碼
|
||||
|
Ghost marked this conversation as resolved
Outdated
gitea-actions
commented
嚴重等級:🟡 警告 **嚴重等級**:🟡 警告
**審查員**:Leo
**問題**:為了處理多位元組字元而使用 Array.from(text),如果回傳的錯誤內容(text)非常巨大(例如幾十 MB 的 HTML 錯誤頁面),會瞬間配置大量記憶體,可能導致效能瓶頸。
**建議**:若預期回應可能非常大,建議先檢查 text.length 是否遠大於限制,或是考慮使用更輕量的字串處理方式來避免不必要的記憶體配置。
gitea-actions
commented
嚴重等級:🔵 建議 **嚴重等級**:🔵 建議
**審查員**:Leo
**問題**:在錯誤訊息中進行字串截斷處理,程式碼寫得較為複雜,使用了 Array.from(boundedText).slice(...)。雖然考慮了 UTF-16 碼元問題,但這段邏輯與核心業務功能(fetchReleases)混在一起,未來維護者若需調整錯誤訊息格式,容易誤傷功能。
**建議**:將此段截斷邏輯抽取為一個獨立的工具函式,例如 truncateString(text, limit),放在 utils 檔案中以提升可讀性與測試獨立性。
|
||||
const boundedText = text.slice(0, API_ERROR_SNIPPET_LENGTH * 2);
|
||||
const contentSnippet = Array.from(boundedText).slice(0, API_ERROR_SNIPPET_LENGTH).join('');
|
||||
|
Ghost marked this conversation as resolved
Outdated
gitea-actions
commented
嚴重等級:🟡 警告 **嚴重等級**:🟡 警告
**審查員**:Mage
**問題**:雖然使用 `Array.from(text)` 可以正確處理多位元組字元(避免在 Unicode code point 中間截斷),但此操作會將整個回應內容轉為陣列,若 API 回傳的錯誤回應內容極大(例如數 MB 的 HTML 錯誤頁面),這會導致嚴重的記憶體耗用,甚至造成執行緒卡頓或記憶體溢位 (OOM)。
**建議**:應先對 `text` 進行長度檢查,或者改用 `Buffer` 或 `string.substring()` 搭配適當的 Unicode 感知邏輯來截斷,而不是將完整內容陣列化。
|
||||
throw new Error(`release API 回傳資料無法解析 (page=${page}),回應內容片段:「${contentSnippet}」`);
|
||||
}
|
||||
|
||||
|
||||
嚴重等級:🔴 嚴重
審查員:Maya
問題:新增的
assertRepository函數對GITEA_REPOSITORY的檢查邏輯並未測試到所有邊界條件,例如:包含特殊字元(非字母數字點下底線連字號)、極長字串段(> 100 字元)、以及完全空白的value。建議:請在
app/test/config.test.js中增加針對非法字元、超過 100 字元的字串段,以及空字串的測試案例,確保assertRepository的防禦性功能完全被驗證。嚴重等級:🟡 警告
審查員:Mage
問題:在
assertRepository中,parts.every僅檢查了單一區段長度,未對總體長度(owner/repo)做任何限制。建議:建議在分割前,先對
value的總長度進行合理範圍的限制。