fix: 修复代码审查发现的5个安全与正确性问题
- pbkdf2Sync 改为异步避免阻塞事件循环 (auth.js, config-api.js) - 路径遍历检查改用 path.resolve 验证绝对路径在 cwd 内 (config-api.js) - _activeInterval 移出配置对象避免序列化到 JSON (config-api.js, api-server.js) - 删除 performScheduledHealthChecks 中冗余的 isDisabled 二次检查 (provider-pool-manager.js)
This commit is contained in:
parent
fca9413f26
commit
9d4864dfed
4 changed files with 22 additions and 16 deletions
|
|
@ -1834,11 +1834,6 @@ export class ProviderPoolManager {
|
|||
let failCount = 0;
|
||||
|
||||
for (const { providerType, provider, uuid, customName } of providersToCheck) {
|
||||
// Skip if provider became disabled during iteration
|
||||
if (provider.config.isDisabled === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const providerCheckStart = Date.now();
|
||||
const checkModelName = provider.config.checkModelName || ProviderPoolManager.DEFAULT_HEALTH_CHECK_MODELS[providerType] || 'unknown';
|
||||
const displayName = customName || uuid.substring(0, 8);
|
||||
|
|
|
|||
|
|
@ -420,9 +420,10 @@ async function startServer() {
|
|||
|
||||
// 设置定时任务
|
||||
runHealthCheckTimer(interval);
|
||||
|
||||
// 导出重载函数供外部调用
|
||||
|
||||
// 注册重载函数和初始 interval 到 globalThis(供 config-api 热更新使用)
|
||||
globalThis.reloadHealthCheckTimer = runHealthCheckTimer;
|
||||
globalThis._activeHealthCheckInterval = interval;
|
||||
}
|
||||
|
||||
// 如果是子进程,通知主进程已就绪
|
||||
|
|
|
|||
|
|
@ -55,7 +55,11 @@ export async function validateCredentials(password) {
|
|||
const parts = storedPassword.split(':');
|
||||
if (parts.length !== 3) return false;
|
||||
const [, salt, storedHash] = parts;
|
||||
const inputHash = crypto.pbkdf2Sync(password.trim(), salt, 100000, 64, 'sha512').toString('hex');
|
||||
const inputHash = await new Promise((resolve, reject) =>
|
||||
crypto.pbkdf2(password.trim(), salt, 100000, 64, 'sha512', (err, key) =>
|
||||
err ? reject(err) : resolve(key.toString('hex'))
|
||||
)
|
||||
);
|
||||
return crypto.timingSafeEqual(Buffer.from(inputHash, 'hex'), Buffer.from(storedHash, 'hex'));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -125,8 +125,11 @@ export async function handleUpdateConfig(req, res, currentConfig) {
|
|||
if (newConfig.MODEL_PROVIDER !== undefined) currentConfig.MODEL_PROVIDER = newConfig.MODEL_PROVIDER;
|
||||
if (newConfig.SYSTEM_PROMPT_FILE_PATH !== undefined) {
|
||||
const p = String(newConfig.SYSTEM_PROMPT_FILE_PATH);
|
||||
// 防止路径遍历:只允许相对路径或限定目录
|
||||
if (!p.includes('..')) currentConfig.SYSTEM_PROMPT_FILE_PATH = p;
|
||||
// 防止路径遍历:解析后的绝对路径必须在工作目录内
|
||||
const resolved = path.resolve(process.cwd(), p);
|
||||
if (resolved.startsWith(process.cwd() + path.sep) || resolved === process.cwd()) {
|
||||
currentConfig.SYSTEM_PROMPT_FILE_PATH = p;
|
||||
}
|
||||
}
|
||||
if (newConfig.SYSTEM_PROMPT_MODE !== undefined) currentConfig.SYSTEM_PROMPT_MODE = newConfig.SYSTEM_PROMPT_MODE;
|
||||
if (newConfig.PROMPT_LOG_BASE_NAME !== undefined) currentConfig.PROMPT_LOG_BASE_NAME = newConfig.PROMPT_LOG_BASE_NAME;
|
||||
|
|
@ -180,11 +183,10 @@ export async function handleUpdateConfig(req, res, currentConfig) {
|
|||
interval: newInterval,
|
||||
providerTypes: Array.isArray(incoming?.providerTypes) ? incoming.providerTypes : []
|
||||
};
|
||||
|
||||
// 如果定时器已存在且 enabled,仅在 interval 实际变化时重新加载 timer
|
||||
const previousInterval = currentConfig.SCHEDULED_HEALTH_CHECK._activeInterval;
|
||||
if (globalThis.reloadHealthCheckTimer && currentConfig.SCHEDULED_HEALTH_CHECK.enabled && newInterval !== previousInterval) {
|
||||
currentConfig.SCHEDULED_HEALTH_CHECK._activeInterval = newInterval;
|
||||
|
||||
// 仅在 interval 实际变化时重新加载 timer(_activeInterval 存在内存变量中,不写入配置文件)
|
||||
if (globalThis.reloadHealthCheckTimer && currentConfig.SCHEDULED_HEALTH_CHECK.enabled && newInterval !== globalThis._activeHealthCheckInterval) {
|
||||
globalThis._activeHealthCheckInterval = newInterval;
|
||||
globalThis.reloadHealthCheckTimer(newInterval);
|
||||
}
|
||||
}
|
||||
|
|
@ -353,7 +355,11 @@ export async function handleUpdateAdminPassword(req, res) {
|
|||
|
||||
// 使用 PBKDF2 哈希存储密码,避免明文写入文件
|
||||
const salt = crypto.randomBytes(16).toString('hex');
|
||||
const hash = crypto.pbkdf2Sync(password.trim(), salt, 100000, 64, 'sha512').toString('hex');
|
||||
const hash = await new Promise((resolve, reject) =>
|
||||
crypto.pbkdf2(password.trim(), salt, 100000, 64, 'sha512', (err, key) =>
|
||||
err ? reject(err) : resolve(key.toString('hex'))
|
||||
)
|
||||
);
|
||||
const stored = `pbkdf2:${salt}:${hash}`;
|
||||
|
||||
const pwdFilePath = path.join(process.cwd(), 'configs', 'pwd');
|
||||
|
|
|
|||
Loading…
Reference in a new issue