Files
release-cleanup/app/validate.js
T
JefferyandClaude Opus 4.8 154ab032bf fix(release-cleanup): 強化輸入驗證、請求逾時與錯誤處理
解決 AI review 的安全性與健壯性問題:
- 驗證 GITEA_SERVER_URL 為合法 http/https URL(降低 SSRF 風險)
- 驗證 GITEA_REPOSITORY 為 owner/repo 形式並拒絕路徑穿越
- 為所有 fetch 請求加上 30 秒逾時(AbortSignal.timeout),避免卡死
- fetchAllPages 解析前檢查 content-type,非 JSON 時拋出明確錯誤
- selectReleasesToDelete 對無效 created_at 防呆,避免 NaN 排序
- index.js 匯出 main 並加上直接執行守衛,錯誤輸出含類型與堆疊

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 10:55:45 +08:00

69 lines
2.8 KiB
JavaScript

// 參數驗證,對應原本 entrypoint.sh 的 is_empty_or_null/require_value/require_integer。
// 驗證失敗時丟出 Error,由進入點統一捕捉後以非零狀態結束。
/**
* 判斷值是否視為「空」。使用嚴格相等,因此 `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 (url.protocol !== 'http:' && url.protocol !== 'https:') {
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('..') || !/^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/.test(value)) {
throw new Error(`${name} must be in the form owner/repo without path traversal`)
}
}