// 參數驗證,對應原本 entrypoint.sh 的 is_empty_or_null/require_value/require_integer。 // 驗證失敗時丟出 Error,由進入點統一捕捉後以非零狀態結束。 // 允許的 URL 協定;集中為常數,方便檢視系統允許的協定與日後擴充。 const ALLOWED_URL_PROTOCOLS = ['http:', 'https:'] // 合法 repository 形式:owner/repo,僅允許英數字與 . _ -,且恰好一個 /。 const REPOSITORY_PATTERN = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/ /** * 判斷值是否視為「空」。使用嚴格相等,因此 `0`、`false`、字串 `"0"` 都不算空。 * @param {*} value 待判斷的值 * @returns {boolean} 當值為 `undefined`、`null`、空字串或字串 `"null"` 時回傳 true */ export function isEmptyOrNull(value) { return value === undefined || value === null || value === '' || value === 'null' } /** * 要求指定欄位有值,空值時丟出 Error 以中止流程。 * @param {string} name 欄位名稱,用於組出錯誤訊息 * @param {*} value 待檢查的值,空值判定委派給 [[isEmptyOrNull]] * @throws {Error} 當 value 為空時丟出 `${name} is required` */ export function requireValue(name, value) { if (isEmptyOrNull(value)) { throw new Error(`${name} is required`) } } /** * 要求指定欄位為非負整數。會先轉成字串再以 `/^[0-9]+$/` 比對,因此可接受數字或純數字字串, * 但拒絕負數、小數、空值與非數字內容。 * @param {string} name 欄位名稱,用於組出錯誤訊息 * @param {string|number} value 待檢查的值 * @throws {Error} 當 value 不是非負整數時丟出 `${name} must be a non-negative integer` */ export function requireInteger(name, value) { if (!/^[0-9]+$/.test(String(value))) { throw new Error(`${name} must be a non-negative integer`) } } /** * 要求值為合法的 http/https URL,藉此避免設定來源指向格式錯誤或非預期協定的伺服器(降低 SSRF 風險)。 * @param {string} name 欄位名稱,用於組出錯誤訊息 * @param {string} value 待檢查的 URL 字串 * @throws {Error} 當 value 無法解析為 URL 時丟出 `${name} must be a valid URL`; * 協定非 http/https 時丟出 `${name} must use http or https protocol` */ export function requireUrl(name, value) { let url try { url = new URL(value) } catch { throw new Error(`${name} must be a valid URL`) } if (!ALLOWED_URL_PROTOCOLS.includes(url.protocol)) { throw new Error(`${name} must use http or https protocol`) } } /** * 要求值為合法的 `owner/repo` 形式:僅允許英數字與 `. _ -`、恰好一個 `/`,且不可含路徑穿越片段 `..`。 * 用於防止 GITEA_REPOSITORY 被竄改造成 API 目標被導向其他儲存庫。 * @param {string} name 欄位名稱,用於組出錯誤訊息 * @param {string} value 待檢查的 repository 字串 * @throws {Error} 格式不符或含 `..` 時丟出 `${name} must be in the form owner/repo without path traversal` */ export function requireRepository(name, value) { if (String(value).includes('..') || !REPOSITORY_PATTERN.test(value)) { throw new Error(`${name} must be in the form owner/repo without path traversal`) } }