diff --git a/app/config.js b/app/config.js index 4e37a2a..8de6c1f 100644 --- a/app/config.js +++ b/app/config.js @@ -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 區段是否合法:長度在範圍內、僅含允許字元,且非 . 或 .. 路徑穿越段 +function isValidRepoSegment(part) { + return part.length > 0 + && part.length <= MAX_REPO_NAME_LENGTH + && 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`); } diff --git a/app/releases.js b/app/releases.js index 51982d3..0077843 100644 --- a/app/releases.js +++ b/app/releases.js @@ -66,8 +66,10 @@ async function fetchReleases(baseUrl, options = {}) { try { pageJson = JSON.parse(text); } catch { - // 以字元(而非 UTF-16 碼元)截斷回傳內容片段,避免拆分多位元組字元造成亂碼 - const contentSnippet = Array.from(text).slice(0, API_ERROR_SNIPPET_LENGTH).join(''); + // 先以 UTF-16 長度粗略上限截斷(避免將可能極大的回應整個陣列化), + // 再以字元(而非 UTF-16 碼元)精準截斷,避免拆分多位元組字元造成亂碼 + const boundedText = text.slice(0, API_ERROR_SNIPPET_LENGTH * 2); + const contentSnippet = Array.from(boundedText).slice(0, API_ERROR_SNIPPET_LENGTH).join(''); throw new Error(`release API 回傳資料無法解析 (page=${page}),回應內容片段:「${contentSnippet}」`); }