/** * AntiInjectionEngine - Système de protection anti-prompt injection * Implémente les 4 couches de sécurité selon CDC : * Layer 1: Content Preprocessing * Layer 2: Pattern Detection * Layer 3: Semantic Validation * Layer 4: Source Scoring avec pénalités */ const logger = require('../utils/logger'); const { setupTracer } = logger; class AntiInjectionEngine { constructor() { this.tracer = setupTracer('AntiInjectionEngine'); // Patterns dangereux - Layer 2 this.dangerousPatterns = [ // Instructions directes /ignore\s+previous\s+instructions/gi, /you\s+are\s+now/gi, /forget\s+everything/gi, /new\s+instructions?:/gi, /system\s+prompt:/gi, /override\s+instructions/gi, /disregard\s+(all\s+)?previous/gi, // Redirections de contexte /instead\s+of\s+writing\s+about/gi, /don't\s+write\s+about.*write\s+about/gi, /change\s+the\s+topic\s+to/gi, /focus\s+on.*instead/gi, // Injections de code/commandes /]*>/gi, /]*>/gi, /javascript:/gi, /eval\s*\(/gi, /exec\s*\(/gi, /system\s*\(/gi, /\$\{.*\}/g, // Template literals /`.*`/g, // Backticks // Métaprompts et tests /this\s+is\s+a\s+test/gi, /output\s+json\s+format/gi, /return\s+only/gi, /respond\s+with\s+only/gi, /answer\s+with\s+(yes|no|true|false)(\s+only)?/gi, // Tentatives de manipulation /pretend\s+(to\s+be|you\s+are)/gi, /act\s+as\s+(if\s+)?you/gi, /simulate\s+(being|that)/gi, /role\s*play/gi, // Bypass attempts /\/\*.*ignore.*\*\//gi, //gi, /\\n\\n/g, // Tentatives newlines /\n\s*\n\s*---/g // Séparateurs suspects ]; // Patterns de validation sémantique - Layer 3 this.semanticValidationRules = [ { name: 'dog_breed_context', pattern: /(chien|dog|race|breed|canin)/gi, minMatches: 1, weight: 0.4 }, { name: 'animal_context', pattern: /(animal|pet|élevage|vétérinaire|comportement)/gi, minMatches: 1, weight: 0.3 }, { name: 'relevant_topics', pattern: /(santé|alimentation|dressage|éducation|soins|exercice)/gi, minMatches: 1, weight: 0.3 } ]; // Scores de pénalité - Layer 4 this.penaltyScores = { PROMPT_INJECTION_DETECTED: -50, SEMANTIC_INCONSISTENCY: -30, UNTRUSTED_SOURCE_HISTORY: -20, SUSPICIOUS_CONTENT_STRUCTURE: -15, MODERATE_RISK_INDICATORS: -10 }; // Statistiques this.stats = { totalValidated: 0, injectionAttempts: 0, semanticFailures: 0, falsePositives: 0, averageProcessingTime: 0, riskLevelDistribution: { low: 0, medium: 0, high: 0, critical: 0 } }; // Cache des résultats de validation this.validationCache = new Map(); this.cacheTimeout = 300000; // 5 minutes } /** * Valider le contenu principal - Point d'entrée * @param {Object} content - Contenu à valider * @param {Object} context - Contexte de validation * @returns {Promise} Résultat de validation complet */ async validateContent(content, context = {}) { return await this.tracer.run('validateContent', async () => { const startTime = Date.now(); this.stats.totalValidated++; try { // Générer clé de cache const cacheKey = this.generateCacheKey(content, context); // Vérifier cache const cachedResult = this.getFromCache(cacheKey); if (cachedResult) { return cachedResult; } logger.debug('Starting content validation', { contentLength: content.content?.length || 0, source: content.sourceType || 'unknown', raceCode: context.raceCode }); // Layer 1: Préprocessing du contenu const preprocessResult = await this.layer1_preprocessContent(content); // Layer 2: Détection de patterns const patternResult = await this.layer2_detectPatterns(preprocessResult); // Layer 3: Validation sémantique const semanticResult = await this.layer3_semanticValidation(preprocessResult, context); // Layer 4: Calcul des pénalités const penaltyResult = await this.layer4_calculatePenalties(patternResult, semanticResult, content); // Construire résultat final const validationResult = { isValid: this.determineValidityStatus(patternResult, semanticResult, penaltyResult), riskLevel: this.calculateRiskLevel(patternResult, semanticResult), processingTime: Date.now() - startTime, // Détails par couche layers: { preprocessing: preprocessResult, patternDetection: patternResult, semanticValidation: semanticResult, penalties: penaltyResult }, // Contenu nettoyé cleanedContent: { ...content, title: preprocessResult.cleanedTitle, content: preprocessResult.cleanedContent }, // Métadonnées de sécurité securityMetadata: { engine: 'AntiInjectionEngine', version: '1.0', validatedAt: new Date().toISOString(), context: { raceCode: context.raceCode, sourceType: content.sourceType, clientId: context.clientId } }, // Recommandations recommendations: this.generateSecurityRecommendations(patternResult, semanticResult, penaltyResult) }; // Mise en cache this.cacheResult(cacheKey, validationResult); // Mise à jour statistiques this.updateValidationStats(validationResult); // Logging selon niveau de risque this.logValidationResult(validationResult, content, context); return validationResult; } catch (error) { logger.error('Content validation failed', error, { contentId: content.id, raceCode: context.raceCode }); return { isValid: false, riskLevel: 'critical', error: error.message, processingTime: Date.now() - startTime, securityMetadata: { engine: 'AntiInjectionEngine', status: 'error', validatedAt: new Date().toISOString() } }; } }, { contentId: content.id, raceCode: context.raceCode }); } /** * Layer 1: Préprocessing et nettoyage du contenu */ async layer1_preprocessContent(content) { return await this.tracer.run('layer1_preprocessing', async () => { const originalTitle = content.title || ''; const originalContent = content.content || ''; // Normalisation de base let cleanedTitle = originalTitle.trim(); let cleanedContent = originalContent.trim(); // Supprimer HTML potentiellement dangereux cleanedTitle = this.removeHtmlTags(cleanedTitle); cleanedContent = this.removeHtmlTags(cleanedContent); // Normaliser espaces et caractères cleanedTitle = this.normalizeWhitespace(cleanedTitle); cleanedContent = this.normalizeWhitespace(cleanedContent); // Supprimer caractères de contrôle suspects cleanedTitle = this.removeControlCharacters(cleanedTitle); cleanedContent = this.removeControlCharacters(cleanedContent); // Encoder caractères potentiellement dangereux cleanedTitle = this.encodeSpecialCharacters(cleanedTitle); cleanedContent = this.encodeSpecialCharacters(cleanedContent); return { cleanedTitle, cleanedContent, originalTitle, originalContent, changesApplied: { htmlRemoved: originalContent !== cleanedContent, whitespaceNormalized: true, controlCharsRemoved: true, specialCharsEncoded: true }, cleaningStats: { titleLengthChange: originalTitle.length - cleanedTitle.length, contentLengthChange: originalContent.length - cleanedContent.length, cleaningScore: this.calculateCleaningScore(originalContent, cleanedContent) } }; }); } /** * Layer 2: Détection de patterns dangereux */ async layer2_detectPatterns(preprocessResult) { return await this.tracer.run('layer2_patternDetection', async () => { const { cleanedTitle, cleanedContent } = preprocessResult; const fullText = `${cleanedTitle} ${cleanedContent}`; const detectedPatterns = []; let totalRiskScore = 0; // Analyser chaque pattern dangereux for (const [index, pattern] of this.dangerousPatterns.entries()) { const matches = fullText.match(pattern); if (matches && matches.length > 0) { const patternInfo = { patternIndex: index, pattern: pattern.source, matches: matches, matchCount: matches.length, riskWeight: this.getPatternRiskWeight(pattern), locations: this.findPatternLocations(fullText, pattern) }; detectedPatterns.push(patternInfo); totalRiskScore += patternInfo.riskWeight * patternInfo.matchCount; } } // Analyser structure suspecte const structureAnalysis = this.analyzeContentStructure(fullText); if (structureAnalysis.suspicious) { totalRiskScore += structureAnalysis.riskScore; } return { detectedPatterns, totalPatterns: detectedPatterns.length, totalRiskScore, maxIndividualRisk: Math.max(...detectedPatterns.map(p => p.riskWeight), 0), structureAnalysis, hasHighRiskPatterns: detectedPatterns.some(p => p.riskWeight >= 8), hasMediumRiskPatterns: detectedPatterns.some(p => p.riskWeight >= 5), summary: this.summarizePatternDetection(detectedPatterns, totalRiskScore) }; }); } /** * Layer 3: Validation sémantique */ async layer3_semanticValidation(preprocessResult, context) { return await this.tracer.run('layer3_semanticValidation', async () => { const { cleanedTitle, cleanedContent } = preprocessResult; const fullText = `${cleanedTitle} ${cleanedContent}`; const validationResults = []; let semanticScore = 0; let totalWeight = 0; // Appliquer chaque règle de validation sémantique for (const rule of this.semanticValidationRules) { const matches = fullText.match(rule.pattern); const matchCount = matches ? matches.length : 0; const ruleResult = { ruleName: rule.name, required: rule.minMatches, found: matchCount, passed: matchCount >= rule.minMatches, weight: rule.weight, matches: matches || [], score: matchCount >= rule.minMatches ? rule.weight : 0 }; validationResults.push(ruleResult); semanticScore += ruleResult.score; totalWeight += rule.weight; } // Validation spécifique au contexte race const raceValidation = await this.validateRaceContext(fullText, context.raceCode); // Détection d'incohérences const inconsistencies = this.detectSemanticInconsistencies(fullText, context); // Score sémantique final (0-1) const finalSemanticScore = totalWeight > 0 ? semanticScore / totalWeight : 0; return { validationResults, raceValidation, inconsistencies, semanticScore: finalSemanticScore, passed: finalSemanticScore >= 0.3, // Seuil minimum 30% confidence: this.calculateSemanticConfidence(validationResults, inconsistencies), contextRelevance: this.assessContextRelevance(fullText, context), recommendations: this.generateSemanticRecommendations(validationResults, raceValidation) }; }); } /** * Layer 4: Calcul des pénalités et score final */ async layer4_calculatePenalties(patternResult, semanticResult, content) { return await this.tracer.run('layer4_penalties', async () => { let totalPenalty = 0; const appliedPenalties = []; // Pénalité injection détectée if (patternResult.hasHighRiskPatterns) { totalPenalty += this.penaltyScores.PROMPT_INJECTION_DETECTED; appliedPenalties.push({ type: 'PROMPT_INJECTION_DETECTED', score: this.penaltyScores.PROMPT_INJECTION_DETECTED, reason: `${patternResult.totalPatterns} patterns dangereux détectés` }); } // Pénalité incohérence sémantique if (!semanticResult.passed) { totalPenalty += this.penaltyScores.SEMANTIC_INCONSISTENCY; appliedPenalties.push({ type: 'SEMANTIC_INCONSISTENCY', score: this.penaltyScores.SEMANTIC_INCONSISTENCY, reason: `Score sémantique: ${Math.round(semanticResult.semanticScore * 100)}%` }); } // Pénalité source historique const sourceHistory = await this.checkSourceHistory(content); if (sourceHistory.isUntrusted) { totalPenalty += this.penaltyScores.UNTRUSTED_SOURCE_HISTORY; appliedPenalties.push({ type: 'UNTRUSTED_SOURCE_HISTORY', score: this.penaltyScores.UNTRUSTED_SOURCE_HISTORY, reason: sourceHistory.reason }); } // Pénalité structure suspecte if (patternResult.structureAnalysis.suspicious) { totalPenalty += this.penaltyScores.SUSPICIOUS_CONTENT_STRUCTURE; appliedPenalties.push({ type: 'SUSPICIOUS_CONTENT_STRUCTURE', score: this.penaltyScores.SUSPICIOUS_CONTENT_STRUCTURE, reason: patternResult.structureAnalysis.reason }); } // Pénalité risque modéré if (patternResult.hasMediumRiskPatterns && !patternResult.hasHighRiskPatterns) { totalPenalty += this.penaltyScores.MODERATE_RISK_INDICATORS; appliedPenalties.push({ type: 'MODERATE_RISK_INDICATORS', score: this.penaltyScores.MODERATE_RISK_INDICATORS, reason: 'Patterns de risque modéré détectés' }); } return { totalPenalty, appliedPenalties, penaltyCount: appliedPenalties.length, maxIndividualPenalty: Math.min(...appliedPenalties.map(p => p.score), 0), sourceHistory, finalRecommendation: this.generateFinalRecommendation(totalPenalty, patternResult, semanticResult) }; }); } // === Méthodes utilitaires === removeHtmlTags(text) { return text .replace(/]*>.*?<\/script>/gi, '') .replace(/]*>.*?<\/style>/gi, '') .replace(/<[^>]*>/g, ''); } normalizeWhitespace(text) { return text .replace(/\s+/g, ' ') .replace(/\n\s*\n/g, '\n') .trim(); } removeControlCharacters(text) { return text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, ''); } encodeSpecialCharacters(text) { const specialChars = { '<': '<', '>': '>', '"': '"', "'": ''', '&': '&' }; return text.replace(/[<>"'&]/g, char => specialChars[char]); } calculateCleaningScore(original, cleaned) { if (original === cleaned) return 100; const lengthDiff = Math.abs(original.length - cleaned.length); const maxLength = Math.max(original.length, cleaned.length); return Math.max(0, 100 - ((lengthDiff / maxLength) * 100)); } getPatternRiskWeight(pattern) { const source = pattern.source.toLowerCase(); if (source.includes('ignore') || source.includes('forget')) return 10; if (source.includes('script') || source.includes('eval')) return 9; if (source.includes('system') || source.includes('exec')) return 8; if (source.includes('instead') || source.includes('pretend')) return 7; if (source.includes('json') || source.includes('only')) return 6; return 5; // Risque par défaut } findPatternLocations(text, pattern) { const locations = []; let match; pattern.lastIndex = 0; // Reset regex while ((match = pattern.exec(text)) !== null) { locations.push({ start: match.index, end: match.index + match[0].length, context: text.substring(Math.max(0, match.index - 20), match.index + match[0].length + 20) }); if (!pattern.global) break; } return locations; } analyzeContentStructure(text) { let riskScore = 0; let suspicious = false; const reasons = []; // Trop de newlines consécutives const excessiveNewlines = (text.match(/\n{3,}/g) || []).length; if (excessiveNewlines > 3) { riskScore += 2; suspicious = true; reasons.push('Trop de sauts de ligne consécutifs'); } // Caractères de séparation suspects const suspiciousSeparators = (text.match(/---+|===+|\*\*\*+/g) || []).length; if (suspiciousSeparators > 2) { riskScore += 3; suspicious = true; reasons.push('Séparateurs suspects détectés'); } // Ratio majuscules anormal const upperCaseRatio = (text.match(/[A-Z]/g) || []).length / text.length; if (upperCaseRatio > 0.3) { riskScore += 2; suspicious = true; reasons.push('Ratio majuscules anormal'); } return { suspicious, riskScore, reasons: reasons.join(', '), metrics: { excessiveNewlines, suspiciousSeparators, upperCaseRatio: Math.round(upperCaseRatio * 100) } }; } summarizePatternDetection(patterns, totalRiskScore) { if (patterns.length === 0) { return 'Aucun pattern dangereux détecté'; } const highRisk = patterns.filter(p => p.riskWeight >= 8).length; const mediumRisk = patterns.filter(p => p.riskWeight >= 5 && p.riskWeight < 8).length; const lowRisk = patterns.length - highRisk - mediumRisk; return `${patterns.length} patterns détectés (Risque élevé: ${highRisk}, moyen: ${mediumRisk}, faible: ${lowRisk})`; } async validateRaceContext(text, raceCode) { if (!raceCode) return { passed: true, score: 1, reason: 'Pas de race spécifique à valider' }; // Extraire numéro de race const raceNumber = raceCode.split('-')[0]; // Rechercher mentions de la race const racePattern = new RegExp(`(${raceNumber}|race\\s+${raceNumber})`, 'gi'); const raceMatches = text.match(racePattern); const passed = raceMatches && raceMatches.length > 0; const score = passed ? 1 : 0; return { passed, score, matches: raceMatches || [], reason: passed ? 'Race mentionnée dans le contenu' : 'Race non mentionnée' }; } detectSemanticInconsistencies(text, context) { const inconsistencies = []; // Vérifier cohérence animal/chien const hasAnimalMention = /animal|pet/gi.test(text); const hasDogMention = /chien|dog|canin/gi.test(text); if (hasAnimalMention && !hasDogMention && context.raceCode) { inconsistencies.push({ type: 'animal_type_mismatch', severity: 'medium', description: 'Mention d\'animaux mais pas de chiens spécifiquement' }); } // Vérifier langue cohérente const frenchWords = (text.match(/\b(le|la|les|de|du|des|et|avec|pour|dans)\b/gi) || []).length; const englishWords = (text.match(/\b(the|and|with|for|in|of|to|a|an)\b/gi) || []).length; if (frenchWords > 0 && englishWords > frenchWords) { inconsistencies.push({ type: 'language_inconsistency', severity: 'low', description: 'Mélange de français et anglais détecté' }); } return inconsistencies; } calculateSemanticConfidence(validationResults, inconsistencies) { const passedRules = validationResults.filter(r => r.passed).length; const totalRules = validationResults.length; const baseConfidence = totalRules > 0 ? passedRules / totalRules : 0; const inconsistencyPenalty = inconsistencies.length * 0.1; return Math.max(0, baseConfidence - inconsistencyPenalty); } assessContextRelevance(text, context) { let relevanceScore = 0; const factors = []; // Contexte race if (context.raceCode && text.includes(context.raceCode.split('-')[0])) { relevanceScore += 0.3; factors.push('Race code found'); } // Contexte produit if (context.productContext && text.toLowerCase().includes(context.productContext.toLowerCase())) { relevanceScore += 0.2; factors.push('Product context relevant'); } // Mots-clés pertinents const relevantKeywords = ['éducation', 'santé', 'comportement', 'alimentation', 'soins']; const foundKeywords = relevantKeywords.filter(keyword => text.toLowerCase().includes(keyword)); relevanceScore += foundKeywords.length * 0.1; if (foundKeywords.length > 0) { factors.push(`${foundKeywords.length} keywords found`); } return { score: Math.min(1, relevanceScore), factors, foundKeywords }; } generateSemanticRecommendations(validationResults, raceValidation) { const recommendations = []; const failedRules = validationResults.filter(r => !r.passed); if (failedRules.length > 0) { recommendations.push({ type: 'semantic_improvement', priority: 'high', message: `Améliorer la pertinence pour: ${failedRules.map(r => r.ruleName).join(', ')}` }); } if (!raceValidation.passed) { recommendations.push({ type: 'race_context', priority: 'medium', message: 'Mentionner la race spécifique dans le contenu' }); } return recommendations; } async checkSourceHistory(content) { // Simulation - À intégrer avec le système de stock const sourceDomain = content.sourceDomain || content.url; if (!sourceDomain) { return { isUntrusted: false, reason: 'Pas de domaine source' }; } // Sources connues non fiables const untrustedDomains = ['example.com', 'test.com', 'spam.com']; if (untrustedDomains.some(domain => sourceDomain.includes(domain))) { return { isUntrusted: true, reason: `Source ${sourceDomain} dans la liste des domaines non fiables` }; } return { isUntrusted: false, reason: 'Source fiable' }; } generateFinalRecommendation(totalPenalty, patternResult, semanticResult) { if (totalPenalty <= -50 || patternResult.hasHighRiskPatterns) { return { action: 'REJECT', reason: 'Risque sécuritaire critique détecté', confidence: 'high' }; } if (totalPenalty <= -30 || !semanticResult.passed) { return { action: 'QUARANTINE', reason: 'Contenu suspect nécessitant révision manuelle', confidence: 'medium' }; } if (totalPenalty <= -10 || patternResult.hasMediumRiskPatterns) { return { action: 'ACCEPT_WITH_MONITORING', reason: 'Risque faible mais surveillance recommandée', confidence: 'medium' }; } return { action: 'ACCEPT', reason: 'Contenu validé, aucun risque détecté', confidence: 'high' }; } generateSecurityRecommendations(patternResult, semanticResult, penaltyResult) { const recommendations = []; if (patternResult.hasHighRiskPatterns) { recommendations.push({ type: 'CRITICAL', message: 'Patterns d\'injection détectés - Rejeter le contenu', patterns: patternResult.detectedPatterns.map(p => p.pattern) }); } if (!semanticResult.passed) { recommendations.push({ type: 'WARNING', message: 'Contenu peu pertinent au contexte demandé', score: Math.round(semanticResult.semanticScore * 100) }); } if (penaltyResult.sourceHistory.isUntrusted) { recommendations.push({ type: 'INFO', message: 'Source historiquement non fiable', details: penaltyResult.sourceHistory.reason }); } return recommendations; } determineValidityStatus(patternResult, semanticResult, penaltyResult) { // Rejet immédiat si patterns critiques if (patternResult.hasHighRiskPatterns) return false; // Rejet si pénalités trop élevées if (penaltyResult.totalPenalty <= -50) return false; // Rejet si sémantique insuffisante ET patterns suspects if (!semanticResult.passed && patternResult.hasMediumRiskPatterns) return false; return true; } calculateRiskLevel(patternResult, semanticResult) { if (patternResult.hasHighRiskPatterns) return 'critical'; if (patternResult.totalRiskScore >= 15) return 'high'; if (!semanticResult.passed || patternResult.hasMediumRiskPatterns) return 'medium'; return 'low'; } // === Cache et performances === generateCacheKey(content, context) { const contentHash = this.simpleHash(content.content + content.title); const contextHash = this.simpleHash(JSON.stringify(context)); return `validation:${contentHash}:${contextHash}`; } simpleHash(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32-bit integer } return hash.toString(36); } getFromCache(cacheKey) { const cached = this.validationCache.get(cacheKey); if (!cached) return null; if (Date.now() - cached.timestamp > this.cacheTimeout) { this.validationCache.delete(cacheKey); return null; } return cached.result; } cacheResult(cacheKey, result) { this.validationCache.set(cacheKey, { result, timestamp: Date.now() }); // Nettoyage périodique du cache if (this.validationCache.size > 1000) { this.cleanupCache(); } } cleanupCache() { const now = Date.now(); for (const [key, cached] of this.validationCache.entries()) { if (now - cached.timestamp > this.cacheTimeout) { this.validationCache.delete(key); } } } // === Statistiques et monitoring === updateValidationStats(result) { this.stats.averageProcessingTime = this.updateRunningAverage( this.stats.averageProcessingTime, result.processingTime, this.stats.totalValidated ); this.stats.riskLevelDistribution[result.riskLevel]++; if (result.layers.patternDetection.hasHighRiskPatterns) { this.stats.injectionAttempts++; } if (!result.layers.semanticValidation.passed) { this.stats.semanticFailures++; } } updateRunningAverage(currentAvg, newValue, totalCount) { if (totalCount === 1) return newValue; const alpha = 1 / totalCount; return alpha * newValue + (1 - alpha) * currentAvg; } logValidationResult(result, content, context) { const logData = { contentId: content.id, riskLevel: result.riskLevel, isValid: result.isValid, processingTime: result.processingTime, patternsDetected: result.layers.patternDetection.totalPatterns, semanticScore: Math.round(result.layers.semanticValidation.semanticScore * 100), totalPenalty: result.layers.penalties.totalPenalty, raceCode: context.raceCode }; switch (result.riskLevel) { case 'critical': logger.securityEvent('CRITICAL security threat detected', 'PROMPT_INJECTION', logData); break; case 'high': logger.securityEvent('HIGH security risk detected', 'SUSPICIOUS_CONTENT', logData); break; case 'medium': logger.warn('Medium security risk in content', logData); break; default: logger.debug('Content validation completed', logData); } } /** * Obtenir statistiques de sécurité */ getSecurityStats() { const cacheStats = { size: this.validationCache.size, hitRate: this.stats.totalValidated > 0 ? (this.stats.totalValidated - this.stats.injectionAttempts - this.stats.semanticFailures) / this.stats.totalValidated : 0 }; return { ...this.stats, cache: cacheStats, engine: 'AntiInjectionEngine', version: '1.0', lastUpdate: new Date().toISOString() }; } /** * Réinitialiser statistiques */ resetStats() { this.stats = { totalValidated: 0, injectionAttempts: 0, semanticFailures: 0, falsePositives: 0, averageProcessingTime: 0, riskLevelDistribution: { low: 0, medium: 0, high: 0, critical: 0 } }; } /** * Health check du moteur de sécurité */ async healthCheck() { try { const testContent = { id: 'health-check', title: 'Test de santé du système', content: 'Contenu de test pour validation du moteur de sécurité', sourceType: 'system' }; const testContext = { raceCode: '352-1', clientId: 'health-check' }; const result = await this.validateContent(testContent, testContext); return { status: 'healthy', engine: 'AntiInjectionEngine', testResult: { processed: true, processingTime: result.processingTime, riskLevel: result.riskLevel }, stats: this.getSecurityStats(), cache: { size: this.validationCache.size, enabled: true } }; } catch (error) { return { status: 'error', engine: 'AntiInjectionEngine', error: error.message }; } } } module.exports = AntiInjectionEngine;