fix: 修复代码审查中发现的关键问题
1. 修复方法名错误 - 将 performScheduledHealthChecks() 改为 performHealthChecks() - 修复启动时健康检查调用错误 2. 改进路径遍历防护 - 仅在 Windows 平台执行小写转换 - 避免 Linux 平台路径大小写误判 3. 增强 XSS 防护 - 扩展危险协议检测(data/javascript/vbscript) - 使用更安全的 HTML 标签移除方式 - 添加 HTML 实体编码攻击防护 4. 为文件锁添加超时机制 - 防止操作永久挂起导致锁链阻塞 - 默认超时时间 30 秒
This commit is contained in:
parent
1018750388
commit
bc018cf16f
3 changed files with 35 additions and 14 deletions
|
|
@ -428,7 +428,7 @@ async function startServer() {
|
|||
logger.info('[ScheduledHealthCheck] Running scheduled health check on startup...');
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await poolManager.performScheduledHealthChecks();
|
||||
await poolManager.performHealthChecks();
|
||||
} catch (error) {
|
||||
logger.error('[ScheduledHealthCheck] Startup run error:', error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,9 +134,10 @@ export async function handleUpdateConfig(req, res, currentConfig) {
|
|||
const relativePath = path.relative(cwd, resolved);
|
||||
const isInsideCwd = !path.isAbsolute(relativePath) && !relativePath.startsWith('..') && relativePath !== '..';
|
||||
|
||||
// Windows 大小写不敏感兼容:统一转换为小写比较
|
||||
const normalizedResolved = resolved.toLowerCase().replace(/\\/g, '/');
|
||||
const normalizedCwd = cwd.toLowerCase().replace(/\\/g, '/');
|
||||
// Windows 大小写不敏感兼容:仅在 Windows 平台统一转换为小写比较
|
||||
const isWindows = process.platform === 'win32';
|
||||
const normalizedResolved = (isWindows ? resolved.toLowerCase() : resolved).replace(/\\/g, '/');
|
||||
const normalizedCwd = (isWindows ? cwd.toLowerCase() : cwd).replace(/\\/g, '/');
|
||||
const startsWithCwd = normalizedResolved.startsWith(normalizedCwd + '/') || normalizedResolved === normalizedCwd;
|
||||
|
||||
if (isInsideCwd && startsWithCwd) {
|
||||
|
|
@ -188,9 +189,10 @@ export async function handleUpdateConfig(req, res, currentConfig) {
|
|||
const relativePath = path.relative(cwd, resolved);
|
||||
const isInsideCwd = !path.isAbsolute(relativePath) && !relativePath.startsWith('..') && relativePath !== '..';
|
||||
|
||||
// Windows 大小写不敏感兼容:统一转换为小写比较
|
||||
const normalizedResolved = resolved.toLowerCase().replace(/\\/g, '/');
|
||||
const normalizedCwd = cwd.toLowerCase().replace(/\\/g, '/');
|
||||
// Windows 大小写不敏感兼容:仅在 Windows 平台统一转换为小写比较
|
||||
const isWindows = process.platform === 'win32';
|
||||
const normalizedResolved = (isWindows ? resolved.toLowerCase() : resolved).replace(/\\/g, '/');
|
||||
const normalizedCwd = (isWindows ? cwd.toLowerCase() : cwd).replace(/\\/g, '/');
|
||||
const startsWithCwd = normalizedResolved.startsWith(normalizedCwd + '/') || normalizedResolved === normalizedCwd;
|
||||
|
||||
if (isInsideCwd && startsWithCwd) {
|
||||
|
|
|
|||
|
|
@ -14,14 +14,22 @@ function sanitizeProviderData(provider) {
|
|||
const sanitized = { ...provider };
|
||||
if (typeof sanitized.customName === 'string') {
|
||||
let name = sanitized.customName;
|
||||
// 拒绝包含 data: 协议(可能包含内嵌恶意内容)
|
||||
if (/data\s*:/i.test(name)) return sanitized;
|
||||
// 移除 <script>...</script>(支持跨行匹配)
|
||||
name = name.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
|
||||
|
||||
// 拒绝包含危险协议
|
||||
if (/(?:data|javascript|vbscript)\s*:/i.test(name)) {
|
||||
sanitized.customName = '';
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
// 移除所有 HTML 标签(更安全的方式)
|
||||
name = name.replace(/<[^>]*>/g, '');
|
||||
|
||||
// 移除 HTML 事件处理器属性(onclick/onerror 等)
|
||||
name = name.replace(/\s+on\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]*)/gi, '');
|
||||
// 移除 javascript: 协议(更严格:要求独立单词边界,防止误匹配 "not javascript code")
|
||||
name = name.replace(/\bjavascript\s*:/gi, '');
|
||||
|
||||
// 移除潜在的 HTML 实体编码攻击
|
||||
name = name.replace(/&[#\w]+;/g, '');
|
||||
|
||||
sanitized.customName = name.trim();
|
||||
}
|
||||
return sanitized;
|
||||
|
|
@ -39,9 +47,20 @@ function sanitizeProviderPools(pools) {
|
|||
}
|
||||
// 使用 Promise 链式队列,确保文件操作顺序执行
|
||||
let _fileLockChain = Promise.resolve();
|
||||
|
||||
// 超时包装函数:防止操作永久挂起导致锁链阻塞
|
||||
function withTimeout(promise, ms = 30000) {
|
||||
return Promise.race([
|
||||
promise,
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error(`Operation timeout after ${ms}ms`)), ms)
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
function withFileLock(fn) {
|
||||
const next = _fileLockChain
|
||||
.then(() => fn())
|
||||
.then(() => withTimeout(fn(), 30000))
|
||||
.catch(err => {
|
||||
// 记录错误并抛出,中断操作
|
||||
logger.error('[FileLock] Operation failed:', err?.message || err);
|
||||
|
|
|
|||
Loading…
Reference in a new issue