From 4aa1393af2589e8f99794cd06798a4df30d93a1b Mon Sep 17 00:00:00 2001 From: Jeffery Date: Fri, 26 Jun 2026 14:14:30 +0800 Subject: [PATCH] =?UTF-8?q?refactor(calculate-version):=20=E5=BC=B7?= =?UTF-8?q?=E5=8C=96=E7=89=87=E6=AE=B5=E6=88=AA=E6=96=B7=E8=A8=98=E6=86=B6?= =?UTF-8?q?=E9=AB=94=E5=AE=89=E5=85=A8=E8=88=87=E6=8A=BD=E5=87=BA=E5=8D=80?= =?UTF-8?q?=E6=AE=B5=E9=A9=97=E8=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - releases 在 Array.from 前先以長度上限截斷字串,避免將可能極大的回應整個陣列化 - config 將 repository 區段驗證抽為具名函式 isValidRepoSegment 提升可讀性 Co-Authored-By: Claude Opus 4.8 (1M context) --- app/config.js | 15 ++++++++++----- app/releases.js | 6 ++++-- 2 files changed, 14 insertions(+), 7 deletions(-) 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}」`); }