157 lines
3.7 KiB
JavaScript
157 lines
3.7 KiB
JavaScript
|
|
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
|
||
|
|
};
|
||
|
|
|