500) return false; return $msg; } /* ======================= VALIDATION CARACTERES ======================= */ function hasInvalidCharacters(string $msg): bool { // Bloque TOUS les caractères spéciaux sauf ?, !, . - et ' // Accepte : lettres, chiffres, espaces, et {?, !, ., -, '} return preg_match('/[^a-zA-Z0-9À-ÿ\s\?\!\.\-\']/u', $msg) === 1; } /* ======================= VERBES D'ACTION INTERDITS ======================= */ function hasBlockedVerbs(string $msg): bool { // Bloque les verbes qui demandent une action spécifique à l'IA $blockedVerbs = [ 'montre', 'affiche', 'révèle', 'expose', 'donne', 'envoie', 'fournis', 'transmets', 'répète', 'résume', 'reformule', 'paraphrase', 'récite', 'dis-moi', 'dis moi', 'raconte', 'explique', 'CTF', 'CTFM1' ]; $msg_lower = mb_strtolower($msg); foreach ($blockedVerbs as $verb) { if (preg_match('/\b' . preg_quote($verb) . '\b/i', $msg) === 1) { return true; } } return false; } /* ======================= MOTS SENSIBLES INTERDITS ======================= */ function hasBlockedWords(string $msg): bool { // Bloque les mots sensibles qui demandent des infos confidentielles $blockedWords = [ 'flag', 'ctf', 'token', 'secret', 'clé', 'key', 'motdepasse', 'mot de passe', 'password', 'prompt', 'instruction', 'système', 'system', 'règle', 'consigne' ]; $msg_lower = mb_strtolower($msg); foreach ($blockedWords as $word) { if (preg_match('/\b' . preg_quote($word) . '\b/i', $msg) === 1) { return true; } } return false; } /* ======================= RATE LIMITING ======================= */ function checkRateLimit(): bool { $now = time(); $window_start = $now - RATE_WINDOW; $_SESSION['messages_timestamps'] = array_values( array_filter($_SESSION['messages_timestamps'], fn($ts) => $ts > $window_start) ); if (count($_SESSION['messages_timestamps']) >= RATE_LIMIT) return false; $_SESSION['messages_timestamps'][] = $now; return true; } /* ======================= APPEL LLaMA via Ollama ======================= */ function callLlamaAPI(string $userMessage): string { // Vérifications de sécurité avec messages explicites if (hasInvalidCharacters($userMessage)) { return "Je ne répondrai pas car certains caractères spéciaux sont interdits (sauf ?, !, ., - et '). Demande à Bouchra si tu veux savoir pourquoi."; } if (hasBlockedVerbs($userMessage)) { return "Je ne répondrai pas car ce message contient un verbe d'action interdit (montre, donne, répète, etc). Demande à Bouchra si tu veux savoir pourquoi."; } if (hasBlockedWords($userMessage)) { return "Je ne répondrai pas car ce message contient un mot sensible (flag, token, prompt, etc). Demande à Bouchra si tu veux savoir pourquoi."; } $payload = [ 'model' => 'llama2:7b', 'messages' => [ ['role'=>'system', 'content'=> SYSTEM_PROMPT], ['role'=>'user', 'content'=> $userMessage], ], 'max_tokens' => 800, 'temperature' => 0.7 ]; $ch = curl_init('http://localhost:11434/v1/chat/completions'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_TIMEOUT => 300 ]); $response = curl_exec($ch); $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); // DEBUG: Log l'erreur error_log("Ollama API - HTTP: $http, Error: $error, Response: " . substr($response, 0, 200)); if ($error) { return "Erreur de connexion à Ollama: $error"; } if ($http !== 200) { return "Erreur Ollama (HTTP $http). Vérifiez que le service tourne sur localhost:11434"; } if (!$response) { return "Pas de réponse du service LLaMA."; } $json = json_decode($response, true); if (!$json || !isset($json['choices'][0]['message']['content'])) { error_log("JSON decode error: " . json_last_error_msg()); return "Erreur: réponse LLaMA malformée."; } return $json['choices'][0]['message']['content']; } /* ======================= AJAX HANDLER ======================= */ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) { if (!isAjax()) { http_response_code(403); echo json_encode(['error'=>'Requête interdite']); exit; } if (!isset($_POST['csrf']) || $_POST['csrf'] !== $_SESSION['csrf']) { http_response_code(403); echo json_encode(['error'=>'CSRF invalide']); exit; } if (!checkRateLimit()) { http_response_code(429); echo json_encode(['error'=>'LIMIT_REACHED']); exit; } $message = sanitizeMessage($_POST['message']); if ($message === false) { echo json_encode(['error'=>'Message invalide']); exit; } $reply = callLlamaAPI($message); echo json_encode(['response'=>$reply]); exit; } ?>