2025-12-03 21:56:50 +00:00
|
|
|
/**
|
|
|
|
|
* XSS Protection utilities
|
|
|
|
|
* Sanitise et valide le contenu utilisateur pour prévenir les attaques XSS
|
|
|
|
|
*/
|
2025-12-17 13:07:35 +00:00
|
|
|
import DOMPurify from 'dompurify';
|
2025-12-03 21:56:50 +00:00
|
|
|
|
|
|
|
|
// Types pour la configuration de sanitisation
|
|
|
|
|
export interface SanitizeOptions {
|
2025-12-13 02:34:34 +00:00
|
|
|
allowedTags?: string[];
|
|
|
|
|
allowedAttributes?: Record<string, string[]>;
|
|
|
|
|
allowedSchemes?: string[];
|
|
|
|
|
stripUnknownTags?: boolean;
|
|
|
|
|
stripEmptyTags?: boolean;
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Configuration par défaut pour la sanitisation
|
|
|
|
|
const DEFAULT_OPTIONS: SanitizeOptions = {
|
|
|
|
|
allowedTags: [
|
2025-12-13 02:34:34 +00:00
|
|
|
'p',
|
|
|
|
|
'br',
|
|
|
|
|
'strong',
|
|
|
|
|
'em',
|
|
|
|
|
'u',
|
|
|
|
|
'i',
|
|
|
|
|
'b',
|
|
|
|
|
'span',
|
|
|
|
|
'div',
|
|
|
|
|
'h1',
|
|
|
|
|
'h2',
|
|
|
|
|
'h3',
|
|
|
|
|
'h4',
|
|
|
|
|
'h5',
|
|
|
|
|
'h6',
|
|
|
|
|
'ul',
|
|
|
|
|
'ol',
|
|
|
|
|
'li',
|
|
|
|
|
'blockquote',
|
|
|
|
|
'pre',
|
|
|
|
|
'code',
|
|
|
|
|
'a',
|
|
|
|
|
'img',
|
2025-12-03 21:56:50 +00:00
|
|
|
],
|
|
|
|
|
allowedAttributes: {
|
2025-12-13 02:34:34 +00:00
|
|
|
a: ['href', 'title', 'target'],
|
|
|
|
|
img: ['src', 'alt', 'title', 'width', 'height'],
|
|
|
|
|
span: ['class'],
|
|
|
|
|
div: ['class'],
|
|
|
|
|
p: ['class'],
|
|
|
|
|
pre: ['class'],
|
|
|
|
|
code: ['class'],
|
2025-12-03 21:56:50 +00:00
|
|
|
},
|
|
|
|
|
allowedSchemes: ['http', 'https', 'mailto'],
|
|
|
|
|
stripUnknownTags: true,
|
2025-12-13 02:34:34 +00:00
|
|
|
stripEmptyTags: true,
|
|
|
|
|
};
|
2025-12-03 21:56:50 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Patterns dangereux à détecter et supprimer
|
|
|
|
|
*/
|
|
|
|
|
const DANGEROUS_PATTERNS = [
|
|
|
|
|
// Scripts et exécution de code
|
|
|
|
|
/<script[^>]*>[\s\S]*?<\/script>/gi,
|
|
|
|
|
/javascript:/gi,
|
|
|
|
|
/vbscript:/gi,
|
|
|
|
|
/data:text\/html/gi,
|
|
|
|
|
/data:application\/javascript/gi,
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
// Event handlers inline
|
|
|
|
|
/on\w+\s*=\s*["'][^"']*["']/gi,
|
|
|
|
|
/on\w+\s*=\s*[^>\s]+/gi,
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
// Expressions CSS dangereuses
|
|
|
|
|
/expression\s*\(/gi,
|
|
|
|
|
/url\s*\(\s*javascript:/gi,
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
// Tags dangereux
|
|
|
|
|
/<iframe[^>]*>[\s\S]*?<\/iframe>/gi,
|
|
|
|
|
/<object[^>]*>[\s\S]*?<\/object>/gi,
|
|
|
|
|
/<embed[^>]*>/gi,
|
|
|
|
|
/<applet[^>]*>[\s\S]*?<\/applet>/gi,
|
|
|
|
|
/<form[^>]*>[\s\S]*?<\/form>/gi,
|
|
|
|
|
/<input[^>]*>/gi,
|
|
|
|
|
/<textarea[^>]*>[\s\S]*?<\/textarea>/gi,
|
|
|
|
|
/<select[^>]*>[\s\S]*?<\/select>/gi,
|
|
|
|
|
/<button[^>]*>[\s\S]*?<\/button>/gi,
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
// Meta tags dangereux
|
|
|
|
|
/<meta[^>]*>/gi,
|
|
|
|
|
/<link[^>]*>/gi,
|
|
|
|
|
/<style[^>]*>[\s\S]*?<\/style>/gi,
|
2025-12-13 02:34:34 +00:00
|
|
|
];
|
2025-12-03 21:56:50 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Patterns pour les URLs suspectes
|
|
|
|
|
*/
|
|
|
|
|
const SUSPICIOUS_URL_PATTERNS = [
|
|
|
|
|
/javascript:/i,
|
|
|
|
|
/vbscript:/i,
|
|
|
|
|
/data:/i,
|
|
|
|
|
/file:/i,
|
|
|
|
|
/ftp:/i,
|
|
|
|
|
/gopher:/i,
|
|
|
|
|
/jar:/i,
|
|
|
|
|
/ldap:/i,
|
|
|
|
|
/ldaps:/i,
|
|
|
|
|
/magnet:/i,
|
|
|
|
|
/news:/i,
|
|
|
|
|
/nntp:/i,
|
|
|
|
|
/sftp:/i,
|
|
|
|
|
/smb:/i,
|
|
|
|
|
/ssh:/i,
|
|
|
|
|
/telnet:/i,
|
|
|
|
|
/tftp:/i,
|
|
|
|
|
/view-source:/i,
|
2025-12-13 02:34:34 +00:00
|
|
|
];
|
2025-12-03 21:56:50 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sanitise le contenu HTML pour prévenir les attaques XSS
|
|
|
|
|
*/
|
2025-12-13 02:34:34 +00:00
|
|
|
export function sanitizeHTML(
|
|
|
|
|
content: string,
|
|
|
|
|
options: SanitizeOptions = {},
|
|
|
|
|
): string {
|
|
|
|
|
const config = { ...DEFAULT_OPTIONS, ...options };
|
|
|
|
|
let sanitized = content;
|
2025-12-03 21:56:50 +00:00
|
|
|
|
|
|
|
|
// Supprimer les patterns dangereux
|
2025-12-13 02:34:34 +00:00
|
|
|
DANGEROUS_PATTERNS.forEach((pattern) => {
|
|
|
|
|
sanitized = sanitized.replace(pattern, '');
|
|
|
|
|
});
|
2025-12-03 21:56:50 +00:00
|
|
|
|
|
|
|
|
// Supprimer les tags non autorisés
|
|
|
|
|
if (config.stripUnknownTags) {
|
2025-12-13 02:34:34 +00:00
|
|
|
sanitized = stripUnknownTags(sanitized, config.allowedTags!);
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Nettoyer les attributs
|
2025-12-13 02:34:34 +00:00
|
|
|
sanitized = sanitizeAttributes(sanitized, config.allowedAttributes!);
|
2025-12-03 21:56:50 +00:00
|
|
|
|
|
|
|
|
// Valider les URLs
|
2025-12-13 02:34:34 +00:00
|
|
|
sanitized = validateURLs(sanitized, config.allowedSchemes!);
|
2025-12-03 21:56:50 +00:00
|
|
|
|
|
|
|
|
// Supprimer les tags vides
|
|
|
|
|
if (config.stripEmptyTags) {
|
2025-12-13 02:34:34 +00:00
|
|
|
sanitized = stripEmptyTags(sanitized);
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Échapper les caractères HTML restants
|
2025-12-13 02:34:34 +00:00
|
|
|
sanitized = escapeHTML(sanitized);
|
2025-12-03 21:56:50 +00:00
|
|
|
|
2025-12-13 02:34:34 +00:00
|
|
|
return sanitized;
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Supprime les tags non autorisés
|
|
|
|
|
*/
|
|
|
|
|
function stripUnknownTags(content: string, allowedTags: string[]): string {
|
2025-12-13 02:34:34 +00:00
|
|
|
const tagPattern = /<\/?([a-zA-Z][a-zA-Z0-9]*)[^>]*>/g;
|
|
|
|
|
|
|
|
|
|
return content.replace(tagPattern, (_match, tagName) => {
|
2025-12-03 21:56:50 +00:00
|
|
|
if (allowedTags.includes(tagName.toLowerCase())) {
|
2025-12-13 02:34:34 +00:00
|
|
|
return _match;
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
return '';
|
|
|
|
|
});
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Nettoie les attributs des tags
|
|
|
|
|
*/
|
2025-12-13 02:34:34 +00:00
|
|
|
function sanitizeAttributes(
|
|
|
|
|
content: string,
|
|
|
|
|
allowedAttributes: Record<string, string[]>,
|
|
|
|
|
): string {
|
|
|
|
|
const tagPattern = /<([a-zA-Z][a-zA-Z0-9]*)([^>]*)>/g;
|
|
|
|
|
|
|
|
|
|
return content.replace(tagPattern, (_match, tagName, attributes) => {
|
|
|
|
|
const allowedAttrs = allowedAttributes[tagName.toLowerCase()];
|
2025-12-03 21:56:50 +00:00
|
|
|
if (!allowedAttrs) {
|
2025-12-13 02:34:34 +00:00
|
|
|
return `<${tagName}>`;
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-13 02:34:34 +00:00
|
|
|
const attrPattern = /(\w+)\s*=\s*["']([^"']*)["']/g;
|
|
|
|
|
const cleanAttributes = attributes.replace(
|
|
|
|
|
attrPattern,
|
|
|
|
|
(_attrMatch: string, attrName: string, attrValue: string) => {
|
|
|
|
|
if (allowedAttrs.includes(attrName.toLowerCase())) {
|
|
|
|
|
// Valider les URLs dans les attributs href et src
|
|
|
|
|
if (
|
|
|
|
|
attrName.toLowerCase() === 'href' ||
|
|
|
|
|
attrName.toLowerCase() === 'src'
|
|
|
|
|
) {
|
|
|
|
|
if (isValidURL(attrValue)) {
|
|
|
|
|
return `${attrName}="${attrValue}"`;
|
|
|
|
|
}
|
|
|
|
|
return '';
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
return _attrMatch;
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
return '';
|
|
|
|
|
},
|
|
|
|
|
);
|
2025-12-03 21:56:50 +00:00
|
|
|
|
2025-12-13 02:34:34 +00:00
|
|
|
return `<${tagName}${cleanAttributes}>`;
|
|
|
|
|
});
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Valide les URLs dans le contenu
|
|
|
|
|
*/
|
|
|
|
|
function validateURLs(content: string, allowedSchemes: string[]): string {
|
2025-12-13 02:34:34 +00:00
|
|
|
const urlPattern = /(https?:\/\/[^\s<>"']+)/g;
|
|
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
return content.replace(urlPattern, (url) => {
|
|
|
|
|
try {
|
2025-12-13 02:34:34 +00:00
|
|
|
const urlObj = new URL(url);
|
2025-12-03 21:56:50 +00:00
|
|
|
if (allowedSchemes.includes(urlObj.protocol.slice(0, -1))) {
|
2025-12-13 02:34:34 +00:00
|
|
|
return url;
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
// URL invalide
|
|
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
return '';
|
|
|
|
|
});
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Vérifie si une URL est valide et sûre
|
|
|
|
|
*/
|
|
|
|
|
function isValidURL(url: string): boolean {
|
|
|
|
|
try {
|
2025-12-13 02:34:34 +00:00
|
|
|
const urlObj = new URL(url);
|
|
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
// Vérifier le protocole
|
|
|
|
|
if (!['http:', 'https:', 'mailto:'].includes(urlObj.protocol)) {
|
2025-12-13 02:34:34 +00:00
|
|
|
return false;
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
// Vérifier les patterns suspects
|
2025-12-13 02:34:34 +00:00
|
|
|
return !SUSPICIOUS_URL_PATTERNS.some((pattern) => pattern.test(url));
|
2025-12-03 21:56:50 +00:00
|
|
|
} catch {
|
2025-12-13 02:34:34 +00:00
|
|
|
return false;
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Supprime les tags vides
|
|
|
|
|
*/
|
|
|
|
|
function stripEmptyTags(content: string): string {
|
2025-12-13 02:34:34 +00:00
|
|
|
return content.replace(/<([a-zA-Z][a-zA-Z0-9]*)[^>]*>\s*<\/\1>/g, '');
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Échappe les caractères HTML spéciaux
|
|
|
|
|
*/
|
|
|
|
|
function escapeHTML(content: string): string {
|
|
|
|
|
const escapeMap: Record<string, string> = {
|
|
|
|
|
'&': '&',
|
|
|
|
|
'<': '<',
|
|
|
|
|
'>': '>',
|
|
|
|
|
'"': '"',
|
|
|
|
|
"'": ''',
|
|
|
|
|
'/': '/',
|
2025-12-13 02:34:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return content.replace(/[&<>"'/]/g, (char) => escapeMap[char]);
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sanitise spécifiquement les messages de chat
|
|
|
|
|
*/
|
2025-12-17 13:07:35 +00:00
|
|
|
/**
|
|
|
|
|
* Sanitise les messages de chat avec DOMPurify pour une protection XSS robuste
|
|
|
|
|
* Utilise DOMPurify en priorité, avec fallback sur sanitizeHTML si DOMPurify n'est pas disponible
|
|
|
|
|
*/
|
2025-12-03 21:56:50 +00:00
|
|
|
export function sanitizeChatMessage(message: string): string {
|
2025-12-17 13:07:35 +00:00
|
|
|
// Utiliser DOMPurify pour une sanitisation robuste et éprouvée
|
|
|
|
|
if (typeof window !== 'undefined' && DOMPurify.isSupported) {
|
|
|
|
|
return DOMPurify.sanitize(message, {
|
|
|
|
|
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'i', 'b', 'span', 'a'],
|
|
|
|
|
ALLOWED_ATTR: ['class', 'href', 'title', 'target'],
|
2026-01-13 18:47:57 +00:00
|
|
|
ALLOWED_URI_REGEXP:
|
|
|
|
|
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|data):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i,
|
2025-12-17 13:07:35 +00:00
|
|
|
KEEP_CONTENT: true,
|
|
|
|
|
RETURN_DOM: false,
|
|
|
|
|
RETURN_DOM_FRAGMENT: false,
|
|
|
|
|
RETURN_TRUSTED_TYPE: false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fallback sur la sanitisation manuelle si DOMPurify n'est pas disponible (SSR)
|
2025-12-03 21:56:50 +00:00
|
|
|
const chatOptions: SanitizeOptions = {
|
|
|
|
|
allowedTags: ['p', 'br', 'strong', 'em', 'u', 'i', 'b', 'span'],
|
|
|
|
|
allowedAttributes: {
|
2025-12-13 02:34:34 +00:00
|
|
|
span: ['class'],
|
2025-12-03 21:56:50 +00:00
|
|
|
},
|
|
|
|
|
allowedSchemes: ['http', 'https'],
|
|
|
|
|
stripUnknownTags: true,
|
2025-12-13 02:34:34 +00:00
|
|
|
stripEmptyTags: true,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return sanitizeHTML(message, chatOptions);
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sanitise les noms d'utilisateur et autres champs texte
|
|
|
|
|
*/
|
|
|
|
|
export function sanitizeTextInput(input: string): string {
|
|
|
|
|
// Pour les champs texte simples, on échappe tout le HTML
|
2025-12-13 02:34:34 +00:00
|
|
|
return escapeHTML(input.trim());
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Valide et nettoie les URLs utilisateur
|
|
|
|
|
*/
|
|
|
|
|
export function sanitizeURL(url: string): string | null {
|
|
|
|
|
try {
|
2025-12-13 02:34:34 +00:00
|
|
|
const urlObj = new URL(url);
|
|
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
// Seuls HTTP et HTTPS sont autorisés
|
|
|
|
|
if (!['http:', 'https:'].includes(urlObj.protocol)) {
|
2025-12-13 02:34:34 +00:00
|
|
|
return null;
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
// Vérifier les patterns suspects
|
2025-12-13 02:34:34 +00:00
|
|
|
if (SUSPICIOUS_URL_PATTERNS.some((pattern) => pattern.test(url))) {
|
|
|
|
|
return null;
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
|
|
|
|
|
return urlObj.toString();
|
2025-12-03 21:56:50 +00:00
|
|
|
} catch {
|
2025-12-13 02:34:34 +00:00
|
|
|
return null;
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Valide les emails
|
|
|
|
|
*/
|
|
|
|
|
export function sanitizeEmail(email: string): string | null {
|
2025-12-13 02:34:34 +00:00
|
|
|
const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
|
|
|
|
const sanitized = email.trim().toLowerCase();
|
|
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
if (emailPattern.test(sanitized)) {
|
2025-12-13 02:34:34 +00:00
|
|
|
return sanitized;
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
|
|
|
|
|
return null;
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Valide les mots de passe selon les critères de sécurité
|
|
|
|
|
*/
|
|
|
|
|
export function validatePassword(password: string): {
|
2025-12-13 02:34:34 +00:00
|
|
|
isValid: boolean;
|
|
|
|
|
errors: string[];
|
2025-12-03 21:56:50 +00:00
|
|
|
} {
|
2025-12-13 02:34:34 +00:00
|
|
|
const errors: string[] = [];
|
|
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
if (password.length < 12) {
|
2025-12-13 02:34:34 +00:00
|
|
|
errors.push('Le mot de passe doit contenir au moins 12 caractères');
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
if (!/[A-Z]/.test(password)) {
|
2025-12-13 02:34:34 +00:00
|
|
|
errors.push('Le mot de passe doit contenir au moins une majuscule');
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
if (!/[a-z]/.test(password)) {
|
2025-12-13 02:34:34 +00:00
|
|
|
errors.push('Le mot de passe doit contenir au moins une minuscule');
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
if (!/[0-9]/.test(password)) {
|
2025-12-13 02:34:34 +00:00
|
|
|
errors.push('Le mot de passe doit contenir au moins un chiffre');
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
if (!/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password)) {
|
2025-12-13 02:34:34 +00:00
|
|
|
errors.push('Le mot de passe doit contenir au moins un caractère spécial');
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
// Vérifier les patterns communs faibles
|
|
|
|
|
const weakPatterns = [
|
|
|
|
|
/(.)\1{3,}/, // Répétition de caractères
|
|
|
|
|
/123456/, // Séquence numérique
|
|
|
|
|
/password/i, // Mot "password"
|
|
|
|
|
/qwerty/i, // Mot "qwerty"
|
2025-12-13 02:34:34 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if (weakPatterns.some((pattern) => pattern.test(password))) {
|
|
|
|
|
errors.push('Le mot de passe contient des patterns trop communs');
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
return {
|
|
|
|
|
isValid: errors.length === 0,
|
2025-12-13 02:34:34 +00:00
|
|
|
errors,
|
|
|
|
|
};
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook React pour utiliser la sanitisation
|
|
|
|
|
*/
|
|
|
|
|
export function useSanitization() {
|
|
|
|
|
return {
|
|
|
|
|
sanitizeHTML,
|
|
|
|
|
sanitizeChatMessage,
|
|
|
|
|
sanitizeTextInput,
|
|
|
|
|
sanitizeURL,
|
|
|
|
|
sanitizeEmail,
|
2025-12-13 02:34:34 +00:00
|
|
|
validatePassword,
|
|
|
|
|
};
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|