Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m55s
354 lines
11 KiB
JavaScript
354 lines
11 KiB
JavaScript
/**
|
|
* Модуль валидации входных данных
|
|
* Содержит валидаторы для IP, CIDR, доменов, ASN, Community
|
|
*/
|
|
|
|
/**
|
|
* Валидация IPv4 адреса
|
|
* @param {string} ip - IP адрес для валидации
|
|
* @returns {boolean}
|
|
*/
|
|
function isValidIPv4(ip) {
|
|
if (!ip || typeof ip !== 'string') return false;
|
|
const octets = ip.trim().split('.');
|
|
if (octets.length !== 4) return false;
|
|
return octets.every(octet => {
|
|
if (!/^\d{1,3}$/.test(octet)) return false;
|
|
const num = Number(octet);
|
|
return num >= 0 && num <= 255;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Валидация IPv6 адреса (базовая)
|
|
* @param {string} ip - IPv6 адрес
|
|
* @returns {boolean}
|
|
*/
|
|
function isValidIPv6(ip) {
|
|
if (!ip || typeof ip !== 'string') return false;
|
|
const segments = ip.trim().split(':');
|
|
if (segments.length < 3 || segments.length > 8) return false;
|
|
return segments.every(seg => {
|
|
if (seg === '') return true; // :: notation
|
|
return /^[0-9a-fA-F]{1,4}$/.test(seg);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Валидация CIDR блока (IPv4)
|
|
* @param {string} cidr - CIDR блок (например, 192.168.1.0/24)
|
|
* @returns {boolean}
|
|
*/
|
|
function isValidCIDRv4(cidr) {
|
|
if (!cidr || typeof cidr !== 'string') return false;
|
|
const parts = cidr.trim().split('/');
|
|
if (parts.length !== 2) return false;
|
|
|
|
const [ip, mask] = parts;
|
|
if (!isValidIPv4(ip)) return false;
|
|
|
|
if (!/^\d{1,2}$/.test(mask)) return false;
|
|
const maskNum = Number(mask);
|
|
return maskNum >= 0 && maskNum <= 32;
|
|
}
|
|
|
|
/**
|
|
* Валидация CIDR блока (IPv6)
|
|
* @param {string} cidr - CIDR блок IPv6
|
|
* @returns {boolean}
|
|
*/
|
|
function isValidCIDRv6(cidr) {
|
|
if (!cidr || typeof cidr !== 'string') return false;
|
|
const parts = cidr.trim().split('/');
|
|
if (parts.length !== 2) return false;
|
|
|
|
const [ip, mask] = parts;
|
|
if (!isValidIPv6(ip)) return false;
|
|
|
|
if (!/^\d{1,3}$/.test(mask)) return false;
|
|
const maskNum = Number(mask);
|
|
return maskNum >= 0 && maskNum <= 128;
|
|
}
|
|
|
|
/**
|
|
* Валидация доменного имени (FQDN)
|
|
* Поддержка IDN (интернационализированных доменов)
|
|
* @param {string} domain - доменное имя
|
|
* @returns {boolean}
|
|
*/
|
|
function isValidDomain(domain) {
|
|
if (!domain || typeof domain !== 'string') return false;
|
|
const d = domain.trim().toLowerCase();
|
|
|
|
// Проверка на максимальную длину
|
|
if (d.length > 253) return false;
|
|
|
|
// Проверка на IP адрес (не должен быть IP)
|
|
if (isValidIPv4(d) || isValidIPv6(d)) return false;
|
|
|
|
// Базовая проверка структуры домена
|
|
// Разрешаем буквы, цифры, дефис, точку, и IDN символы
|
|
const domainRegex = /^([a-z0-9\u00a1-\uffff]([a-z0-9\u00a1-\uffff-]{0,61}[a-z0-9\u00a1-\uffff])?\.)+[a-z\u00a1-\uffff]{2,}$/i;
|
|
return domainRegex.test(d);
|
|
}
|
|
|
|
/**
|
|
* Валидация wildcard домена (*.example.com)
|
|
* @param {string} domain - wildcard домен
|
|
* @returns {boolean}
|
|
*/
|
|
function isValidWildcardDomain(domain) {
|
|
if (!domain || typeof domain !== 'string') return false;
|
|
const d = domain.trim().toLowerCase();
|
|
|
|
// Проверка на wildcard в начале
|
|
if (d.startsWith('*.')) {
|
|
const baseDomain = d.substring(2);
|
|
return isValidDomain(baseDomain);
|
|
}
|
|
|
|
return isValidDomain(d);
|
|
}
|
|
|
|
/**
|
|
* Валидация номера ASN
|
|
* @param {string|number} asn - номер ASN
|
|
* @returns {boolean}
|
|
*/
|
|
function isValidASN(asn) {
|
|
if (!asn) return false;
|
|
const asnStr = String(asn).trim();
|
|
|
|
// Формат: AS12345 или просто 12345
|
|
const asnRegex = /^(AS)?(\d{1,10})$/i;
|
|
const match = asnStr.match(asnRegex);
|
|
if (!match) return false;
|
|
|
|
const num = Number(match[2]);
|
|
// ASN диапазон: 0-4294967295 (32-bit)
|
|
return num >= 0 && num <= 4294967295;
|
|
}
|
|
|
|
/**
|
|
* Валидация BGP Community
|
|
* Поддержка форматов: 65000:100, 65000
|
|
* @param {string} community - BGP community
|
|
* @returns {boolean}
|
|
*/
|
|
function isValidCommunity(community) {
|
|
if (!community || typeof community !== 'string') return false;
|
|
const c = community.trim();
|
|
|
|
// Формат: N:N или просто N
|
|
const communityRegex = /^(\d{1,10})(:(\d{1,10}))?$/;
|
|
const match = c.match(communityRegex);
|
|
if (!match) return false;
|
|
|
|
const first = Number(match[1]);
|
|
if (first < 0 || first > 4294967295) return false;
|
|
|
|
if (match[3]) {
|
|
const second = Number(match[3]);
|
|
if (second < 0 || second > 65535) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Валидация gateway имени (для MikroTik)
|
|
* @param {string} gateway - имя gateway
|
|
* @returns {boolean}
|
|
*/
|
|
function isValidGateway(gateway) {
|
|
if (!gateway || typeof gateway !== 'string') return false;
|
|
const g = gateway.trim();
|
|
|
|
// MikroTik gateway: буквы, цифры, дефис, подчеркивание
|
|
// Длина: 1-64 символа
|
|
if (g.length < 1 || g.length > 64) return false;
|
|
|
|
const gatewayRegex = /^[a-zA-Z0-9_-]+$/;
|
|
return gatewayRegex.test(g);
|
|
}
|
|
|
|
/**
|
|
* Санитизация строки от опасных символов
|
|
* @param {string} str - входная строка
|
|
* @returns {string}
|
|
*/
|
|
function sanitizeString(str) {
|
|
if (!str || typeof str !== 'string') return '';
|
|
|
|
// Удаляем управляющие символы
|
|
let cleaned = str.replace(/[\x00-\x1F\x7F]/g, '');
|
|
|
|
// Экранируем HTML специальные символы
|
|
cleaned = cleaned
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''')
|
|
.replace(/\//g, '/');
|
|
|
|
return cleaned.trim();
|
|
}
|
|
|
|
/**
|
|
* Валидация идентификатора сервера для использования в S3-ключах и путях.
|
|
* Запрещает path traversal (.., /, \) и небезопасные символы.
|
|
* @param {string} serverId - идентификатор сервера
|
|
* @param {object} opts - { maxLength: number } (по умолчанию 128)
|
|
* @returns {boolean}
|
|
*/
|
|
function isValidServerId(serverId, opts = {}) {
|
|
if (!serverId || typeof serverId !== 'string') return false;
|
|
const maxLen = opts.maxLength != null ? opts.maxLength : 128;
|
|
const trimmed = serverId.trim();
|
|
if (trimmed.length === 0 || trimmed.length > maxLen) return false;
|
|
if (trimmed.includes('..') || trimmed.includes('/') || trimmed.includes('\\')) return false;
|
|
return /^[a-zA-Z0-9_.-]+$/.test(trimmed);
|
|
}
|
|
|
|
/**
|
|
* Проверка на SQL injection паттерны
|
|
* @param {string} str - строка для проверки
|
|
* @returns {boolean} true если безопасно
|
|
*/
|
|
function isSafeSQLString(str) {
|
|
if (!str || typeof str !== 'string') return true;
|
|
|
|
const dangerousPatterns = [
|
|
/(\bSELECT\b|\bINSERT\b|\bUPDATE\b|\bDELETE\b|\bDROP\b|\bCREATE\b)/i,
|
|
/(\bUNION\b|\bJOIN\b)/i,
|
|
/(--|;|\/\*|\*\/)/,
|
|
/(\bOR\b|\bAND\b)\s+\d+\s*=\s*\d+/i,
|
|
];
|
|
|
|
return !dangerousPatterns.some(pattern => pattern.test(str));
|
|
}
|
|
|
|
/**
|
|
* Проверка на XSS паттерны
|
|
* @param {string} str - строка для проверки
|
|
* @returns {boolean} true если безопасно
|
|
*/
|
|
function isSafeXSSString(str) {
|
|
if (!str || typeof str !== 'string') return true;
|
|
|
|
const dangerousPatterns = [
|
|
/<script/i,
|
|
/javascript:/i,
|
|
/on\w+\s*=/i, // onclick=, onerror=, etc
|
|
/<iframe/i,
|
|
/<embed/i,
|
|
/<object/i,
|
|
];
|
|
|
|
return !dangerousPatterns.some(pattern => pattern.test(str));
|
|
}
|
|
|
|
/**
|
|
* Комплексная валидация входных данных
|
|
* @param {object} data - объект с данными для валидации
|
|
* @param {object} schema - схема валидации
|
|
* @returns {object} { valid: boolean, errors: array }
|
|
*/
|
|
function validateData(data, schema) {
|
|
const errors = [];
|
|
|
|
for (const [field, rules] of Object.entries(schema)) {
|
|
const value = data[field];
|
|
|
|
// Проверка required
|
|
if (rules.required && (value === undefined || value === null || value === '')) {
|
|
errors.push({ field, message: `Поле ${field} обязательно` });
|
|
continue;
|
|
}
|
|
|
|
// Если поле не обязательно и пустое - пропускаем остальные проверки
|
|
if (!rules.required && (value === undefined || value === null || value === '')) {
|
|
continue;
|
|
}
|
|
|
|
// Проверка типа
|
|
if (rules.type) {
|
|
switch (rules.type) {
|
|
case 'ipv4':
|
|
if (!isValidIPv4(value)) {
|
|
errors.push({ field, message: `${field} должен быть валидным IPv4 адресом` });
|
|
}
|
|
break;
|
|
case 'cidrv4':
|
|
if (!isValidCIDRv4(value)) {
|
|
errors.push({ field, message: `${field} должен быть валидным CIDR блоком` });
|
|
}
|
|
break;
|
|
case 'domain':
|
|
if (!isValidDomain(value)) {
|
|
errors.push({ field, message: `${field} должен быть валидным доменом` });
|
|
}
|
|
break;
|
|
case 'asn':
|
|
if (!isValidASN(value)) {
|
|
errors.push({ field, message: `${field} должен быть валидным ASN` });
|
|
}
|
|
break;
|
|
case 'community':
|
|
if (!isValidCommunity(value)) {
|
|
errors.push({ field, message: `${field} должен быть валидным BGP Community` });
|
|
}
|
|
break;
|
|
case 'gateway':
|
|
if (!isValidGateway(value)) {
|
|
errors.push({ field, message: `${field} должен быть валидным gateway именем` });
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Проверка минимальной длины
|
|
if (rules.minLength && String(value).length < rules.minLength) {
|
|
errors.push({ field, message: `${field} должен быть не короче ${rules.minLength} символов` });
|
|
}
|
|
|
|
// Проверка максимальной длины
|
|
if (rules.maxLength && String(value).length > rules.maxLength) {
|
|
errors.push({ field, message: `${field} должен быть не длиннее ${rules.maxLength} символов` });
|
|
}
|
|
|
|
// Проверка безопасности
|
|
if (rules.checkXSS && !isSafeXSSString(value)) {
|
|
errors.push({ field, message: `${field} содержит потенциально опасные символы` });
|
|
}
|
|
|
|
if (rules.checkSQL && !isSafeSQLString(value)) {
|
|
errors.push({ field, message: `${field} содержит недопустимые SQL конструкции` });
|
|
}
|
|
}
|
|
|
|
return {
|
|
valid: errors.length === 0,
|
|
errors
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
isValidIPv4,
|
|
isValidIPv6,
|
|
isValidCIDRv4,
|
|
isValidCIDRv6,
|
|
isValidDomain,
|
|
isValidWildcardDomain,
|
|
isValidASN,
|
|
isValidCommunity,
|
|
isValidGateway,
|
|
isValidServerId,
|
|
sanitizeString,
|
|
isSafeSQLString,
|
|
isSafeXSSString,
|
|
validateData,
|
|
};
|
|
|