nakama/backend/middleware/validator.js

158 lines
3.7 KiB
JavaScript
Raw Permalink Normal View History

2025-11-04 21:51:05 +00:00
const validator = require('validator');
// Валидация и санитизация входных данных
const sanitizeInput = (req, res, next) => {
// Рекурсивная функция для очистки объекта
const sanitizeObject = (obj) => {
if (typeof obj !== 'object' || obj === null) {
return typeof obj === 'string' ? validator.escape(obj) : obj;
}
if (Array.isArray(obj)) {
return obj.map(item => sanitizeObject(item));
}
const sanitized = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = obj[key];
if (typeof value === 'string') {
sanitized[key] = validator.escape(validator.trim(value));
} else {
sanitized[key] = sanitizeObject(value);
}
}
}
return sanitized;
};
// Очистка body
if (req.body) {
req.body = sanitizeObject(req.body);
}
// Очистка query
if (req.query) {
req.query = sanitizeObject(req.query);
}
// Очистка params (только строковые значения)
if (req.params) {
for (const key in req.params) {
if (typeof req.params[key] === 'string') {
req.params[key] = validator.escape(req.params[key]);
}
}
}
next();
};
// Валидация URL
const validateUrl = (url) => {
if (!url || typeof url !== 'string') {
return false;
}
// Проверка на path traversal
if (url.includes('..') || url.includes('./') || url.includes('../')) {
return false;
}
// Проверка на валидный URL
return validator.isURL(url, {
protocols: ['http', 'https'],
require_protocol: true,
require_valid_protocol: true
});
};
// Валидация Telegram User ID
const validateTelegramId = (id) => {
if (!id || typeof id !== 'number' && typeof id !== 'string') {
return false;
}
const numId = typeof id === 'string' ? parseInt(id, 10) : id;
return !isNaN(numId) && numId > 0 && numId < Number.MAX_SAFE_INTEGER;
};
// Валидация контента поста
const validatePostContent = (content) => {
if (!content || typeof content !== 'string') {
return false;
}
// Максимальная длина
if (content.length > 5000) {
return false;
}
// Проверка на опасные паттерны
const dangerousPatterns = [
/<script/i,
/javascript:/i,
/on\w+\s*=/i,
/data:text\/html/i
];
return !dangerousPatterns.some(pattern => pattern.test(content));
};
// Валидация тегов
const validateTags = (tags) => {
if (!Array.isArray(tags)) {
return false;
}
// Максимум 20 тегов
if (tags.length > 20) {
return false;
}
// Каждый тег должен быть строкой и не превышать 50 символов
return tags.every(tag =>
typeof tag === 'string' &&
tag.length > 0 &&
tag.length <= 50 &&
/^[a-zA-Z0-9_\-]+$/.test(tag) // Только буквы, цифры, подчеркивания и дефисы
);
};
// Валидация изображений
const validateImageUrl = (url) => {
if (!url || typeof url !== 'string') {
return false;
}
// Разрешенные домены
const allowedDomains = [
'e621.net',
'static1.e621.net',
'gelbooru.com',
'img3.gelbooru.com',
'img2.gelbooru.com',
'img1.gelbooru.com',
'simg3.gelbooru.com',
'simg4.gelbooru.com'
];
try {
const urlObj = new URL(url);
return allowedDomains.some(domain => urlObj.hostname.includes(domain));
} catch {
return false;
}
};
module.exports = {
sanitizeInput,
validateUrl,
validateTelegramId,
validatePostContent,
validateTags,
validateImageUrl
};
2025-11-20 20:50:14 +00:00