fix(security): 修复多个严重安全漏洞和运行时崩溃
- devices.js: 修复 hasRoomField 重复 const 声明导致模块加载失败 - devices.js: 修复 batch-status 路由 affectedCount 未定义导致崩溃 - devices.js: 修复 sequelize.literal SQL 注入,转义 LIKE 通配符和字段名 - server.js: 修复 initializeApp() 未 await 导致服务器在初始化前接收请求 - backup.js: 添加 resolveBackupPath() 防止路径遍历攻击(6处) - inventory.js: 用 bulkCreate 替代原始 SQL 拼接,消除 SQL 注入 - systemSettings.js: 添加全局 authMiddleware,修复敏感操作无认证 - dangerousOperations.js: 添加全局认证,修复日志清理功能完全不可用 - background.js: 添加文件名安全检查、扩展名白名单、路径校验
This commit is contained in:
@@ -532,7 +532,12 @@ router.get('/', validateQuery(queryDeviceSchema), async (req, res) => {
|
||||
console.log('数据库类型:', dbDialect);
|
||||
|
||||
// 转义关键词中的特殊字符,防止SQL注入
|
||||
const escapedKeyword = keyword.replace(/'/g, "''");
|
||||
// 转义单引号(SQL注入)和 LIKE 通配符(%、_)
|
||||
const escapedKeyword = keyword
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/'/g, "''")
|
||||
.replace(/%/g, '\\%')
|
||||
.replace(/_/g, '\\_');
|
||||
|
||||
// 基础字段搜索条件(只包含文本类型字段)
|
||||
const searchConditions = [
|
||||
@@ -562,15 +567,20 @@ router.get('/', validateQuery(queryDeviceSchema), async (req, res) => {
|
||||
if (customFields.length > 0) {
|
||||
// 使用原始SQL查询JSON字段
|
||||
const jsonConditions = customFields.map(field => {
|
||||
const fieldName = field.fieldName;
|
||||
// 转义 fieldName 中的特殊字符,防止通过字段名注入
|
||||
const safeFieldName = field.fieldName
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/"/g, '\\"')
|
||||
.replace(/'/g, "''");
|
||||
|
||||
// 使用 sequelize.literal 构建原始SQL条件
|
||||
if (dbDialect === 'mysql') {
|
||||
return sequelize.literal(
|
||||
`JSON_EXTRACT(customFields, '$."${fieldName}"') LIKE '%${escapedKeyword}%'`
|
||||
`JSON_EXTRACT(customFields, '$."${safeFieldName}"') LIKE '%${escapedKeyword}%' ESCAPE '\\\\'`
|
||||
);
|
||||
} else {
|
||||
return sequelize.literal(
|
||||
`json_extract(customFields, '$.${fieldName}') LIKE '%${escapedKeyword}%'`
|
||||
`json_extract(customFields, '$.${safeFieldName}') LIKE '%${escapedKeyword}%' ESCAPE '\\'`
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1485,8 +1495,10 @@ router.post('/import', async (req, res) => {
|
||||
});
|
||||
|
||||
for (const [rackId, powerToAdd] of rackPowerMap) {
|
||||
// 确保功率值为合法数字,防止SQL注入
|
||||
const safePower = Number(powerToAdd) || 0;
|
||||
await Rack.update(
|
||||
{ currentPower: sequelize.literal(`currentPower + ${powerToAdd}`) },
|
||||
{ currentPower: sequelize.literal(`currentPower + ${safePower}`) },
|
||||
{ where: { rackId }, transaction: t }
|
||||
);
|
||||
}
|
||||
@@ -1809,8 +1821,9 @@ router.put('/batch-move', async (req, res) => {
|
||||
|
||||
for (const [rackId, powerChange] of sourceRackPowerChanges) {
|
||||
if (rackId !== targetRackId) {
|
||||
const safePower = Number(powerChange) || 0;
|
||||
await Rack.update(
|
||||
{ currentPower: sequelize.literal(`currentPower + ${powerChange}`) },
|
||||
{ currentPower: sequelize.literal(`currentPower + ${safePower}`) },
|
||||
{ where: { rackId } }
|
||||
);
|
||||
}
|
||||
@@ -1821,8 +1834,9 @@ router.put('/batch-move', async (req, res) => {
|
||||
}
|
||||
|
||||
if (targetRackPowerChange.change !== 0) {
|
||||
const safeTargetPower = Number(targetRackPowerChange.change) || 0;
|
||||
await Rack.update(
|
||||
{ currentPower: sequelize.literal(`currentPower + ${targetRackPowerChange.change}`) },
|
||||
{ currentPower: sequelize.literal(`currentPower + ${safeTargetPower}`) },
|
||||
{ where: { rackId: targetRackId } }
|
||||
);
|
||||
}
|
||||
@@ -1972,8 +1986,7 @@ router.get('/enhanced-export', async (req, res) => {
|
||||
const headerSet = new Set();
|
||||
|
||||
// 添加机房字段(如果存在)
|
||||
const hasRoomField = allFields.some(f => f.fieldName === 'roomName');
|
||||
if (!hasRoomField) {
|
||||
if (!allFields.some(f => f.fieldName === 'roomName')) {
|
||||
headerSet.add('所在机房');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user