diff --git a/README.md b/README.md index 0887cd6..bfaa5f0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,351 @@ -# SIEM---Wazuh +# Plugin GLPI SIEM-Wazuh -Plugin GLPI pour intéger Wazuh dans GLPI \ No newline at end of file +[![Version](https://img.shields.io/badge/version-1.0.0-blue.svg)](https://github.com/siem-wazuh/glpi-plugin) +[![GLPI](https://img.shields.io/badge/GLPI-10.0.x-green.svg)](https://glpi-project.org/) +[![License](https://img.shields.io/badge/license-GPL%20v2%2B-orange.svg)](LICENSE) + +## Description + +Le plugin SIEM-Wazuh permet l'intégration complète entre Wazuh (solution SIEM open source) et GLPI (système de gestion des services IT). Il permet de : + +- **Surveiller** automatiquement les serveurs Wazuh configurés +- **Récupérer** les alertes de sécurité en temps réel +- **Associer** automatiquement les alertes aux assets de l'inventaire GLPI +- **Créer** automatiquement des tickets pour les incidents critiques +- **Gérer** centralement la configuration depuis l'interface GLPI + +## Fonctionnalités + +### 🛡️ Surveillance Proactive +- Synchronisation automatique des alertes Wazuh +- Configuration de multiples serveurs Wazuh +- Filtrage par niveau de règle et sévérité +- Intervalle de synchronisation configurable + +### 🔗 Intégration Assets +- Association automatique alertes ↔ ordinateurs +- Association automatique alertes ↔ équipements réseau +- Correspondance par nom d'hôte et adresse IP +- Onglets dédiés sur chaque asset + +### 🎫 Gestion des Tickets +- Création automatique de tickets GLPI +- Configuration des types et catégories +- Priorité automatique basée sur la sévérité +- Assignation automatique configurables + +### 🌍 Interface Multilingue +Support complet pour : +- 🇫🇷 Français +- 🇬🇧 Anglais +- 🇩🇪 Allemand +- 🇪🇸 Espagnol +- 🇮🇹 Italien +- 🇵🇹 Portugais +- 🇵🇱 Polonais + +### ⚙️ Configuration Avancée +- Interface de configuration intuitive +- Gestion granulaire des droits utilisateur +- Logs de débogage détaillés +- Statistiques et tableaux de bord + +## Prérequis + +### Système +- **GLPI** : Version 10.0.0 minimum +- **PHP** : Version 7.4 minimum +- **Extensions PHP** : curl, json, mbstring, openssl +- **Base de données** : MySQL 5.7+ ou MariaDB 10.2+ + +### Wazuh +- **Wazuh Manager** : Version 4.0+ recommandée +- **API Wazuh** : Accès avec authentification +- **Wazuh Indexer** : Optionnel (pour requêtes avancées) + +## Installation + +### 1. Téléchargement + +```bash +cd /path/to/glpi/plugins/ +wget https://github.com/siem-wazuh/glpi-plugin/releases/download/1.0.0/siem-wazuh-1.0.0.tar.gz +tar -xzf siem-wazuh-1.0.0.tar.gz +``` + +### 2. Installation via Interface GLPI + +1. Se connecter en tant qu'administrateur GLPI +2. Aller dans **Configuration > Plugins** +3. Localiser le plugin "SIEM - Wazuh" +4. Cliquer sur **Installer** +5. Cliquer sur **Activer** + +### 3. Configuration Initiale + +#### Configuration des Droits +1. **Administration > Profils** +2. Sélectionner le profil à configurer +3. Onglet **SIEM Wazuh Rights** +4. Attribuer les droits appropriés : + - **Configuration** : Accès à la configuration + - **Servers** : Gestion des serveurs Wazuh + - **Alerts** : Consultation des alertes + +#### Ajout d'un Serveur Wazuh +1. **Administration > Serveurs Wazuh** +2. Cliquer sur **+** (Ajouter) +3. Remplir les informations : + ``` + Nom connexion: Serveur Wazuh Production + URL Serveur Wazuh: https://wazuh-manager.domain.com + Port API: 55000 + Login API: wazuh + Mot de passe API: [mot_de_passe_sécurisé] + Intervalle de synchronisation: 300 (secondes) + ``` +4. **Tester la connexion** +5. **Enregistrer** + +#### Configuration Générale +1. **Outils > Configuration SIEM Wazuh** +2. Configurer les paramètres selon vos besoins : + - **Synchronisation automatique** : Activé + - **Création automatique de tickets** : Activé + - **Niveau de règle minimum** : 5 + - **Rétention des alertes** : 90 jours + +## Configuration + +### Paramètres de Synchronisation + +| Paramètre | Description | Valeur par défaut | +|-----------|-------------|-------------------| +| `sync_enabled` | Active la synchronisation | `1` (Oui) | +| `max_alerts_per_sync` | Maximum d'alertes par sync | `100` | +| `min_rule_level` | Niveau minimum des règles | `5` | +| `sync_interval` | Intervalle en secondes | `300` | + +### Mapping des Assets + +Le plugin associe automatiquement les alertes aux assets GLPI : + +1. **Par nom d'hôte** : `agent.name` → `computers.name` +2. **Par adresse IP** : `agent.ip` → `networkports.ip` +3. **Sensible à la casse** : Configurable +4. **Patterns à ignorer** : `localhost`, `127.0.0.1`, etc. + +### Création de Tickets + +Les tickets sont créés automatiquement selon ces règles : + +| Niveau Règle | Sévérité | Priorité Ticket | +|--------------|----------|-----------------| +| 1-4 | Low | Très faible | +| 5-6 | Low | Faible | +| 7-9 | Medium | Moyenne | +| 10-11 | High | Élevée | +| 12+ | Critical | Très élevée | + +## Utilisation + +### Consultation des Alertes + +#### Sur les Assets +- Aller sur un ordinateur ou équipement réseau +- Cliquer sur l'onglet **"Alertes Wazuh"** +- Consulter le résumé et la liste des alertes + +#### Vue Globale +- **Outils > Configuration SIEM Wazuh** +- Consulter les **statistiques générales** +- Filtrer par serveur, sévérité, statut + +### Actions Disponibles + +#### Sur les Alertes +- ✅ **Marquer comme traitée** +- ❌ **Ignorer l'alerte** +- 🎫 **Créer un ticket manuellement** +- 📊 **Exporter en CSV** + +#### Sur les Serveurs +- 🔧 **Tester la connexion** +- 🔄 **Synchroniser manuellement** +- ⏯️ **Activer/Désactiver** +- 📈 **Consulter les statistiques** + +## Tâches Automatisées + +### Cron Jobs + +Le plugin configure automatiquement deux tâches : + +```bash +# Synchronisation des alertes (toutes les 5 minutes) +*/5 * * * * php /path/to/glpi/front/cron.php --force=PluginSiemWazuhAlert-sync_alerts + +# Nettoyage des anciennes alertes (quotidien) +0 2 * * * php /path/to/glpi/front/cron.php --force=PluginSiemWazuhAlert-cleanup_old_alerts +``` + +### Activation des Tâches + +1. **Administration > Tâches automatiques** +2. Rechercher "Wazuh" +3. Activer les tâches +4. Configurer la fréquence si nécessaire + +## Dépannage + +### Problèmes Courants + +#### Connexion Échouée +``` +Erreur: Connection failed: cURL error 7: Failed to connect +``` +**Solutions :** +- Vérifier l'URL et le port du serveur +- Contrôler les règles de firewall +- Tester depuis le serveur GLPI : `curl -k https://wazuh-server:55000` + +#### Authentification Échouée +``` +Erreur: Authentication failed: 401 Unauthorized +``` +**Solutions :** +- Vérifier les identifiants API +- Contrôler les permissions utilisateur Wazuh +- Vérifier que l'utilisateur existe : `GET /security/users` + +#### Pas d'Alertes Synchronisées +**Vérifications :** +1. Niveau de règle minimum trop élevé +2. Serveur inactif +3. Pas d'alertes récentes dans Wazuh +4. Erreurs dans les logs : **Debug & Logs** + +### Logs de Débogage + +Activer le mode debug : +1. **Outils > Configuration SIEM Wazuh** +2. Onglet **Debug & Logs** +3. **Mode debug** : Activé +4. **Niveau de log** : Debug +5. Consulter la section **Logs récents** + +### Support et Logs Système + +```bash +# Logs Apache/Nginx +tail -f /var/log/apache2/error.log | grep -i wazuh + +# Logs PHP +tail -f /var/log/php/error.log | grep -i wazuh + +# Logs GLPI +tail -f /var/log/glpi/glpi.log | grep -i wazuh +``` + +## API et Intégration + +### Endpoints Disponibles + +Le plugin expose quelques endpoints pour intégration : + +``` +GET /plugins/siem-wazuh/ajax/sync_alerts.php?action=status +POST /plugins/siem-wazuh/ajax/sync_alerts.php (sync) +POST /plugins/siem-wazuh/ajax/test_connection.php +``` + +### Webhooks Wazuh + +Pour recevoir les alertes en temps réel, configurez un webhook : + +```xml + + + webhook + https://glpi.domain.com/plugins/siem-wazuh/webhook.php + 7 + 100001,100002 + json + +``` + +## Développement + +### Structure du Plugin + +``` +plugins/siem-wazuh/ +├── README.md # Documentation +├── CHANGELOG.md # Historique des versions +├── LICENSE # Licence GPL v2+ +├── SIEM-Wazuh.xml # Descripteur plugin +├── setup.php # Configuration principale +├── hook.php # Installation/désinstallation +├── inc/ # Classes PHP +│ ├── wazuhserver.class.php +│ ├── wazuhalert.class.php +│ ├── wazuhconfig.class.php +│ ├── wazuhtab.class.php +│ └── wazuhapi.class.php +├── front/ # Interface web +│ ├── wazuhserver.php +│ ├── wazuhserver.form.php +│ └── wazuhconfig.php +├── ajax/ # Requêtes AJAX +│ ├── test_connection.php +│ └── sync_alerts.php +├── locales/ # Traductions +│ ├── fr_FR.po +│ ├── en_GB.po +│ └── ... +├── css/ # Feuilles de style +│ └── style.css +├── js/ # JavaScript +│ └── wazuh.js +├── pics/ # Images +│ └── wazuh-logo.png +└── sql/ # Scripts SQL + ├── install.sql + └── uninstall.sql +``` + +### Contribution + +1. Fork le projet +2. Créer une branche feature (`git checkout -b feature/nouvelle-fonctionnalite`) +3. Commiter les modifications (`git commit -m 'Ajout nouvelle fonctionnalité'`) +4. Pousser sur la branche (`git push origin feature/nouvelle-fonctionnalite`) +5. Créer une Pull Request + +## Licence + +Ce plugin est distribué sous licence **GPL v2+**. Voir le fichier [LICENSE](LICENSE) pour plus de détails. + +## Support + +- 🐛 **Issues** : [GitHub Issues](https://github.com/siem-wazuh/glpi-plugin/issues) +- 💬 **Discussions** : [GitHub Discussions](https://github.com/siem-wazuh/glpi-plugin/discussions) +- 📧 **Email** : support@siem-wazuh.org +- 📖 **Documentation** : [Wiki](https://github.com/siem-wazuh/glpi-plugin/wiki) + +## Changelog + +### Version 1.0.0 (2024-01-01) +- 🎉 Version initiale +- ✅ Support multiples serveurs Wazuh +- ✅ Synchronisation automatique des alertes +- ✅ Association avec assets GLPI +- ✅ Création automatique de tickets +- ✅ Interface multilingue (7 langues) +- ✅ Gestion des droits utilisateur +- ✅ API REST pour intégration +- ✅ Logs de débogage complets + +--- + +**Made with ❤️ by the SIEM-Wazuh Team** \ No newline at end of file diff --git a/SIEM-Wazuh.xml b/SIEM-Wazuh.xml new file mode 100644 index 0000000..6f355a9 --- /dev/null +++ b/SIEM-Wazuh.xml @@ -0,0 +1,106 @@ + + + SIEM - Wazuh + siem-wazuh + stable + https://raw.githubusercontent.com/siem-wazuh/glpi-plugin/main/pics/wazuh-logo.png + + + + + + + + + + + + + + + + https://github.com/siem-wazuh/glpi-plugin + https://github.com/siem-wazuh/glpi-plugin/releases + https://github.com/siem-wazuh/glpi-plugin/issues + https://github.com/siem-wazuh/glpi-plugin/blob/main/README.md + + SIEM-Wazuh Team + + + + 1.0.0 + ~10.0.0 + https://github.com/siem-wazuh/glpi-plugin/releases/download/1.0.0/glpi-siem-wazuh-1.0.0.tar.gz + + 7.4 + 10.0.0 + 10.0.99 + + + + fr_FR + en_GB + de_DE + es_ES + it_IT + pt_BR + pl_PL + + GPL v2+ + + + SIEM + Sécurité + Wazuh + Alertes + Monitoring + Intégration + + + SIEM + Security + Wazuh + Alerts + Monitoring + Integration + + + + https://raw.githubusercontent.com/siem-wazuh/glpi-plugin/main/pics/screenshot-1.png + https://raw.githubusercontent.com/siem-wazuh/glpi-plugin/main/pics/screenshot-2.png + https://raw.githubusercontent.com/siem-wazuh/glpi-plugin/main/pics/screenshot-3.png + + \ No newline at end of file diff --git a/ajax/sync_alerts.php b/ajax/sync_alerts.php new file mode 100644 index 0000000..e1cb8b1 --- /dev/null +++ b/ajax/sync_alerts.php @@ -0,0 +1,342 @@ + false, 'message' => __('Access denied', 'siem-wazuh')]); + exit; +} + +// Vérification CSRF pour les requêtes POST +if ($_SERVER['REQUEST_METHOD'] === 'POST' && !Session::validateCSRF($_POST)) { + echo json_encode(['success' => false, 'message' => __('Invalid CSRF token', 'siem-wazuh')]); + exit; +} + +$response = ['success' => false, 'message' => '', 'stats' => []]; + +try { + $action = $_POST['action'] ?? $_GET['action'] ?? 'sync'; + + switch ($action) { + case 'sync': + $server_id = intval($_POST['server_id'] ?? $_GET['server_id'] ?? 0); + + if ($server_id > 0) { + // Synchronisation d'un serveur spécifique + $result = syncSingleServer($server_id); + } else { + // Synchronisation de tous les serveurs actifs + $result = syncAllServers(); + } + + $response = $result; + break; + + case 'sync_for_item': + // Synchronisation pour un élément spécifique + $item_id = intval($_POST['item_id'] ?? 0); + $item_type = $_POST['item_type'] ?? ''; + + if ($item_id > 0 && !empty($item_type)) { + $result = syncForItem($item_id, $item_type); + $response = $result; + } else { + throw new Exception(__('Invalid item parameters', 'siem-wazuh')); + } + break; + + case 'status': + // Statut de la synchronisation + $response = getSyncStatus(); + break; + + default: + throw new Exception(__('Unknown action', 'siem-wazuh')); + } + +} catch (Exception $e) { + $response = [ + 'success' => false, + 'message' => $e->getMessage(), + 'error_type' => 'exception' + ]; +} + +echo json_encode($response, JSON_UNESCAPED_UNICODE); + +/** + * Synchroniser un serveur spécifique + */ +function syncSingleServer($server_id) { + $server = new PluginSiemWazuhServer(); + if (!$server->getFromDB($server_id)) { + throw new Exception(__('Server not found', 'siem-wazuh')); + } + + $start_time = microtime(true); + $result = $server->syncAlerts(); + $sync_time = round((microtime(true) - $start_time) * 1000, 2); + + if ($result['success']) { + // Extraire le nombre d'alertes du message + $processed = 0; + if (preg_match('/(\d+)/', $result['message'], $matches)) { + $processed = intval($matches[1]); + } + + Event::log( + $server_id, + "PluginSiemWazuhServer", + 5, + "sync", + sprintf(__('Manual sync completed for server %s: %s'), $server->fields['name'], $result['message']) + ); + + return [ + 'success' => true, + 'message' => $result['message'], + 'stats' => [ + 'processed' => $processed, + 'sync_time' => $sync_time, + 'server_name' => $server->fields['name'] + ] + ]; + } else { + Event::log( + $server_id, + "PluginSiemWazuhServer", + 2, + "sync", + sprintf(__('Manual sync failed for server %s: %s'), $server->fields['name'], $result['message']) + ); + + return [ + 'success' => false, + 'message' => $result['message'], + 'server_name' => $server->fields['name'] + ]; + } +} + +/** + * Synchroniser tous les serveurs actifs + */ +function syncAllServers() { + global $DB; + + $iterator = $DB->request([ + 'FROM' => 'glpi_plugin_siem_wazuh_servers', + 'WHERE' => ['is_active' => 1] + ]); + + $total_processed = 0; + $successful_servers = 0; + $failed_servers = 0; + $sync_results = []; + + $start_time = microtime(true); + + foreach ($iterator as $server_data) { + try { + $result = syncSingleServer($server_data['id']); + + if ($result['success']) { + $successful_servers++; + $total_processed += $result['stats']['processed'] ?? 0; + $sync_results[] = [ + 'server' => $server_data['name'], + 'status' => 'success', + 'processed' => $result['stats']['processed'] ?? 0 + ]; + } else { + $failed_servers++; + $sync_results[] = [ + 'server' => $server_data['name'], + 'status' => 'failed', + 'error' => $result['message'] + ]; + } + } catch (Exception $e) { + $failed_servers++; + $sync_results[] = [ + 'server' => $server_data['name'], + 'status' => 'error', + 'error' => $e->getMessage() + ]; + } + } + + $total_time = round((microtime(true) - $start_time) * 1000, 2); + + $message = sprintf( + __('Sync completed: %d servers processed, %d successful, %d failed, %d total alerts processed'), + count($sync_results), + $successful_servers, + $failed_servers, + $total_processed + ); + + return [ + 'success' => $failed_servers == 0, + 'message' => $message, + 'stats' => [ + 'total_servers' => count($sync_results), + 'successful_servers' => $successful_servers, + 'failed_servers' => $failed_servers, + 'total_processed' => $total_processed, + 'sync_time' => $total_time + ], + 'details' => $sync_results + ]; +} + +/** + * Synchroniser pour un élément spécifique + */ +function syncForItem($item_id, $item_type) { + // Cette fonction recherche et synchronise les alertes + // pour un élément spécifique (ordinateur, équipement réseau, etc.) + + $valid_types = ['Computer', 'NetworkEquipment', 'Peripheral', 'Phone', 'Printer']; + if (!in_array($item_type, $valid_types)) { + throw new Exception(__('Invalid item type', 'siem-wazuh')); + } + + $item = new $item_type(); + if (!$item->getFromDB($item_id)) { + throw new Exception(__('Item not found', 'siem-wazuh')); + } + + // Récupérer les informations de l'élément pour le matching + $search_criteria = []; + + // Recherche par nom + if (!empty($item->fields['name'])) { + $search_criteria['hostname'] = $item->fields['name']; + } + + // Recherche par IP (si disponible) + $ip_addresses = []; + if (method_exists($item, 'getNetworkInterfaces')) { + // Logique pour récupérer les adresses IP de l'élément + // Cette partie nécessiterait une implémentation plus détaillée + } + + // Synchroniser avec tous les serveurs actifs en filtrant par critères + global $DB; + $iterator = $DB->request([ + 'FROM' => 'glpi_plugin_siem_wazuh_servers', + 'WHERE' => ['is_active' => 1] + ]); + + $total_found = 0; + + foreach ($iterator as $server_data) { + $server = new PluginSiemWazuhServer(); + $server->fields = $server_data; + + $api = new PluginSiemWazuhAPI($server); + + // Construire les paramètres de recherche pour l'API + $search_params = []; + if (!empty($search_criteria['hostname'])) { + $search_params['agent.name'] = $search_criteria['hostname']; + } + + try { + $result = $api->getAlerts($search_params); + if ($result['success'] && isset($result['data']['data'])) { + $alert = new PluginSiemWazuhAlert(); + $processed = $alert->processAlerts($server_data['id'], $result['data']['data']); + $total_found += $processed; + } + } catch (Exception $e) { + // Log l'erreur mais continue avec les autres serveurs + error_log("SIEM-Wazuh: Error syncing for item $item_type:$item_id on server {$server_data['name']}: " . $e->getMessage()); + } + } + + return [ + 'success' => true, + 'message' => sprintf(__('%d alerts found and processed for %s'), $total_found, $item->getName()), + 'stats' => [ + 'processed' => $total_found, + 'item_name' => $item->getName(), + 'item_type' => $item_type + ] + ]; +} + +/** + * Obtenir le statut de synchronisation + */ +function getSyncStatus() { + global $DB; + + // Statistiques générales + $stats = []; + + // Nombre total de serveurs + $stats['total_servers'] = countElementsInTable('glpi_plugin_siem_wazuh_servers'); + $stats['active_servers'] = countElementsInTable('glpi_plugin_siem_wazuh_servers', ['is_active' => 1]); + + // Dernière synchronisation + $last_sync = $DB->request([ + 'SELECT' => ['MAX' => 'last_sync AS last_sync'], + 'FROM' => 'glpi_plugin_siem_wazuh_servers', + 'WHERE' => ['is_active' => 1] + ])->current(); + + $stats['last_sync'] = $last_sync['last_sync']; + + // Alertes par statut + $alert_stats = []; + $statuses = ['new', 'processed', 'ignored', 'ticket_created']; + foreach ($statuses as $status) { + $alert_stats[$status] = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['status' => $status]); + } + $stats['alerts'] = $alert_stats; + + // Alertes par sévérité + $severity_stats = []; + $severities = ['low', 'medium', 'high', 'critical']; + foreach ($severities as $severity) { + $severity_stats[$severity] = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['severity' => $severity]); + } + $stats['severity'] = $severity_stats; + + // Alertes des dernières 24h + $stats['alerts_24h'] = countElementsInTable( + 'glpi_plugin_siem_wazuh_alerts', + ['date_creation' => ['>=', date('Y-m-d H:i:s', strtotime('-24 hours'))]] + ); + + // Tickets créés aujourd'hui + $stats['tickets_today'] = countElementsInTable( + 'glpi_plugin_siem_wazuh_alerts', + [ + 'status' => 'ticket_created', + 'date_mod' => ['>=', date('Y-m-d 00:00:00')] + ] + ); + + // Vérifier s'il y a une synchronisation en cours + $config = new PluginSiemWazuhConfig(); + $sync_in_progress = $config->getConfiguration('sync_in_progress', 0); + + return [ + 'success' => true, + 'message' => __('Status retrieved successfully', 'siem-wazuh'), + 'stats' => $stats, + 'sync_in_progress' => (bool)$sync_in_progress, + 'timestamp' => date('Y-m-d H:i:s') + ]; +} \ No newline at end of file diff --git a/ajax/test_connection.php b/ajax/test_connection.php new file mode 100644 index 0000000..d96f216 --- /dev/null +++ b/ajax/test_connection.php @@ -0,0 +1,81 @@ + false, 'message' => __('Access denied', 'siem-wazuh')]); + exit; +} + +// Vérification CSRF +if (!Session::validateCSRF($_POST)) { + echo json_encode(['success' => false, 'message' => __('Invalid CSRF token', 'siem-wazuh')]); + exit; +} + +$response = ['success' => false, 'message' => '']; + +try { + $server_id = intval($_POST['server_id'] ?? 0); + + if ($server_id <= 0) { + throw new Exception(__('Invalid server ID', 'siem-wazuh')); + } + + $server = new PluginSiemWazuhServer(); + if (!$server->getFromDB($server_id)) { + throw new Exception(__('Server not found', 'siem-wazuh')); + } + + // Test de connexion + $result = $server->testConnection(); + + if ($result['success']) { + $response = [ + 'success' => true, + 'message' => $result['message'], + 'server_info' => $result['data'] ?? null, + 'timestamp' => date('Y-m-d H:i:s') + ]; + + // Log du test réussi + Event::log( + $server_id, + "PluginSiemWazuhServer", + 5, + "connection", + sprintf(__('Connection test successful for server %s'), $server->fields['name']) + ); + } else { + $response = [ + 'success' => false, + 'message' => $result['message'], + 'error_type' => 'connection_failed' + ]; + + // Log du test échoué + Event::log( + $server_id, + "PluginSiemWazuhServer", + 2, + "connection", + sprintf(__('Connection test failed for server %s: %s'), $server->fields['name'], $result['message']) + ); + } + +} catch (Exception $e) { + $response = [ + 'success' => false, + 'message' => $e->getMessage(), + 'error_type' => 'exception' + ]; +} + +echo json_encode($response, JSON_UNESCAPED_UNICODE); \ No newline at end of file diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..cb53700 --- /dev/null +++ b/css/style.css @@ -0,0 +1,487 @@ +/** + * SIEM-Wazuh Plugin for GLPI - Stylesheet + * Styles for the SIEM-Wazuh plugin interface + */ + +/* General plugin styles */ +.siem-wazuh-container { + padding: 15px; +} + +.siem-wazuh-header { + display: flex; + align-items: center; + margin-bottom: 20px; + padding: 10px; + background: #f8f9fa; + border-radius: 5px; +} + +.siem-wazuh-logo { + width: 32px; + height: 32px; + margin-right: 10px; +} + +/* Server status indicators */ +.server-status { + display: inline-block; + padding: 3px 8px; + border-radius: 3px; + font-size: 11px; + font-weight: bold; + text-transform: uppercase; +} + +.server-status.active { + background-color: #28a745; + color: white; +} + +.server-status.inactive { + background-color: #dc3545; + color: white; +} + +.server-status.testing { + background-color: #ffc107; + color: #212529; +} + +/* Alert severity badges */ +.severity-badge { + display: inline-block; + padding: 2px 6px; + border-radius: 3px; + font-size: 10px; + font-weight: bold; + text-transform: uppercase; +} + +.severity-low { + background-color: #28a745; + color: white; +} + +.severity-medium { + background-color: #ffc107; + color: #212529; +} + +.severity-high { + background-color: #fd7e14; + color: white; +} + +.severity-critical { + background-color: #dc3545; + color: white; +} + +/* Status badges */ +.status-badge { + display: inline-block; + padding: 2px 6px; + border-radius: 3px; + font-size: 10px; + font-weight: bold; +} + +.status-new { + background-color: #17a2b8; + color: white; +} + +.status-processed { + background-color: #28a745; + color: white; +} + +.status-ignored { + background-color: #6c757d; + color: white; +} + +.status-ticket { + background-color: #007bff; + color: white; +} + +.status-ticket_created { + background-color: #007bff; + color: white; +} + +/* Rule level indicators */ +.rule-level-1, +.rule-level-2, +.rule-level-3, +.rule-level-4 { + background-color: #28a745; + color: white; +} + +.rule-level-5, +.rule-level-6, +.rule-level-7 { + background-color: #ffc107; + color: #212529; +} + +.rule-level-8, +.rule-level-9, +.rule-level-10, +.rule-level-11 { + background-color: #fd7e14; + color: white; +} + +.rule-level-12, +.rule-level-13, +.rule-level-14, +.rule-level-15 { + background-color: #dc3545; + color: white; +} + +/* Alert summary styles */ +.alert-summary { + margin-bottom: 20px; +} + +.alert-summary .tab_cadre_fixe { + border: 1px solid #dee2e6; +} + +.alert-summary td { + text-align: center; + padding: 10px; + border-right: 1px solid #dee2e6; +} + +.alert-summary td:last-child { + border-right: none; +} + +.alert-summary strong { + font-size: 18px; + display: block; + margin-bottom: 5px; +} + +/* Filters section */ +.alert-filters { + background-color: #f8f9fa; + padding: 15px; + border-radius: 5px; + margin-bottom: 20px; +} + +/* Action buttons */ +.action-buttons { + white-space: nowrap; +} + +.action-buttons .btn { + margin: 0 2px; + padding: 4px 8px; + font-size: 11px; + border-radius: 3px; +} + +.btn-primary { + background-color: #007bff; + border-color: #007bff; + color: white; +} + +.btn-success { + background-color: #28a745; + border-color: #28a745; + color: white; +} + +.btn-warning { + background-color: #ffc107; + border-color: #ffc107; + color: #212529; +} + +.btn-danger { + background-color: #dc3545; + border-color: #dc3545; + color: white; +} + +.btn-secondary { + background-color: #6c757d; + border-color: #6c757d; + color: white; +} + +/* JSON data display */ +.json-data { + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 4px; + padding: 10px; + font-family: Monaco, Menlo, "Ubuntu Mono", monospace; + font-size: 12px; + max-height: 300px; + overflow-y: auto; + white-space: pre-wrap; +} + +/* Configuration tabs */ +.config-tabs { + margin-bottom: 20px; +} + +.config-tabs .nav-tabs { + border-bottom: 1px solid #dee2e6; + margin-bottom: 0; +} + +.config-tabs .nav-item { + display: inline-block; + margin-bottom: -1px; +} + +.config-tabs .nav-link { + display: block; + padding: 10px 15px; + text-decoration: none; + color: #495057; + border: 1px solid transparent; + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; + margin-right: 2px; +} + +.config-tabs .nav-link:hover { + border-color: #e9ecef #e9ecef #dee2e6; + text-decoration: none; +} + +.config-tabs .nav-link.active { + color: #495057; + background-color: #fff; + border-color: #dee2e6 #dee2e6 #fff; +} + +/* Log level badges */ +.log-debug { + background-color: #d1ecf1; + color: #0c5460; + padding: 2px 6px; + border-radius: 3px; + font-size: 10px; + font-weight: bold; +} + +.log-info { + background-color: #bee5eb; + color: #0c5460; + padding: 2px 6px; + border-radius: 3px; + font-size: 10px; + font-weight: bold; +} + +.log-warning { + background-color: #fff3cd; + color: #856404; + padding: 2px 6px; + border-radius: 3px; + font-size: 10px; + font-weight: bold; +} + +.log-error { + background-color: #f8d7da; + color: #721c24; + padding: 2px 6px; + border-radius: 3px; + font-size: 10px; + font-weight: bold; +} + +.log-critical { + background-color: #f5c6cb; + color: #721c24; + padding: 2px 6px; + border-radius: 3px; + font-size: 10px; + font-weight: bold; +} + +/* Alert messages */ +.alert { + padding: 12px 20px; + margin: 15px 0; + border: 1px solid transparent; + border-radius: 4px; +} + +.alert-success { + color: #155724; + background-color: #d4edda; + border-color: #c3e6cb; +} + +.alert-info { + color: #0c5460; + background-color: #d1ecf1; + border-color: #bee5eb; +} + +.alert-warning { + color: #856404; + background-color: #fff3cd; + border-color: #ffeaa7; +} + +.alert-danger { + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb; +} + +/* Statistics cards */ +.stats-card { + background: #fff; + border: 1px solid #dee2e6; + border-radius: 5px; + padding: 20px; + margin-bottom: 20px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.stats-card h3 { + color: #495057; + font-size: 18px; + margin-bottom: 15px; + border-bottom: 1px solid #dee2e6; + padding-bottom: 10px; +} + +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 15px; + margin-top: 20px; +} + +.stat-item { + text-align: center; + padding: 15px; + background: #f8f9fa; + border-radius: 5px; +} + +.stat-number { + font-size: 24px; + font-weight: bold; + color: #007bff; + display: block; +} + +.stat-label { + font-size: 12px; + color: #6c757d; + margin-top: 5px; +} + +/* Responsive design */ +@media (max-width: 768px) { + .alert-summary td { + padding: 5px; + font-size: 12px; + } + + .alert-summary strong { + font-size: 14px; + } + + .action-buttons .btn { + padding: 2px 4px; + font-size: 10px; + margin: 1px; + } + + .stats-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +/* Loading animations */ +.loading-spinner { + display: inline-block; + width: 16px; + height: 16px; + border: 2px solid #f3f3f3; + border-top: 2px solid #007bff; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-right: 5px; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Tooltip styles */ +.wazuh-tooltip { + position: relative; + display: inline-block; + cursor: help; +} + +.wazuh-tooltip .tooltiptext { + visibility: hidden; + width: 200px; + background-color: #555; + color: #fff; + text-align: center; + border-radius: 6px; + padding: 5px; + position: absolute; + z-index: 1; + bottom: 125%; + left: 50%; + margin-left: -100px; + opacity: 0; + transition: opacity 0.3s; + font-size: 11px; +} + +.wazuh-tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} + +/* Dark theme support */ +@media (prefers-color-scheme: dark) { + .siem-wazuh-header { + background: #343a40; + color: #fff; + } + + .alert-filters { + background-color: #343a40; + color: #fff; + } + + .json-data { + background-color: #2d3748; + color: #e2e8f0; + border-color: #4a5568; + } + + .stats-card { + background: #343a40; + color: #fff; + border-color: #4a5568; + } +} \ No newline at end of file diff --git a/front/wazuhalert.php b/front/wazuhalert.php new file mode 100644 index 0000000..ad80d13 --- /dev/null +++ b/front/wazuhalert.php @@ -0,0 +1,706 @@ + 'common', + 'name' => __('Characteristics') + ]; + + $tab[] = [ + 'id' => '1', + 'table' => $this->getTable(), + 'field' => 'alert_id', + 'name' => __('Alert ID', 'siem-wazuh'), + 'datatype' => 'itemlink', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '2', + 'table' => 'glpi_plugin_siem_wazuh_servers', + 'field' => 'name', + 'name' => __('Wazuh Server', 'siem-wazuh'), + 'datatype' => 'dropdown', + 'massiveaction' => false, + 'linkfield' => 'wazuh_server_id' + ]; + + $tab[] = [ + 'id' => '3', + 'table' => $this->getTable(), + 'field' => 'rule_id', + 'name' => __('Rule ID', 'siem-wazuh'), + 'datatype' => 'integer', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '4', + 'table' => $this->getTable(), + 'field' => 'rule_level', + 'name' => __('Rule Level', 'siem-wazuh'), + 'datatype' => 'integer', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '5', + 'table' => $this->getTable(), + 'field' => 'rule_description', + 'name' => __('Rule Description', 'siem-wazuh'), + 'datatype' => 'text', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '6', + 'table' => $this->getTable(), + 'field' => 'agent_name', + 'name' => __('Agent Name', 'siem-wazuh'), + 'datatype' => 'string', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '7', + 'table' => $this->getTable(), + 'field' => 'agent_ip', + 'name' => __('Agent IP', 'siem-wazuh'), + 'datatype' => 'string', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '8', + 'table' => $this->getTable(), + 'field' => 'timestamp', + 'name' => __('Alert Timestamp', 'siem-wazuh'), + 'datatype' => 'datetime', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '9', + 'table' => $this->getTable(), + 'field' => 'status', + 'name' => __('Status'), + 'datatype' => 'specific', + 'searchtype' => 'equals', + 'massiveaction' => true + ]; + + $tab[] = [ + 'id' => '10', + 'table' => $this->getTable(), + 'field' => 'severity', + 'name' => __('Severity', 'siem-wazuh'), + 'datatype' => 'specific', + 'searchtype' => 'equals', + 'massiveaction' => true + ]; + + $tab[] = [ + 'id' => '11', + 'table' => 'glpi_computers', + 'field' => 'name', + 'name' => __('Associated Computer', 'siem-wazuh'), + 'datatype' => 'dropdown', + 'massiveaction' => false, + 'linkfield' => 'computer_id' + ]; + + $tab[] = [ + 'id' => '12', + 'table' => 'glpi_networkequipments', + 'field' => 'name', + 'name' => __('Associated Network Equipment', 'siem-wazuh'), + 'datatype' => 'dropdown', + 'massiveaction' => false, + 'linkfield' => 'networkequipment_id' + ]; + + $tab[] = [ + 'id' => '13', + 'table' => 'glpi_tickets', + 'field' => 'name', + 'name' => __('Associated Ticket', 'siem-wazuh'), + 'datatype' => 'dropdown', + 'massiveaction' => false, + 'linkfield' => 'ticket_id' + ]; + + $tab[] = [ + 'id' => '30', + 'table' => $this->getTable(), + 'field' => 'date_creation', + 'name' => __('Creation date'), + 'datatype' => 'datetime', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '31', + 'table' => $this->getTable(), + 'field' => 'date_mod', + 'name' => __('Last update'), + 'datatype' => 'datetime', + 'massiveaction' => false + ]; + + return $tab; + } + + /** + * Display specific values + */ + static function getSpecificValueToDisplay($field, $values, array $options = []) { + if (!is_array($values)) { + $values = [$field => $values]; + } + + switch ($field) { + case 'status': + $statuses = self::getStatusArray(); + return $statuses[$values[$field]] ?? $values[$field]; + + case 'severity': + $severities = self::getSeverityArray(); + return $severities[$values[$field]] ?? $values[$field]; + } + + return parent::getSpecificValueToDisplay($field, $values, $options); + } + + /** + * Get status array + */ + static function getStatusArray() { + return [ + 'new' => __('New', 'siem-wazuh'), + 'processed' => __('Processed', 'siem-wazuh'), + 'ignored' => __('Ignored', 'siem-wazuh'), + 'ticket_created' => __('Ticket Created', 'siem-wazuh') + ]; + } + + /** + * Get severity array + */ + static function getSeverityArray() { + return [ + 'low' => __('Low', 'siem-wazuh'), + 'medium' => __('Medium', 'siem-wazuh'), + 'high' => __('High', 'siem-wazuh'), + 'critical' => __('Critical', 'siem-wazuh') + ]; + } + + /** + * Display form + */ + function showForm($ID, $options = []) { + $this->initForm($ID, $options); + $this->showFormHeader($options); + + echo ""; + echo "".__('Alert ID', 'siem-wazuh').""; + echo "".$this->fields["alert_id"].""; + echo "".__('Rule ID', 'siem-wazuh').""; + echo "".$this->fields["rule_id"].""; + echo ""; + + echo ""; + echo "".__('Rule Level', 'siem-wazuh').""; + echo ""; + echo $this->fields["rule_level"]; + if ($this->fields["rule_level"]) { + $severity = self::getSeverityFromLevel($this->fields["rule_level"]); + $severities = self::getSeverityArray(); + echo " ".$severities[$severity].""; + } + echo ""; + echo "".__('Agent Name', 'siem-wazuh').""; + echo "".$this->fields["agent_name"].""; + echo ""; + + echo ""; + echo "".__('Agent IP', 'siem-wazuh').""; + echo "".$this->fields["agent_ip"].""; + echo "".__('Timestamp', 'siem-wazuh').""; + echo "".Html::convDateTime($this->fields["timestamp"]).""; + echo ""; + + echo ""; + echo "".__('Status').""; + echo ""; + $statuses = self::getStatusArray(); + Dropdown::showFromArray('status', $statuses, ['value' => $this->fields["status"]]); + echo ""; + echo "".__('Severity', 'siem-wazuh').""; + echo ""; + $severities = self::getSeverityArray(); + Dropdown::showFromArray('severity', $severities, ['value' => $this->fields["severity"]]); + echo ""; + echo ""; + + echo ""; + echo "".__('Rule Description', 'siem-wazuh').""; + echo ""; + echo nl2br(Html::entities_deep($this->fields["rule_description"])); + echo ""; + echo ""; + + // Affichage des associations + if ($this->fields["computer_id"]) { + $computer = new Computer(); + $computer->getFromDB($this->fields["computer_id"]); + echo ""; + echo "".__('Associated Computer').""; + echo ""; + echo $computer->getLink(); + echo ""; + echo ""; + echo ""; + } + + if ($this->fields["networkequipment_id"]) { + $netequip = new NetworkEquipment(); + $netequip->getFromDB($this->fields["networkequipment_id"]); + echo ""; + echo "".__('Associated Network Equipment').""; + echo ""; + echo $netequip->getLink(); + echo ""; + echo ""; + echo ""; + } + + if ($this->fields["ticket_id"]) { + $ticket = new Ticket(); + $ticket->getFromDB($this->fields["ticket_id"]); + echo ""; + echo "".__('Associated Ticket').""; + echo ""; + echo $ticket->getLink(); + echo ""; + echo ""; + echo ""; + } + + // Affichage des données brutes + if (!empty($this->fields["raw_data"])) { + echo ""; + echo ""; + echo "
"; + echo "".__('Raw Alert Data', 'siem-wazuh').""; + echo "
";
+            echo json_encode(json_decode($this->fields["raw_data"]), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
+            echo "
"; + echo "
"; + echo ""; + echo ""; + } + + $this->showFormButtons($options); + return true; + } + + /** + * Process alerts from Wazuh API + */ + function processAlerts($server_id, $alerts_data) { + $processed = 0; + $config = new PluginSiemWazuhConfig(); + + foreach ($alerts_data as $alert_data) { + $alert_id = $alert_data['id'] ?? $alert_data['_id'] ?? null; + if (!$alert_id) continue; + + // Vérifier si l'alerte existe déjà + if ($this->getFromDBByCrit([ + 'wazuh_server_id' => $server_id, + 'alert_id' => $alert_id + ])) { + continue; // Alerte déjà traitée + } + + // Extraire les informations de l'alerte + $rule_id = $alert_data['rule']['id'] ?? null; + $rule_level = $alert_data['rule']['level'] ?? 0; + $rule_description = $alert_data['rule']['description'] ?? ''; + $agent_name = $alert_data['agent']['name'] ?? ''; + $agent_ip = $alert_data['agent']['ip'] ?? ''; + $timestamp = $alert_data['timestamp'] ?? $alert_data['@timestamp'] ?? date('Y-m-d H:i:s'); + + // Convertir le timestamp au format MySQL + if (strpos($timestamp, 'T') !== false) { + $timestamp = str_replace('T', ' ', substr($timestamp, 0, 19)); + } + + // Déterminer la sévérité + $severity = self::getSeverityFromLevel($rule_level); + + // Créer l'alerte + $alert_input = [ + 'wazuh_server_id' => $server_id, + 'alert_id' => $alert_id, + 'rule_id' => $rule_id, + 'rule_level' => $rule_level, + 'rule_description' => $rule_description, + 'agent_id' => $alert_data['agent']['id'] ?? '', + 'agent_name' => $agent_name, + 'agent_ip' => $agent_ip, + 'timestamp' => $timestamp, + 'raw_data' => json_encode($alert_data), + 'severity' => $severity, + 'status' => 'new' + ]; + + // Tenter d'associer avec un asset + $this->associateWithAsset($alert_input, $agent_name, $agent_ip); + + if ($alert_id = $this->add($alert_input)) { + $processed++; + + // Créer automatiquement un ticket si configuré + if ($config->getConfiguration('auto_create_ticket', 1)) { + $this->createTicketFromAlert($alert_id); + } + } + } + + return $processed; + } + + /** + * Associate alert with GLPI asset + */ + private function associateWithAsset(&$alert_input, $agent_name, $agent_ip) { + global $DB; + + // Recherche par nom d'agent + if (!empty($agent_name)) { + // Recherche dans les ordinateurs + $computer = $DB->request([ + 'SELECT' => ['id'], + 'FROM' => 'glpi_computers', + 'WHERE' => [ + 'name' => $agent_name, + 'is_deleted' => 0 + ], + 'LIMIT' => 1 + ])->current(); + + if ($computer) { + $alert_input['computer_id'] = $computer['id']; + return; + } + + // Recherche dans les équipements réseau + $netequip = $DB->request([ + 'SELECT' => ['id'], + 'FROM' => 'glpi_networkequipments', + 'WHERE' => [ + 'name' => $agent_name, + 'is_deleted' => 0 + ], + 'LIMIT' => 1 + ])->current(); + + if ($netequip) { + $alert_input['networkequipment_id'] = $netequip['id']; + return; + } + } + + // Recherche par adresse IP + if (!empty($agent_ip)) { + // Recherche dans les ports réseau + $port = $DB->request([ + 'SELECT' => ['items_id', 'itemtype'], + 'FROM' => 'glpi_networkports', + 'INNER JOIN' => [ + 'glpi_ipaddresses' => [ + 'ON' => [ + 'glpi_networkports' => 'id', + 'glpi_ipaddresses' => 'items_id' + ] + ] + ], + 'WHERE' => [ + 'glpi_ipaddresses.name' => $agent_ip, + 'glpi_ipaddresses.itemtype' => 'NetworkPort' + ], + 'LIMIT' => 1 + ])->current(); + + if ($port) { + if ($port['itemtype'] == 'Computer') { + $alert_input['computer_id'] = $port['items_id']; + } elseif ($port['itemtype'] == 'NetworkEquipment') { + $alert_input['networkequipment_id'] = $port['items_id']; + } + } + } + } + + /** + * Create ticket from alert + */ + function createTicketFromAlert($alert_id) { + if (!$this->getFromDB($alert_id)) { + return false; + } + + $server = new PluginSiemWazuhServer(); + if (!$server->getFromDB($this->fields['wazuh_server_id'])) { + return false; + } + + $ticket = new Ticket(); + $config = new PluginSiemWazuhConfig(); + + // Déterminer la priorité basée sur le niveau de règle + $priority = self::getPriorityFromLevel($this->fields['rule_level']); + + $ticket_input = [ + 'name' => sprintf(__('Wazuh Alert: %s', 'siem-wazuh'), $this->fields['rule_description']), + 'content' => $this->generateTicketContent(), + 'type' => $server->fields['ticket_type'] ?: 1, + 'itilcategories_id' => $server->fields['ticket_category'], + 'priority' => $priority, + 'urgency' => $priority, + 'impact' => $priority, + 'status' => CommonITILObject::INCOMING, + '_add_validation' => 0 + ]; + + // Associer le ticket à l'asset si trouvé + if ($this->fields['computer_id']) { + $ticket_input['_link']['items_id'] = $this->fields['computer_id']; + $ticket_input['_link']['itemtype'] = 'Computer'; + } elseif ($this->fields['networkequipment_id']) { + $ticket_input['_link']['items_id'] = $this->fields['networkequipment_id']; + $ticket_input['_link']['itemtype'] = 'NetworkEquipment'; + } + + $ticket_id = $ticket->add($ticket_input); + + if ($ticket_id) { + // Mettre à jour l'alerte + $this->update([ + 'id' => $alert_id, + 'ticket_id' => $ticket_id, + 'status' => 'ticket_created' + ]); + + return $ticket_id; + } + + return false; + } + + /** + * Generate ticket content from alert + */ + private function generateTicketContent() { + $content = "[WAZUH-ALERT]\n\n"; + $content .= sprintf(__('Alert ID: %s', 'siem-wazuh'), $this->fields['alert_id']) . "\n"; + $content .= sprintf(__('Rule ID: %s', 'siem-wazuh'), $this->fields['rule_id']) . "\n"; + $content .= sprintf(__('Rule Level: %s', 'siem-wazuh'), $this->fields['rule_level']) . "\n"; + $content .= sprintf(__('Agent: %s (%s)', 'siem-wazuh'), $this->fields['agent_name'], $this->fields['agent_ip']) . "\n"; + $content .= sprintf(__('Timestamp: %s', 'siem-wazuh'), $this->fields['timestamp']) . "\n\n"; + $content .= sprintf(__('Description: %s', 'siem-wazuh'), $this->fields['rule_description']) . "\n\n"; + + if ($this->fields['raw_data']) { + $content .= __('Raw Alert Data:', 'siem-wazuh') . "\n"; + $content .= "```json\n"; + $content .= json_encode(json_decode($this->fields['raw_data']), JSON_PRETTY_PRINT); + $content .= "\n```"; + } + + return $content; + } + + /** + * Get severity from rule level + */ + static function getSeverityFromLevel($level) { + if ($level >= 12) return 'critical'; + if ($level >= 10) return 'high'; + if ($level >= 7) return 'medium'; + return 'low'; + } + + /** + * Get priority from rule level + */ + static function getPriorityFromLevel($level) { + if ($level >= 12) return 5; // Very high + if ($level >= 10) return 4; // High + if ($level >= 7) return 3; // Medium + if ($level >= 5) return 2; // Low + return 1; // Very low + } + + /** + * Show alerts for a server + */ + static function showForServer($server_id) { + global $DB; + + $iterator = $DB->request([ + 'FROM' => 'glpi_plugin_siem_wazuh_alerts', + 'WHERE' => ['wazuh_server_id' => $server_id], + 'ORDER' => 'timestamp DESC', + 'LIMIT' => 50 + ]); + + echo "
"; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + $statuses = self::getStatusArray(); + + foreach ($iterator as $row) { + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + } + + echo "
".__('Alert ID', 'siem-wazuh')."".__('Rule', 'siem-wazuh')."".__('Level', 'siem-wazuh')."".__('Agent', 'siem-wazuh')."".__('Timestamp', 'siem-wazuh')."".__('Status')."".__('Ticket')."
".$row['alert_id']."".$row['rule_id']." - ".Html::clean($row['rule_description'])."".$row['rule_level']."".$row['agent_name']." (".$row['agent_ip'].")".Html::convDateTime($row['timestamp'])."".$statuses[$row['status']].""; + if ($row['ticket_id']) { + $ticket = new Ticket(); + if ($ticket->getFromDB($row['ticket_id'])) { + echo $ticket->getLink(); + } + } + echo "
"; + echo "
"; + } + + /** + * Cron function to sync alerts + */ + static function cronSyncAlerts($task = null) { + global $DB; + + $config = new PluginSiemWazuhConfig(); + if (!$config->getConfiguration('sync_enabled', 1)) { + return 0; // Synchronisation désactivée + } + + $iterator = $DB->request([ + 'FROM' => 'glpi_plugin_siem_wazuh_servers', + 'WHERE' => ['is_active' => 1] + ]); + + $total_processed = 0; + + foreach ($iterator as $server_data) { + $server = new PluginSiemWazuhServer(); + $server->fields = $server_data; + + $result = $server->syncAlerts(); + if ($result['success']) { + $matches = []; + if (preg_match('/(\d+)/', $result['message'], $matches)) { + $total_processed += (int)$matches[1]; + } + } + } + + if ($task) { + $task->addVolume($total_processed); + return 1; + } + + return $total_processed; + } + + /** + * Cron function to cleanup old alerts + */ + static function cronCleanupOldAlerts($task = null) { + global $DB; + + $config = new PluginSiemWazuhConfig(); + $retention_days = $config->getConfiguration('alert_retention_days', 90); + + $query = "DELETE FROM glpi_plugin_siem_wazuh_alerts + WHERE date_creation < DATE_SUB(NOW(), INTERVAL $retention_days DAY)"; + + $result = $DB->query($query); + $deleted = $DB->affectedRows(); + + if ($task) { + $task->addVolume($deleted); + return 1; + } + + return $deleted; + } + + /** + * Get menu content + */ + static function getMenuContent() { + $menu = []; + if (Session::haveRight(static::$rightname, READ)) { + $menu['title'] = self::getMenuName(); + $menu['page'] = "/plugins/siem-wazuh/front/wazuhalert.php"; + $menu['links']['search'] = "/plugins/siem-wazuh/front/wazuhalert.php"; + } + return $menu; + } + + /** + * Get menu name + */ + static function getMenuName() { + return self::getTypeName(Session::getPluralNumber()); + } +} \ No newline at end of file diff --git a/front/wazuhconfig.php b/front/wazuhconfig.php new file mode 100644 index 0000000..20acd94 --- /dev/null +++ b/front/wazuhconfig.php @@ -0,0 +1,393 @@ +processConfigForm(); + +// Initialisation de l'affichage +Html::header( + PluginSiemWazuhConfig::getTypeName(1), + $_SERVER['PHP_SELF'], + 'tools', + 'PluginSiemWazuhConfig' +); + +echo "
"; + +// Affichage des onglets de configuration +$tabs = [ + 'config' => __('Configuration', 'siem-wazuh'), + 'mapping' => __('Asset Mapping', 'siem-wazuh'), + 'notifications' => __('Notifications', 'siem-wazuh'), + 'debug' => __('Debug & Logs', 'siem-wazuh') +]; + +$active_tab = $_GET['tab'] ?? 'config'; + +echo "
"; +echo ""; +echo "
"; + +echo "
"; + +switch ($active_tab) { + case 'config': + // Configuration générale + $config->showConfigForm(); + break; + + case 'mapping': + // Configuration du mapping des assets + echo "
"; + echo "

" . __('Asset Mapping Configuration', 'siem-wazuh') . "

"; + showMappingConfiguration($config); + echo "
"; + break; + + case 'notifications': + // Configuration des notifications + echo "
"; + echo "

" . __('Notification Configuration', 'siem-wazuh') . "

"; + showNotificationConfiguration($config); + echo "
"; + break; + + case 'debug': + // Configuration de debug et logs + echo "
"; + echo "

" . __('Debug & Logs Configuration', 'siem-wazuh') . "

"; + showDebugConfiguration($config); + showRecentLogs(); + echo "
"; + break; +} + +echo "
"; +echo "
"; + +/** + * Show mapping configuration + */ +function showMappingConfiguration($config) { + echo "
"; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + + echo "
" . __('Asset Detection Rules', 'siem-wazuh') . "
" . __('Match computers by hostname', 'siem-wazuh') . ""; + Dropdown::showYesNo("match_computers_hostname", $config->getConfiguration('match_computers_hostname', 1)); + echo "" . __('Match network equipment by hostname', 'siem-wazuh') . ""; + Dropdown::showYesNo("match_netequip_hostname", $config->getConfiguration('match_netequip_hostname', 1)); + echo "
" . __('Match by IP address', 'siem-wazuh') . ""; + Dropdown::showYesNo("match_by_ip", $config->getConfiguration('match_by_ip', 1)); + echo "" . __('Case sensitive matching', 'siem-wazuh') . ""; + Dropdown::showYesNo("case_sensitive_matching", $config->getConfiguration('case_sensitive_matching', 0)); + echo "
" . __('Hostname patterns to ignore', 'siem-wazuh') . ""; + echo ""; + echo "
" . __('One pattern per line. Use * as wildcard.', 'siem-wazuh') . ""; + echo "
"; + echo ""; + echo "
"; + echo Html::closeForm(); + + // Test de correspondance + echo "
"; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo "
" . __('Test Asset Mapping', 'siem-wazuh') . "
" . __('Agent name or IP', 'siem-wazuh') . ""; + echo ""; + echo " "; + echo "
"; + echo "
"; +} + +/** + * Show notification configuration + */ +function showNotificationConfiguration($config) { + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + + echo "
" . __('Email Notifications', 'siem-wazuh') . "
" . __('Enable email notifications', 'siem-wazuh') . ""; + Dropdown::showYesNo("email_notifications", $config->getConfiguration('email_notifications', 1)); + echo "" . __('Notification for critical alerts only', 'siem-wazuh') . ""; + Dropdown::showYesNo("notify_critical_only", $config->getConfiguration('notify_critical_only', 0)); + echo "
" . __('Default notification recipients', 'siem-wazuh') . ""; + echo ""; + echo "
" . __('Comma-separated email addresses', 'siem-wazuh') . ""; + echo "
"; + echo ""; + echo "
"; + echo Html::closeForm(); +} + +/** + * Show debug configuration + */ +function showDebugConfiguration($config) { + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + + echo "
" . __('Debug Configuration', 'siem-wazuh') . "
" . __('Enable debug mode', 'siem-wazuh') . ""; + Dropdown::showYesNo("debug_mode", $config->getConfiguration('debug_mode', 0)); + echo "" . __('Log API requests', 'siem-wazuh') . ""; + Dropdown::showYesNo("log_api_requests", $config->getConfiguration('log_api_requests', 0)); + echo "
" . __('Log level', 'siem-wazuh') . ""; + $log_levels = [ + 'error' => __('Error only', 'siem-wazuh'), + 'warning' => __('Warning and above', 'siem-wazuh'), + 'info' => __('Info and above', 'siem-wazuh'), + 'debug' => __('All messages', 'siem-wazuh') + ]; + Dropdown::showFromArray('log_level', $log_levels, [ + 'value' => $config->getConfiguration('log_level', 'info') + ]); + echo "" . __('Keep logs for (days)', 'siem-wazuh') . ""; + Dropdown::showNumber("log_retention_days", [ + 'value' => $config->getConfiguration('log_retention_days', 30), + 'min' => 1, + 'max' => 365 + ]); + echo "
"; + echo ""; + echo ""; + echo ""; + echo "
"; + echo Html::closeForm(); +} + +/** + * Show recent logs + */ +function showRecentLogs() { + global $DB; + + echo "
"; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + $iterator = $DB->request([ + 'SELECT' => [ + 'glpi_plugin_siem_wazuh_logs.*', + 'glpi_plugin_siem_wazuh_servers.name AS server_name' + ], + 'FROM' => 'glpi_plugin_siem_wazuh_logs', + 'LEFT JOIN' => [ + 'glpi_plugin_siem_wazuh_servers' => [ + 'ON' => [ + 'glpi_plugin_siem_wazuh_logs' => 'wazuh_server_id', + 'glpi_plugin_siem_wazuh_servers' => 'id' + ] + ] + ], + 'ORDER' => 'date_creation DESC', + 'LIMIT' => 100 + ]); + + if (count($iterator) == 0) { + echo ""; + echo ""; + echo ""; + } else { + foreach ($iterator as $log) { + $level_class = 'log-' . $log['level']; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + } + } + + echo "
" . __('Recent Logs', 'siem-wazuh') . " (100 dernières entrées)
" . __('Date') . "" . __('Level') . "" . __('Server') . "" . __('Message') . "
" . __('No logs found', 'siem-wazuh') . "
" . Html::convDateTime($log['date_creation']) . "" . ucfirst($log['level']) . "" . ($log['server_name'] ?: __('System', 'siem-wazuh')) . "" . Html::clean($log['message']) . "
"; +} + +// JavaScript pour les fonctionnalités interactives +echo ""; + +// CSS pour les logs et badges +echo ""; + +Html::footer(); \ No newline at end of file diff --git a/front/wazuhserver.form.php b/front/wazuhserver.form.php new file mode 100644 index 0000000..128cb3b --- /dev/null +++ b/front/wazuhserver.form.php @@ -0,0 +1,102 @@ +check(-1, CREATE, $_POST); + if ($newID = $server->add($_POST)) { + Event::log( + $newID, + "PluginSiemWazuhServer", + 4, + "inventory", + sprintf(__('%1$s adds the item %2$s'), $_SESSION["glpiname"], $_POST["name"]) + ); + if ($_SESSION['glpibackcreated']) { + Html::redirect($server->getLinkURL()); + } + } + Html::back(); + +} elseif (isset($_POST["delete"])) { + $server->check($_POST["id"], DELETE); + if ($server->delete($_POST)) { + Event::log( + $_POST["id"], + "PluginSiemWazuhServer", + 4, + "inventory", + sprintf(__('%s deletes an item'), $_SESSION["glpiname"]) + ); + } + $server->redirectToList(); + +} elseif (isset($_POST["restore"])) { + $server->check($_POST["id"], DELETE); + if ($server->restore($_POST)) { + Event::log( + $_POST["id"], + "PluginSiemWazuhServer", + 4, + "inventory", + sprintf(__('%s restores an item'), $_SESSION["glpiname"]) + ); + } + $server->redirectToList(); + +} elseif (isset($_POST["purge"])) { + $server->check($_POST["id"], PURGE); + if ($server->delete($_POST, 1)) { + Event::log( + $_POST["id"], + "PluginSiemWazuhServer", + 4, + "inventory", + sprintf(__('%s purges an item'), $_SESSION["glpiname"]) + ); + } + $server->redirectToList(); + +} elseif (isset($_POST["update"])) { + $server->check($_POST["id"], UPDATE); + if ($server->update($_POST)) { + Event::log( + $_POST["id"], + "PluginSiemWazuhServer", + 4, + "inventory", + sprintf(__('%s updates an item'), $_SESSION["glpiname"]) + ); + } + Html::back(); + +} else { + // Affichage du formulaire + $menus = [ + 'admin' => 'PluginSiemWazuhServer', + 'PluginSiemWazuhServer' => 'PluginSiemWazuhServer' + ]; + + PluginSiemWazuhServer::displayFullPageForItem( + $_GET["id"], + $menus, + [ + 'formoptions' => "method='post' enctype='multipart/form-data'" + ] + ); +} \ No newline at end of file diff --git a/front/wazuhserver.php b/front/wazuhserver.php new file mode 100644 index 0000000..6c450e8 --- /dev/null +++ b/front/wazuhserver.php @@ -0,0 +1,172 @@ +getFromDB($_GET['id'])) { + $result = $server->testConnection(); + echo json_encode($result); + exit; + } + break; + + case 'sync_alerts': + if (isset($_GET['id']) && $server->getFromDB($_GET['id'])) { + $result = $server->syncAlerts(); + echo json_encode($result); + exit; + } + break; + + case 'toggle_active': + if (isset($_GET['id']) && $server->getFromDB($_GET['id'])) { + if (Session::haveRight("plugin_siem_wazuh_server", UPDATE)) { + $new_status = $server->fields['is_active'] ? 0 : 1; + $server->update([ + 'id' => $_GET['id'], + 'is_active' => $new_status + ]); + Session::addMessageAfterRedirect( + $new_status ? __('Server activated', 'siem-wazuh') : __('Server deactivated', 'siem-wazuh') + ); + } + } + Html::back(); + break; + } +} + +// Initialisation de la recherche +$search = Search::show('PluginSiemWazuhServer'); + +// Ajout de CSS pour l'interface +echo ""; + +// Ajout de JavaScript pour les actions AJAX +echo ""; + +Html::footer(); \ No newline at end of file diff --git a/hook.php b/hook.php new file mode 100644 index 0000000..b908879 --- /dev/null +++ b/hook.php @@ -0,0 +1,287 @@ +queryOrDie($query, "Erreur lors de l'installation du plugin SIEM-Wazuh : " . $DB->error()); + } + } + + // Création des droits par défaut + plugin_siem_wazuh_create_default_rights(); + + // Création de la tâche cron + plugin_siem_wazuh_create_cron_tasks(); + + // Configuration par défaut + plugin_siem_wazuh_create_default_config(); + + return true; +} + +/** + * Plugin uninstall process + * + * @return boolean + */ +function plugin_siem_wazuh_uninstall() { + global $DB; + + // Lecture du fichier SQL de désinstallation + $sql_file = GLPI_ROOT . "/plugins/siem-wazuh/sql/uninstall.sql"; + + if (file_exists($sql_file)) { + $sql_content = file_get_contents($sql_file); + $queries = explode(';', $sql_content); + + foreach ($queries as $query) { + $query = trim($query); + if (!empty($query)) { + $DB->queryOrDie($query, "Erreur lors de la désinstallation du plugin SIEM-Wazuh : " . $DB->error()); + } + } + } + + // Suppression des tâches cron + plugin_siem_wazuh_remove_cron_tasks(); + + // Suppression des droits + plugin_siem_wazuh_remove_rights(); + + return true; +} + +/** + * Plugin update process + * + * @param string $current_version + * @return boolean + */ +function plugin_siem_wazuh_update($current_version) { + global $DB; + + // Mise à jour de la version 1.0.0 + if (version_compare($current_version, '1.0.0', '<')) { + // Ajout de nouvelles colonnes si nécessaire + $migration = new Migration(PLUGIN_SIEM_WAZUH_VERSION); + + // Exemple de migration + if (!$DB->fieldExists('glpi_plugin_siem_wazuh_servers', 'ticket_category')) { + $migration->addField('glpi_plugin_siem_wazuh_servers', 'ticket_category', 'int(11) DEFAULT NULL'); + } + + $migration->executeMigration(); + } + + return true; +} + +/** + * Create default rights + */ +function plugin_siem_wazuh_create_default_rights() { + global $DB; + + $rights = [ + 'plugin_siem_wazuh_config' => ['name' => __('SIEM Wazuh Configuration', 'siem-wazuh')], + 'plugin_siem_wazuh_server' => ['name' => __('Wazuh Servers', 'siem-wazuh')], + 'plugin_siem_wazuh_alert' => ['name' => __('Wazuh Alerts', 'siem-wazuh')] + ]; + + foreach ($rights as $right => $data) { + // Ajout du droit s'il n'existe pas + $query = "SELECT id FROM glpi_profilerights WHERE name = '$right'"; + $result = $DB->query($query); + + if ($DB->numrows($result) == 0) { + $query = "INSERT INTO glpi_profilerights (profiles_id, name, rights) + SELECT id, '$right', '0' FROM glpi_profiles"; + $DB->query($query); + + // Attribution des droits au profil Super-Admin + $query = "UPDATE glpi_profilerights SET rights = '" . (READ | UPDATE | CREATE | DELETE | PURGE) . "' + WHERE name = '$right' AND profiles_id IN ( + SELECT id FROM glpi_profiles WHERE name = 'Super-Admin' + )"; + $DB->query($query); + } + } +} + +/** + * Remove rights + */ +function plugin_siem_wazuh_remove_rights() { + global $DB; + + $rights = [ + 'plugin_siem_wazuh_config', + 'plugin_siem_wazuh_server', + 'plugin_siem_wazuh_alert' + ]; + + foreach ($rights as $right) { + $query = "DELETE FROM glpi_profilerights WHERE name = '$right'"; + $DB->query($query); + } +} + +/** + * Create cron tasks + */ +function plugin_siem_wazuh_create_cron_tasks() { + CronTask::Register('PluginSiemWazuhAlert', 'sync_alerts', 5 * MINUTE_TIMESTAMP, [ + 'comment' => __('Synchronize Wazuh alerts', 'siem-wazuh'), + 'mode' => CronTask::MODE_EXTERNAL + ]); + + CronTask::Register('PluginSiemWazuhAlert', 'cleanup_old_alerts', DAY_TIMESTAMP, [ + 'comment' => __('Cleanup old alerts', 'siem-wazuh'), + 'mode' => CronTask::MODE_EXTERNAL + ]); +} + +/** + * Remove cron tasks + */ +function plugin_siem_wazuh_remove_cron_tasks() { + global $DB; + + $query = "DELETE FROM glpi_crontasks WHERE itemtype LIKE 'PluginSiemWazuh%'"; + $DB->query($query); +} + +/** + * Create default configuration + */ +function plugin_siem_wazuh_create_default_config() { + $config = new PluginSiemWazuhConfig(); + + $default_configs = [ + 'auto_create_ticket' => '1', + 'default_ticket_priority' => '3', + 'alert_retention_days' => '90', + 'sync_enabled' => '1', + 'max_alerts_per_sync' => '100', + 'notification_enabled' => '1' + ]; + + foreach ($default_configs as $name => $value) { + $config->setConfiguration($name, $value); + } +} + +/** + * Get database relations + */ +function plugin_siem_wazuh_getDatabaseRelations() { + $plugin_relations = []; + + $plugin_relations["glpi_plugin_siem_wazuh_alerts"] = [ + "glpi_computers" => "computer_id", + "glpi_networkequipments" => "networkequipment_id", + "glpi_tickets" => "ticket_id" + ]; + + return $plugin_relations; +} + +/** + * Define dropdown relations + */ +function plugin_siem_wazuh_getDropdown() { + return [ + 'PluginSiemWazuhServer' => __('Wazuh Servers', 'siem-wazuh') + ]; +} + +/** + * Hook for profile changes + */ +function plugin_siem_wazuh_profile_form($prof) { + global $DB; + + if ($prof->getID() + && Session::haveRight("profile", READ)) { + + $prof_id = $prof->getID(); + + $query = "SELECT * FROM glpi_plugin_siem_wazuh_profiles WHERE profiles_id = '$prof_id'"; + $result = $DB->query($query); + + if ($DB->numrows($result)) { + $rights = $DB->fetchAssoc($result); + } else { + $rights = [ + 'wazuh_config' => '', + 'wazuh_server' => '', + 'wazuh_alert' => '' + ]; + } + + echo "
"; + echo ""; + echo ""; + echo ""; + echo ""; + + $right_names = [ + 'wazuh_config' => __('Configuration', 'siem-wazuh'), + 'wazuh_server' => __('Servers', 'siem-wazuh'), + 'wazuh_alert' => __('Alerts', 'siem-wazuh') + ]; + + foreach ($right_names as $field => $label) { + echo ""; + echo ""; + echo ""; + echo ""; + } + + echo "
" . __('SIEM Wazuh Rights', 'siem-wazuh') . "
$label"; + Profile::dropdownNoneReadWrite($field, $rights[$field], 1, 1, 1); + echo "
"; + echo "
"; + } +} + +/** + * Save profile rights + */ +function plugin_siem_wazuh_profile_save($prof) { + global $DB; + + $prof_id = $prof->getID(); + + if (isset($_POST['wazuh_config']) || isset($_POST['wazuh_server']) || isset($_POST['wazuh_alert'])) { + $query = "REPLACE INTO glpi_plugin_siem_wazuh_profiles + (profiles_id, wazuh_config, wazuh_server, wazuh_alert) + VALUES ('$prof_id', '" . $_POST['wazuh_config'] . "', + '" . $_POST['wazuh_server'] . "', '" . $_POST['wazuh_alert'] . "')"; + $DB->query($query); + } +} \ No newline at end of file diff --git a/inc/wazuhalert.class.php b/inc/wazuhalert.class.php new file mode 100644 index 0000000..ad80d13 --- /dev/null +++ b/inc/wazuhalert.class.php @@ -0,0 +1,706 @@ + 'common', + 'name' => __('Characteristics') + ]; + + $tab[] = [ + 'id' => '1', + 'table' => $this->getTable(), + 'field' => 'alert_id', + 'name' => __('Alert ID', 'siem-wazuh'), + 'datatype' => 'itemlink', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '2', + 'table' => 'glpi_plugin_siem_wazuh_servers', + 'field' => 'name', + 'name' => __('Wazuh Server', 'siem-wazuh'), + 'datatype' => 'dropdown', + 'massiveaction' => false, + 'linkfield' => 'wazuh_server_id' + ]; + + $tab[] = [ + 'id' => '3', + 'table' => $this->getTable(), + 'field' => 'rule_id', + 'name' => __('Rule ID', 'siem-wazuh'), + 'datatype' => 'integer', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '4', + 'table' => $this->getTable(), + 'field' => 'rule_level', + 'name' => __('Rule Level', 'siem-wazuh'), + 'datatype' => 'integer', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '5', + 'table' => $this->getTable(), + 'field' => 'rule_description', + 'name' => __('Rule Description', 'siem-wazuh'), + 'datatype' => 'text', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '6', + 'table' => $this->getTable(), + 'field' => 'agent_name', + 'name' => __('Agent Name', 'siem-wazuh'), + 'datatype' => 'string', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '7', + 'table' => $this->getTable(), + 'field' => 'agent_ip', + 'name' => __('Agent IP', 'siem-wazuh'), + 'datatype' => 'string', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '8', + 'table' => $this->getTable(), + 'field' => 'timestamp', + 'name' => __('Alert Timestamp', 'siem-wazuh'), + 'datatype' => 'datetime', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '9', + 'table' => $this->getTable(), + 'field' => 'status', + 'name' => __('Status'), + 'datatype' => 'specific', + 'searchtype' => 'equals', + 'massiveaction' => true + ]; + + $tab[] = [ + 'id' => '10', + 'table' => $this->getTable(), + 'field' => 'severity', + 'name' => __('Severity', 'siem-wazuh'), + 'datatype' => 'specific', + 'searchtype' => 'equals', + 'massiveaction' => true + ]; + + $tab[] = [ + 'id' => '11', + 'table' => 'glpi_computers', + 'field' => 'name', + 'name' => __('Associated Computer', 'siem-wazuh'), + 'datatype' => 'dropdown', + 'massiveaction' => false, + 'linkfield' => 'computer_id' + ]; + + $tab[] = [ + 'id' => '12', + 'table' => 'glpi_networkequipments', + 'field' => 'name', + 'name' => __('Associated Network Equipment', 'siem-wazuh'), + 'datatype' => 'dropdown', + 'massiveaction' => false, + 'linkfield' => 'networkequipment_id' + ]; + + $tab[] = [ + 'id' => '13', + 'table' => 'glpi_tickets', + 'field' => 'name', + 'name' => __('Associated Ticket', 'siem-wazuh'), + 'datatype' => 'dropdown', + 'massiveaction' => false, + 'linkfield' => 'ticket_id' + ]; + + $tab[] = [ + 'id' => '30', + 'table' => $this->getTable(), + 'field' => 'date_creation', + 'name' => __('Creation date'), + 'datatype' => 'datetime', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '31', + 'table' => $this->getTable(), + 'field' => 'date_mod', + 'name' => __('Last update'), + 'datatype' => 'datetime', + 'massiveaction' => false + ]; + + return $tab; + } + + /** + * Display specific values + */ + static function getSpecificValueToDisplay($field, $values, array $options = []) { + if (!is_array($values)) { + $values = [$field => $values]; + } + + switch ($field) { + case 'status': + $statuses = self::getStatusArray(); + return $statuses[$values[$field]] ?? $values[$field]; + + case 'severity': + $severities = self::getSeverityArray(); + return $severities[$values[$field]] ?? $values[$field]; + } + + return parent::getSpecificValueToDisplay($field, $values, $options); + } + + /** + * Get status array + */ + static function getStatusArray() { + return [ + 'new' => __('New', 'siem-wazuh'), + 'processed' => __('Processed', 'siem-wazuh'), + 'ignored' => __('Ignored', 'siem-wazuh'), + 'ticket_created' => __('Ticket Created', 'siem-wazuh') + ]; + } + + /** + * Get severity array + */ + static function getSeverityArray() { + return [ + 'low' => __('Low', 'siem-wazuh'), + 'medium' => __('Medium', 'siem-wazuh'), + 'high' => __('High', 'siem-wazuh'), + 'critical' => __('Critical', 'siem-wazuh') + ]; + } + + /** + * Display form + */ + function showForm($ID, $options = []) { + $this->initForm($ID, $options); + $this->showFormHeader($options); + + echo ""; + echo "".__('Alert ID', 'siem-wazuh').""; + echo "".$this->fields["alert_id"].""; + echo "".__('Rule ID', 'siem-wazuh').""; + echo "".$this->fields["rule_id"].""; + echo ""; + + echo ""; + echo "".__('Rule Level', 'siem-wazuh').""; + echo ""; + echo $this->fields["rule_level"]; + if ($this->fields["rule_level"]) { + $severity = self::getSeverityFromLevel($this->fields["rule_level"]); + $severities = self::getSeverityArray(); + echo " ".$severities[$severity].""; + } + echo ""; + echo "".__('Agent Name', 'siem-wazuh').""; + echo "".$this->fields["agent_name"].""; + echo ""; + + echo ""; + echo "".__('Agent IP', 'siem-wazuh').""; + echo "".$this->fields["agent_ip"].""; + echo "".__('Timestamp', 'siem-wazuh').""; + echo "".Html::convDateTime($this->fields["timestamp"]).""; + echo ""; + + echo ""; + echo "".__('Status').""; + echo ""; + $statuses = self::getStatusArray(); + Dropdown::showFromArray('status', $statuses, ['value' => $this->fields["status"]]); + echo ""; + echo "".__('Severity', 'siem-wazuh').""; + echo ""; + $severities = self::getSeverityArray(); + Dropdown::showFromArray('severity', $severities, ['value' => $this->fields["severity"]]); + echo ""; + echo ""; + + echo ""; + echo "".__('Rule Description', 'siem-wazuh').""; + echo ""; + echo nl2br(Html::entities_deep($this->fields["rule_description"])); + echo ""; + echo ""; + + // Affichage des associations + if ($this->fields["computer_id"]) { + $computer = new Computer(); + $computer->getFromDB($this->fields["computer_id"]); + echo ""; + echo "".__('Associated Computer').""; + echo ""; + echo $computer->getLink(); + echo ""; + echo ""; + echo ""; + } + + if ($this->fields["networkequipment_id"]) { + $netequip = new NetworkEquipment(); + $netequip->getFromDB($this->fields["networkequipment_id"]); + echo ""; + echo "".__('Associated Network Equipment').""; + echo ""; + echo $netequip->getLink(); + echo ""; + echo ""; + echo ""; + } + + if ($this->fields["ticket_id"]) { + $ticket = new Ticket(); + $ticket->getFromDB($this->fields["ticket_id"]); + echo ""; + echo "".__('Associated Ticket').""; + echo ""; + echo $ticket->getLink(); + echo ""; + echo ""; + echo ""; + } + + // Affichage des données brutes + if (!empty($this->fields["raw_data"])) { + echo ""; + echo ""; + echo "
"; + echo "".__('Raw Alert Data', 'siem-wazuh').""; + echo "
";
+            echo json_encode(json_decode($this->fields["raw_data"]), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
+            echo "
"; + echo "
"; + echo ""; + echo ""; + } + + $this->showFormButtons($options); + return true; + } + + /** + * Process alerts from Wazuh API + */ + function processAlerts($server_id, $alerts_data) { + $processed = 0; + $config = new PluginSiemWazuhConfig(); + + foreach ($alerts_data as $alert_data) { + $alert_id = $alert_data['id'] ?? $alert_data['_id'] ?? null; + if (!$alert_id) continue; + + // Vérifier si l'alerte existe déjà + if ($this->getFromDBByCrit([ + 'wazuh_server_id' => $server_id, + 'alert_id' => $alert_id + ])) { + continue; // Alerte déjà traitée + } + + // Extraire les informations de l'alerte + $rule_id = $alert_data['rule']['id'] ?? null; + $rule_level = $alert_data['rule']['level'] ?? 0; + $rule_description = $alert_data['rule']['description'] ?? ''; + $agent_name = $alert_data['agent']['name'] ?? ''; + $agent_ip = $alert_data['agent']['ip'] ?? ''; + $timestamp = $alert_data['timestamp'] ?? $alert_data['@timestamp'] ?? date('Y-m-d H:i:s'); + + // Convertir le timestamp au format MySQL + if (strpos($timestamp, 'T') !== false) { + $timestamp = str_replace('T', ' ', substr($timestamp, 0, 19)); + } + + // Déterminer la sévérité + $severity = self::getSeverityFromLevel($rule_level); + + // Créer l'alerte + $alert_input = [ + 'wazuh_server_id' => $server_id, + 'alert_id' => $alert_id, + 'rule_id' => $rule_id, + 'rule_level' => $rule_level, + 'rule_description' => $rule_description, + 'agent_id' => $alert_data['agent']['id'] ?? '', + 'agent_name' => $agent_name, + 'agent_ip' => $agent_ip, + 'timestamp' => $timestamp, + 'raw_data' => json_encode($alert_data), + 'severity' => $severity, + 'status' => 'new' + ]; + + // Tenter d'associer avec un asset + $this->associateWithAsset($alert_input, $agent_name, $agent_ip); + + if ($alert_id = $this->add($alert_input)) { + $processed++; + + // Créer automatiquement un ticket si configuré + if ($config->getConfiguration('auto_create_ticket', 1)) { + $this->createTicketFromAlert($alert_id); + } + } + } + + return $processed; + } + + /** + * Associate alert with GLPI asset + */ + private function associateWithAsset(&$alert_input, $agent_name, $agent_ip) { + global $DB; + + // Recherche par nom d'agent + if (!empty($agent_name)) { + // Recherche dans les ordinateurs + $computer = $DB->request([ + 'SELECT' => ['id'], + 'FROM' => 'glpi_computers', + 'WHERE' => [ + 'name' => $agent_name, + 'is_deleted' => 0 + ], + 'LIMIT' => 1 + ])->current(); + + if ($computer) { + $alert_input['computer_id'] = $computer['id']; + return; + } + + // Recherche dans les équipements réseau + $netequip = $DB->request([ + 'SELECT' => ['id'], + 'FROM' => 'glpi_networkequipments', + 'WHERE' => [ + 'name' => $agent_name, + 'is_deleted' => 0 + ], + 'LIMIT' => 1 + ])->current(); + + if ($netequip) { + $alert_input['networkequipment_id'] = $netequip['id']; + return; + } + } + + // Recherche par adresse IP + if (!empty($agent_ip)) { + // Recherche dans les ports réseau + $port = $DB->request([ + 'SELECT' => ['items_id', 'itemtype'], + 'FROM' => 'glpi_networkports', + 'INNER JOIN' => [ + 'glpi_ipaddresses' => [ + 'ON' => [ + 'glpi_networkports' => 'id', + 'glpi_ipaddresses' => 'items_id' + ] + ] + ], + 'WHERE' => [ + 'glpi_ipaddresses.name' => $agent_ip, + 'glpi_ipaddresses.itemtype' => 'NetworkPort' + ], + 'LIMIT' => 1 + ])->current(); + + if ($port) { + if ($port['itemtype'] == 'Computer') { + $alert_input['computer_id'] = $port['items_id']; + } elseif ($port['itemtype'] == 'NetworkEquipment') { + $alert_input['networkequipment_id'] = $port['items_id']; + } + } + } + } + + /** + * Create ticket from alert + */ + function createTicketFromAlert($alert_id) { + if (!$this->getFromDB($alert_id)) { + return false; + } + + $server = new PluginSiemWazuhServer(); + if (!$server->getFromDB($this->fields['wazuh_server_id'])) { + return false; + } + + $ticket = new Ticket(); + $config = new PluginSiemWazuhConfig(); + + // Déterminer la priorité basée sur le niveau de règle + $priority = self::getPriorityFromLevel($this->fields['rule_level']); + + $ticket_input = [ + 'name' => sprintf(__('Wazuh Alert: %s', 'siem-wazuh'), $this->fields['rule_description']), + 'content' => $this->generateTicketContent(), + 'type' => $server->fields['ticket_type'] ?: 1, + 'itilcategories_id' => $server->fields['ticket_category'], + 'priority' => $priority, + 'urgency' => $priority, + 'impact' => $priority, + 'status' => CommonITILObject::INCOMING, + '_add_validation' => 0 + ]; + + // Associer le ticket à l'asset si trouvé + if ($this->fields['computer_id']) { + $ticket_input['_link']['items_id'] = $this->fields['computer_id']; + $ticket_input['_link']['itemtype'] = 'Computer'; + } elseif ($this->fields['networkequipment_id']) { + $ticket_input['_link']['items_id'] = $this->fields['networkequipment_id']; + $ticket_input['_link']['itemtype'] = 'NetworkEquipment'; + } + + $ticket_id = $ticket->add($ticket_input); + + if ($ticket_id) { + // Mettre à jour l'alerte + $this->update([ + 'id' => $alert_id, + 'ticket_id' => $ticket_id, + 'status' => 'ticket_created' + ]); + + return $ticket_id; + } + + return false; + } + + /** + * Generate ticket content from alert + */ + private function generateTicketContent() { + $content = "[WAZUH-ALERT]\n\n"; + $content .= sprintf(__('Alert ID: %s', 'siem-wazuh'), $this->fields['alert_id']) . "\n"; + $content .= sprintf(__('Rule ID: %s', 'siem-wazuh'), $this->fields['rule_id']) . "\n"; + $content .= sprintf(__('Rule Level: %s', 'siem-wazuh'), $this->fields['rule_level']) . "\n"; + $content .= sprintf(__('Agent: %s (%s)', 'siem-wazuh'), $this->fields['agent_name'], $this->fields['agent_ip']) . "\n"; + $content .= sprintf(__('Timestamp: %s', 'siem-wazuh'), $this->fields['timestamp']) . "\n\n"; + $content .= sprintf(__('Description: %s', 'siem-wazuh'), $this->fields['rule_description']) . "\n\n"; + + if ($this->fields['raw_data']) { + $content .= __('Raw Alert Data:', 'siem-wazuh') . "\n"; + $content .= "```json\n"; + $content .= json_encode(json_decode($this->fields['raw_data']), JSON_PRETTY_PRINT); + $content .= "\n```"; + } + + return $content; + } + + /** + * Get severity from rule level + */ + static function getSeverityFromLevel($level) { + if ($level >= 12) return 'critical'; + if ($level >= 10) return 'high'; + if ($level >= 7) return 'medium'; + return 'low'; + } + + /** + * Get priority from rule level + */ + static function getPriorityFromLevel($level) { + if ($level >= 12) return 5; // Very high + if ($level >= 10) return 4; // High + if ($level >= 7) return 3; // Medium + if ($level >= 5) return 2; // Low + return 1; // Very low + } + + /** + * Show alerts for a server + */ + static function showForServer($server_id) { + global $DB; + + $iterator = $DB->request([ + 'FROM' => 'glpi_plugin_siem_wazuh_alerts', + 'WHERE' => ['wazuh_server_id' => $server_id], + 'ORDER' => 'timestamp DESC', + 'LIMIT' => 50 + ]); + + echo "
"; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + $statuses = self::getStatusArray(); + + foreach ($iterator as $row) { + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + } + + echo "
".__('Alert ID', 'siem-wazuh')."".__('Rule', 'siem-wazuh')."".__('Level', 'siem-wazuh')."".__('Agent', 'siem-wazuh')."".__('Timestamp', 'siem-wazuh')."".__('Status')."".__('Ticket')."
".$row['alert_id']."".$row['rule_id']." - ".Html::clean($row['rule_description'])."".$row['rule_level']."".$row['agent_name']." (".$row['agent_ip'].")".Html::convDateTime($row['timestamp'])."".$statuses[$row['status']].""; + if ($row['ticket_id']) { + $ticket = new Ticket(); + if ($ticket->getFromDB($row['ticket_id'])) { + echo $ticket->getLink(); + } + } + echo "
"; + echo "
"; + } + + /** + * Cron function to sync alerts + */ + static function cronSyncAlerts($task = null) { + global $DB; + + $config = new PluginSiemWazuhConfig(); + if (!$config->getConfiguration('sync_enabled', 1)) { + return 0; // Synchronisation désactivée + } + + $iterator = $DB->request([ + 'FROM' => 'glpi_plugin_siem_wazuh_servers', + 'WHERE' => ['is_active' => 1] + ]); + + $total_processed = 0; + + foreach ($iterator as $server_data) { + $server = new PluginSiemWazuhServer(); + $server->fields = $server_data; + + $result = $server->syncAlerts(); + if ($result['success']) { + $matches = []; + if (preg_match('/(\d+)/', $result['message'], $matches)) { + $total_processed += (int)$matches[1]; + } + } + } + + if ($task) { + $task->addVolume($total_processed); + return 1; + } + + return $total_processed; + } + + /** + * Cron function to cleanup old alerts + */ + static function cronCleanupOldAlerts($task = null) { + global $DB; + + $config = new PluginSiemWazuhConfig(); + $retention_days = $config->getConfiguration('alert_retention_days', 90); + + $query = "DELETE FROM glpi_plugin_siem_wazuh_alerts + WHERE date_creation < DATE_SUB(NOW(), INTERVAL $retention_days DAY)"; + + $result = $DB->query($query); + $deleted = $DB->affectedRows(); + + if ($task) { + $task->addVolume($deleted); + return 1; + } + + return $deleted; + } + + /** + * Get menu content + */ + static function getMenuContent() { + $menu = []; + if (Session::haveRight(static::$rightname, READ)) { + $menu['title'] = self::getMenuName(); + $menu['page'] = "/plugins/siem-wazuh/front/wazuhalert.php"; + $menu['links']['search'] = "/plugins/siem-wazuh/front/wazuhalert.php"; + } + return $menu; + } + + /** + * Get menu name + */ + static function getMenuName() { + return self::getTypeName(Session::getPluralNumber()); + } +} \ No newline at end of file diff --git a/inc/wazuhapi.class.php b/inc/wazuhapi.class.php new file mode 100644 index 0000000..72bd6d2 --- /dev/null +++ b/inc/wazuhapi.class.php @@ -0,0 +1,515 @@ +server = $server; + $this->config = new PluginSiemWazuhConfig(); + } + + /** + * Test connection to Wazuh server + */ + public function testConnection() { + try { + $result = $this->authenticate(); + if ($result['success']) { + // Test basic API call + $info = $this->makeRequest('GET', '/'); + if ($info['success']) { + return [ + 'success' => true, + 'message' => __('Connection successful', 'siem-wazuh'), + 'data' => $info['data'] + ]; + } else { + return [ + 'success' => false, + 'message' => __('Connection failed:', 'siem-wazuh') . ' ' . $info['message'] + ]; + } + } else { + return [ + 'success' => false, + 'message' => __('Authentication failed:', 'siem-wazuh') . ' ' . $result['message'] + ]; + } + } catch (Exception $e) { + return [ + 'success' => false, + 'message' => __('Connection error:', 'siem-wazuh') . ' ' . $e->getMessage() + ]; + } + } + + /** + * Authenticate with Wazuh API + */ + private function authenticate() { + if (!empty($this->token)) { + return ['success' => true, 'token' => $this->token]; + } + + $url = rtrim($this->server->fields['wazuh_url'], '/') . ':' . $this->server->fields['wazuh_port'] . '/security/user/authenticate'; + + $credentials = base64_encode($this->server->fields['wazuh_login'] . ':' . $this->server->getPassword()); + + $headers = [ + 'Authorization: Basic ' . $credentials, + 'Content-Type: application/json' + ]; + + try { + $response = $this->httpRequest('POST', $url, null, $headers); + + if ($response['success'] && isset($response['data']['data']['token'])) { + $this->token = $response['data']['data']['token']; + return ['success' => true, 'token' => $this->token]; + } else { + return [ + 'success' => false, + 'message' => $response['message'] ?? __('Authentication failed', 'siem-wazuh') + ]; + } + } catch (Exception $e) { + return [ + 'success' => false, + 'message' => $e->getMessage() + ]; + } + } + + /** + * Get alerts from Wazuh + */ + public function getAlerts($params = []) { + $auth_result = $this->authenticate(); + if (!$auth_result['success']) { + return $auth_result; + } + + $default_params = [ + 'offset' => 0, + 'limit' => $this->config->getConfiguration('max_alerts_per_sync', 100), + 'sort' => '-timestamp', + 'rule.level' => '>=' . $this->config->getConfiguration('min_rule_level', 5) + ]; + + // Merge with provided parameters + $params = array_merge($default_params, $params); + + // Construire l'URL avec les paramètres + $query_string = http_build_query($params); + $endpoint = '/alerts?' . $query_string; + + return $this->makeRequest('GET', $endpoint); + } + + /** + * Get alert by ID + */ + public function getAlert($alert_id) { + $auth_result = $this->authenticate(); + if (!$auth_result['success']) { + return $auth_result; + } + + return $this->makeRequest('GET', '/alerts/' . $alert_id); + } + + /** + * Get agents + */ + public function getAgents($params = []) { + $auth_result = $this->authenticate(); + if (!$auth_result['success']) { + return $auth_result; + } + + $default_params = [ + 'offset' => 0, + 'limit' => 500, + 'sort' => '+name' + ]; + + $params = array_merge($default_params, $params); + $query_string = http_build_query($params); + $endpoint = '/agents?' . $query_string; + + return $this->makeRequest('GET', $endpoint); + } + + /** + * Get agent by ID + */ + public function getAgent($agent_id) { + $auth_result = $this->authenticate(); + if (!$auth_result['success']) { + return $auth_result; + } + + return $this->makeRequest('GET', '/agents/' . $agent_id); + } + + /** + * Get rules + */ + public function getRules($params = []) { + $auth_result = $this->authenticate(); + if (!$auth_result['success']) { + return $auth_result; + } + + $default_params = [ + 'offset' => 0, + 'limit' => 500, + 'sort' => '+id' + ]; + + $params = array_merge($default_params, $params); + $query_string = http_build_query($params); + $endpoint = '/rules?' . $query_string; + + return $this->makeRequest('GET', $endpoint); + } + + /** + * Get decoders + */ + public function getDecoders($params = []) { + $auth_result = $this->authenticate(); + if (!$auth_result['success']) { + return $auth_result; + } + + $default_params = [ + 'offset' => 0, + 'limit' => 500 + ]; + + $params = array_merge($default_params, $params); + $query_string = http_build_query($params); + $endpoint = '/decoders?' . $query_string; + + return $this->makeRequest('GET', $endpoint); + } + + /** + * Get cluster status + */ + public function getClusterStatus() { + $auth_result = $this->authenticate(); + if (!$auth_result['success']) { + return $auth_result; + } + + return $this->makeRequest('GET', '/cluster/status'); + } + + /** + * Get manager information + */ + public function getManagerInfo() { + $auth_result = $this->authenticate(); + if (!$auth_result['success']) { + return $auth_result; + } + + return $this->makeRequest('GET', '/manager/info'); + } + + /** + * Make API request + */ + private function makeRequest($method, $endpoint, $data = null, $extra_headers = []) { + if (!$this->token) { + $auth_result = $this->authenticate(); + if (!$auth_result['success']) { + return $auth_result; + } + } + + $url = rtrim($this->server->fields['wazuh_url'], '/') . ':' . $this->server->fields['wazuh_port'] . $endpoint; + + $headers = array_merge([ + 'Authorization: Bearer ' . $this->token, + 'Content-Type: application/json' + ], $extra_headers); + + return $this->httpRequest($method, $url, $data, $headers); + } + + /** + * Make HTTP request with retry logic + */ + private function httpRequest($method, $url, $data = null, $headers = []) { + $retries = 0; + $last_error = null; + + while ($retries < self::MAX_RETRIES) { + try { + $ch = curl_init(); + + curl_setopt_array($ch, [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => self::TIMEOUT, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_SSL_VERIFYHOST => false, + CURLOPT_CUSTOMREQUEST => $method, + CURLOPT_HTTPHEADER => $headers, + CURLOPT_USERAGENT => 'GLPI-SIEM-Wazuh-Plugin/1.0' + ]); + + if ($data !== null) { + curl_setopt($ch, CURLOPT_POSTFIELDS, is_array($data) ? json_encode($data) : $data); + } + + // Logging for debug mode + if ($this->config->getConfiguration('debug_mode', 0)) { + $this->logDebug("HTTP Request: $method $url", [ + 'headers' => $headers, + 'data' => $data, + 'retry' => $retries + ]); + } + + $response = curl_exec($ch); + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $curl_error = curl_error($ch); + + curl_close($ch); + + if ($response === false) { + throw new Exception("cURL Error: $curl_error"); + } + + $decoded_response = json_decode($response, true); + + if ($this->config->getConfiguration('debug_mode', 0)) { + $this->logDebug("HTTP Response: $http_code", [ + 'response' => $decoded_response, + 'raw_response' => substr($response, 0, 1000) + ]); + } + + if ($http_code >= 200 && $http_code < 300) { + return [ + 'success' => true, + 'data' => $decoded_response, + 'http_code' => $http_code + ]; + } elseif ($http_code == 401) { + // Token expired, clear it and retry authentication + $this->token = null; + if ($retries < self::MAX_RETRIES - 1) { + $retries++; + continue; + } + } + + $error_message = "HTTP $http_code"; + if ($decoded_response && isset($decoded_response['detail'])) { + $error_message .= ": " . $decoded_response['detail']; + } elseif ($decoded_response && isset($decoded_response['message'])) { + $error_message .= ": " . $decoded_response['message']; + } + + throw new Exception($error_message); + + } catch (Exception $e) { + $last_error = $e; + $retries++; + + if ($retries < self::MAX_RETRIES) { + sleep(pow(2, $retries)); // Exponential backoff + continue; + } + + $this->logError("HTTP Request failed after $retries retries", [ + 'url' => $url, + 'method' => $method, + 'error' => $e->getMessage() + ]); + + break; + } + } + + return [ + 'success' => false, + 'message' => $last_error ? $last_error->getMessage() : __('Request failed after maximum retries', 'siem-wazuh') + ]; + } + + /** + * Query Wazuh Indexer (OpenSearch/Elasticsearch) + */ + public function queryIndexer($index, $query = [], $params = []) { + if (empty($this->server->fields['indexer_url'])) { + return [ + 'success' => false, + 'message' => __('Indexer not configured', 'siem-wazuh') + ]; + } + + $url = rtrim($this->server->fields['indexer_url'], '/') . ':' . $this->server->fields['indexer_port'] . '/' . $index . '/_search'; + + $headers = [ + 'Content-Type: application/json' + ]; + + if (!empty($this->server->fields['indexer_login'])) { + $credentials = base64_encode($this->server->fields['indexer_login'] . ':' . $this->server->getPassword('indexer_password')); + $headers[] = 'Authorization: Basic ' . $credentials; + } + + $default_query = [ + 'size' => $this->config->getConfiguration('max_alerts_per_sync', 100), + 'sort' => [ + ['timestamp' => ['order' => 'desc']] + ] + ]; + + $final_query = array_merge($default_query, $query); + + return $this->httpRequest('POST', $url, $final_query, $headers); + } + + /** + * Get recent alerts from indexer + */ + public function getRecentAlertsFromIndexer($minutes = 60) { + $query = [ + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'range' => [ + 'timestamp' => [ + 'gte' => 'now-' . $minutes . 'm', + 'lt' => 'now' + ] + ] + ] + ] + ] + ] + ]; + + $min_level = $this->config->getConfiguration('min_rule_level', 5); + if ($min_level > 0) { + $query['query']['bool']['must'][] = [ + 'range' => [ + 'rule.level' => [ + 'gte' => $min_level + ] + ] + ]; + } + + return $this->queryIndexer('wazuh-alerts-*', $query); + } + + /** + * Log debug message + */ + private function logDebug($message, $context = []) { + if ($this->config->getConfiguration('debug_mode', 0)) { + $this->log('debug', $message, $context); + } + } + + /** + * Log error message + */ + private function logError($message, $context = []) { + $this->log('error', $message, $context); + } + + /** + * Log message + */ + private function log($level, $message, $context = []) { + global $DB; + + $log_levels = ['debug', 'info', 'warning', 'error', 'critical']; + $current_log_level = $this->config->getConfiguration('log_level', 'info'); + $current_level_index = array_search($current_log_level, $log_levels); + $message_level_index = array_search($level, $log_levels); + + // Only log if message level is equal or higher than configured level + if ($message_level_index >= $current_level_index) { + try { + $DB->insert('glpi_plugin_siem_wazuh_logs', [ + 'wazuh_server_id' => $this->server->getID(), + 'level' => $level, + 'message' => $message, + 'context' => json_encode($context), + 'date_creation' => $_SESSION['glpi_currenttime'] + ]); + } catch (Exception $e) { + // Ignore logging errors to prevent recursion + error_log("SIEM-Wazuh Plugin: Failed to log message - " . $e->getMessage()); + } + } + } + + /** + * Get API statistics + */ + public function getStatistics() { + global $DB; + + $stats = []; + + // Requests count by server + $iterator = $DB->request([ + 'SELECT' => ['COUNT' => '* as count'], + 'FROM' => 'glpi_plugin_siem_wazuh_logs', + 'WHERE' => [ + 'wazuh_server_id' => $this->server->getID(), + 'date_creation' => ['>=', date('Y-m-d 00:00:00')] + ] + ]); + + $stats['requests_today'] = $iterator->current()['count']; + + // Error rate + $iterator = $DB->request([ + 'SELECT' => ['COUNT' => '* as count'], + 'FROM' => 'glpi_plugin_siem_wazuh_logs', + 'WHERE' => [ + 'wazuh_server_id' => $this->server->getID(), + 'level' => ['IN', ['error', 'critical']], + 'date_creation' => ['>=', date('Y-m-d 00:00:00')] + ] + ]); + + $stats['errors_today'] = $iterator->current()['count']; + $stats['error_rate'] = $stats['requests_today'] > 0 ? + round(($stats['errors_today'] / $stats['requests_today']) * 100, 2) : 0; + + return $stats; + } +} \ No newline at end of file diff --git a/inc/wazuhconfig.class.php b/inc/wazuhconfig.class.php new file mode 100644 index 0000000..da0d186 --- /dev/null +++ b/inc/wazuhconfig.class.php @@ -0,0 +1,423 @@ +request([ + 'FROM' => $this->getTable(), + 'WHERE' => [ + 'name' => $name, + 'context' => $context + ], + 'LIMIT' => 1 + ]); + + if (count($iterator) > 0) { + $row = $iterator->current(); + return $row['value']; + } + + return $default; + } + + /** + * Set configuration value + */ + function setConfiguration($name, $value, $context = 'global') { + global $DB; + + // Vérifier si la configuration existe + $iterator = $DB->request([ + 'FROM' => $this->getTable(), + 'WHERE' => [ + 'name' => $name, + 'context' => $context + ], + 'LIMIT' => 1 + ]); + + if (count($iterator) > 0) { + // Mettre à jour + $row = $iterator->current(); + return $this->update([ + 'id' => $row['id'], + 'value' => $value + ]); + } else { + // Créer + return $this->add([ + 'name' => $name, + 'value' => $value, + 'context' => $context + ]); + } + } + + /** + * Display configuration form + */ + function showConfigForm() { + global $CFG_GLPI; + + echo ""; + echo "
"; + echo ""; + + echo ""; + + // Configuration générale + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + // Configuration des tickets par défaut + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + if ($this->getConfiguration('auto_assign_tickets', 0)) { + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + } + + // Configuration de mapping + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + // Configuration de debug + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + + echo "
" . __('General Configuration', 'siem-wazuh') . "
" . __('Enable synchronization', 'siem-wazuh') . ""; + Dropdown::showYesNo("sync_enabled", $this->getConfiguration('sync_enabled', 1)); + echo "" . __('Auto-create tickets', 'siem-wazuh') . ""; + Dropdown::showYesNo("auto_create_ticket", $this->getConfiguration('auto_create_ticket', 1)); + echo "
" . __('Maximum alerts per sync', 'siem-wazuh') . ""; + Dropdown::showNumber("max_alerts_per_sync", [ + 'value' => $this->getConfiguration('max_alerts_per_sync', 100), + 'min' => 10, + 'max' => 1000, + 'step' => 10 + ]); + echo "" . __('Alert retention (days)', 'siem-wazuh') . ""; + Dropdown::showNumber("alert_retention_days", [ + 'value' => $this->getConfiguration('alert_retention_days', 90), + 'min' => 7, + 'max' => 365, + 'step' => 7 + ]); + echo "
" . __('Minimum rule level', 'siem-wazuh') . ""; + Dropdown::showNumber("min_rule_level", [ + 'value' => $this->getConfiguration('min_rule_level', 5), + 'min' => 0, + 'max' => 15 + ]); + echo "" . __('Enable notifications', 'siem-wazuh') . ""; + Dropdown::showYesNo("notification_enabled", $this->getConfiguration('notification_enabled', 1)); + echo "
" . __('Default Ticket Settings', 'siem-wazuh') . "
" . __('Default Priority', 'siem-wazuh') . ""; + Ticket::dropdownPriority('default_ticket_priority', $this->getConfiguration('default_ticket_priority', 3)); + echo "" . __('Default Urgency', 'siem-wazuh') . ""; + Ticket::dropdownUrgency('default_ticket_urgency', $this->getConfiguration('default_ticket_urgency', 3)); + echo "
" . __('Default Impact', 'siem-wazuh') . ""; + Ticket::dropdownImpact('default_ticket_impact', $this->getConfiguration('default_ticket_impact', 3)); + echo "" . __('Auto-assign tickets', 'siem-wazuh') . ""; + Dropdown::showYesNo("auto_assign_tickets", $this->getConfiguration('auto_assign_tickets', 0)); + echo "
" . __('Default Assignee', 'siem-wazuh') . ""; + User::dropdown([ + 'name' => 'default_assignee', + 'value' => $this->getConfiguration('default_assignee', 0), + 'right' => 'own_ticket' + ]); + echo "" . __('Default Group', 'siem-wazuh') . ""; + Group::dropdown([ + 'name' => 'default_group', + 'value' => $this->getConfiguration('default_group', 0) + ]); + echo "
" . __('Asset Mapping Configuration', 'siem-wazuh') . "
" . __('Match by hostname', 'siem-wazuh') . ""; + Dropdown::showYesNo("match_by_hostname", $this->getConfiguration('match_by_hostname', 1)); + echo "" . __('Match by IP address', 'siem-wazuh') . ""; + Dropdown::showYesNo("match_by_ip", $this->getConfiguration('match_by_ip', 1)); + echo "
" . __('Case sensitive matching', 'siem-wazuh') . ""; + Dropdown::showYesNo("case_sensitive_matching", $this->getConfiguration('case_sensitive_matching', 0)); + echo "" . __('Create unknown assets', 'siem-wazuh') . ""; + Dropdown::showYesNo("create_unknown_assets", $this->getConfiguration('create_unknown_assets', 0)); + echo "
" . __('Debug Configuration', 'siem-wazuh') . "
" . __('Enable debug mode', 'siem-wazuh') . ""; + Dropdown::showYesNo("debug_mode", $this->getConfiguration('debug_mode', 0)); + echo "" . __('Log level', 'siem-wazuh') . ""; + $log_levels = [ + 'error' => __('Error only', 'siem-wazuh'), + 'warning' => __('Warning and above', 'siem-wazuh'), + 'info' => __('Info and above', 'siem-wazuh'), + 'debug' => __('All messages', 'siem-wazuh') + ]; + Dropdown::showFromArray('log_level', $log_levels, [ + 'value' => $this->getConfiguration('log_level', 'info') + ]); + echo "
" . __('Keep debug logs (days)', 'siem-wazuh') . ""; + Dropdown::showNumber("debug_retention_days", [ + 'value' => $this->getConfiguration('debug_retention_days', 30), + 'min' => 1, + 'max' => 90 + ]); + echo "
"; + echo ""; + echo "
"; + echo "
"; + Html::closeForm(); + + // Affichage des statistiques + $this->showStatistics(); + } + + /** + * Process configuration form + */ + function processConfigForm() { + if (isset($_POST['update_config'])) { + $config_fields = [ + 'sync_enabled', 'auto_create_ticket', 'max_alerts_per_sync', 'alert_retention_days', + 'min_rule_level', 'notification_enabled', 'default_ticket_priority', + 'default_ticket_urgency', 'default_ticket_impact', 'auto_assign_tickets', + 'default_assignee', 'default_group', 'match_by_hostname', 'match_by_ip', + 'case_sensitive_matching', 'create_unknown_assets', 'debug_mode', + 'log_level', 'debug_retention_days' + ]; + + foreach ($config_fields as $field) { + if (isset($_POST[$field])) { + $this->setConfiguration($field, $_POST[$field]); + } + } + + Session::addMessageAfterRedirect(__('Configuration updated successfully', 'siem-wazuh')); + Html::back(); + } + } + + /** + * Show statistics + */ + function showStatistics() { + global $DB; + + echo "
"; + echo ""; + echo ""; + + // Statistiques des serveurs + $servers_count = countElementsInTable('glpi_plugin_siem_wazuh_servers'); + $active_servers = countElementsInTable('glpi_plugin_siem_wazuh_servers', ['is_active' => 1]); + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + // Statistiques des alertes + $total_alerts = countElementsInTable('glpi_plugin_siem_wazuh_alerts'); + $new_alerts = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['status' => 'new']); + $processed_alerts = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['status' => 'processed']); + $tickets_created = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['status' => 'ticket_created']); + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + // Alertes par sévérité + $severities = PluginSiemWazuhAlert::getSeverityArray(); + foreach ($severities as $severity => $label) { + $count = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['severity' => $severity]); + echo ""; + echo ""; + echo ""; + if (next($severities) !== false) { + $next_severity = key($severities); + $next_label = current($severities); + $next_count = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['severity' => $next_severity]); + echo ""; + echo ""; + next($severities); + } else { + echo ""; + } + echo ""; + } + + // Dernière synchronisation + $last_sync = $DB->request([ + 'SELECT' => ['MAX' => 'last_sync AS last_sync'], + 'FROM' => 'glpi_plugin_siem_wazuh_servers', + 'WHERE' => ['is_active' => 1] + ])->current(); + + echo ""; + echo ""; + echo ""; + echo ""; + + echo "
" . __('Statistics', 'siem-wazuh') . "
" . __('Total servers', 'siem-wazuh') . "$servers_count" . __('Active servers', 'siem-wazuh') . "$active_servers
" . __('Total alerts', 'siem-wazuh') . "$total_alerts" . __('New alerts', 'siem-wazuh') . "$new_alerts
" . __('Processed alerts', 'siem-wazuh') . "$processed_alerts" . __('Tickets created', 'siem-wazuh') . "$tickets_created
" . sprintf(__('%s alerts', 'siem-wazuh'), $label) . "$count" . sprintf(__('%s alerts', 'siem-wazuh'), $next_label) . "$next_count
" . __('Last global sync', 'siem-wazuh') . ""; + echo $last_sync['last_sync'] ? Html::convDateTime($last_sync['last_sync']) : __('Never'); + echo "
"; + echo "
"; + } + + /** + * Get menu content + */ + static function getMenuContent() { + $menu = []; + if (Session::haveRight(static::$rightname, READ)) { + $menu['title'] = self::getMenuName(); + $menu['page'] = "/plugins/siem-wazuh/front/wazuhconfig.php"; + $menu['links']['config'] = "/plugins/siem-wazuh/front/wazuhconfig.php"; + } + return $menu; + } + + /** + * Get menu name + */ + static function getMenuName() { + return self::getTypeName(1); + } + + /** + * Get default configurations + */ + static function getDefaultConfigurations() { + return [ + 'sync_enabled' => 1, + 'auto_create_ticket' => 1, + 'max_alerts_per_sync' => 100, + 'alert_retention_days' => 90, + 'min_rule_level' => 5, + 'notification_enabled' => 1, + 'default_ticket_priority' => 3, + 'default_ticket_urgency' => 3, + 'default_ticket_impact' => 3, + 'auto_assign_tickets' => 0, + 'default_assignee' => 0, + 'default_group' => 0, + 'match_by_hostname' => 1, + 'match_by_ip' => 1, + 'case_sensitive_matching' => 0, + 'create_unknown_assets' => 0, + 'debug_mode' => 0, + 'log_level' => 'info', + 'debug_retention_days' => 30 + ]; + } + + /** + * Install default configuration + */ + static function installDefaultConfiguration() { + $config = new self(); + $defaults = self::getDefaultConfigurations(); + + foreach ($defaults as $name => $value) { + $config->setConfiguration($name, $value); + } + } +} \ No newline at end of file diff --git a/inc/wazuhserver.class.php b/inc/wazuhserver.class.php new file mode 100644 index 0000000..4652dc8 --- /dev/null +++ b/inc/wazuhserver.class.php @@ -0,0 +1,411 @@ +getType()) { + case 'PluginSiemWazuhServer': + if ($_SESSION['glpishow_count_on_tabs']) { + $nb = countElementsInTable('glpi_plugin_siem_wazuh_alerts', + "`wazuh_server_id` = '".$item->getID()."'"); + } + return self::createTabEntry(__('Alerts', 'siem-wazuh'), $nb); + } + } + return ''; + } + + /** + * Display tab content + */ + static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { + if ($item->getType() == 'PluginSiemWazuhServer') { + PluginSiemWazuhAlert::showForServer($item->getID()); + } + return true; + } + + /** + * Define search options + */ + function rawSearchOptions() { + $tab = []; + + $tab[] = [ + 'id' => 'common', + 'name' => __('Characteristics') + ]; + + $tab[] = [ + 'id' => '1', + 'table' => $this->getTable(), + 'field' => 'name', + 'name' => __('Name'), + 'datatype' => 'itemlink', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '2', + 'table' => $this->getTable(), + 'field' => 'wazuh_url', + 'name' => __('Wazuh URL', 'siem-wazuh'), + 'datatype' => 'string', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '3', + 'table' => $this->getTable(), + 'field' => 'wazuh_port', + 'name' => __('Wazuh Port', 'siem-wazuh'), + 'datatype' => 'integer', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '4', + 'table' => $this->getTable(), + 'field' => 'is_active', + 'name' => __('Active'), + 'datatype' => 'bool', + 'massiveaction' => true + ]; + + $tab[] = [ + 'id' => '5', + 'table' => $this->getTable(), + 'field' => 'sync_interval', + 'name' => __('Sync Interval (seconds)', 'siem-wazuh'), + 'datatype' => 'integer', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '6', + 'table' => $this->getTable(), + 'field' => 'last_sync', + 'name' => __('Last Sync', 'siem-wazuh'), + 'datatype' => 'datetime', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '7', + 'table' => 'glpi_itilcategories', + 'field' => 'name', + 'name' => __('Default Ticket Category', 'siem-wazuh'), + 'datatype' => 'dropdown', + 'massiveaction' => false, + 'linkfield' => 'ticket_category' + ]; + + $tab[] = [ + 'id' => '30', + 'table' => $this->getTable(), + 'field' => 'date_creation', + 'name' => __('Creation date'), + 'datatype' => 'datetime', + 'massiveaction' => false + ]; + + $tab[] = [ + 'id' => '31', + 'table' => $this->getTable(), + 'field' => 'date_mod', + 'name' => __('Last update'), + 'datatype' => 'datetime', + 'massiveaction' => false + ]; + + return $tab; + } + + /** + * Display form + */ + function showForm($ID, $options = []) { + $this->initForm($ID, $options); + $this->showFormHeader($options); + + echo ""; + echo "".__('Name').""; + echo ""; + Html::autocompletionTextField($this, "name", ['size' => 50]); + echo ""; + echo "".__('Active').""; + echo ""; + Dropdown::showYesNo("is_active", $this->fields["is_active"]); + echo ""; + echo ""; + + echo ""; + echo "".__('Wazuh Server URL', 'siem-wazuh').""; + echo ""; + Html::autocompletionTextField($this, "wazuh_url", ['size' => 50, 'placeholder' => 'https://wazuh-server.domain.com']); + echo ""; + echo "".__('Wazuh API Port', 'siem-wazuh').""; + echo ""; + Html::autocompletionTextField($this, "wazuh_port", ['size' => 10, 'value' => ($this->fields["wazuh_port"] ?: 55000)]); + echo ""; + echo ""; + + echo ""; + echo "".__('Wazuh API Login', 'siem-wazuh').""; + echo ""; + Html::autocompletionTextField($this, "wazuh_login", ['size' => 30]); + echo ""; + echo "".__('Wazuh API Password', 'siem-wazuh').""; + echo ""; + echo ""; + if (!empty($this->fields["wazuh_password"])) { + echo "
".__('Leave empty to keep current password', 'siem-wazuh').""; + } + echo ""; + echo ""; + + echo ""; + echo "".__('Indexer Server URL', 'siem-wazuh').""; + echo ""; + Html::autocompletionTextField($this, "indexer_url", ['size' => 50, 'placeholder' => 'https://wazuh-indexer.domain.com']); + echo ""; + echo "".__('Indexer API Port', 'siem-wazuh').""; + echo ""; + Html::autocompletionTextField($this, "indexer_port", ['size' => 10, 'value' => ($this->fields["indexer_port"] ?: 9200)]); + echo ""; + echo ""; + + echo ""; + echo "".__('Indexer API Login', 'siem-wazuh').""; + echo ""; + Html::autocompletionTextField($this, "indexer_login", ['size' => 30]); + echo ""; + echo "".__('Indexer API Password', 'siem-wazuh').""; + echo ""; + echo ""; + if (!empty($this->fields["indexer_password"])) { + echo "
".__('Leave empty to keep current password', 'siem-wazuh').""; + } + echo ""; + echo ""; + + echo ""; + echo "".__('Sync Interval (seconds)', 'siem-wazuh').""; + echo ""; + Html::autocompletionTextField($this, "sync_interval", ['size' => 10, 'value' => ($this->fields["sync_interval"] ?: 300)]); + echo ""; + echo "".__('Default Ticket Type', 'siem-wazuh').""; + echo ""; + Ticket::dropdownType('ticket_type', $this->fields["ticket_type"]); + echo ""; + echo ""; + + echo ""; + echo "".__('Default Ticket Category', 'siem-wazuh').""; + echo ""; + ITILCategory::dropdown(['name' => 'ticket_category', 'value' => $this->fields["ticket_category"]]); + echo ""; + echo "".__('Last Sync', 'siem-wazuh').""; + echo ""; + echo ($this->fields["last_sync"] ? Html::convDateTime($this->fields["last_sync"]) : __('Never')); + echo ""; + echo ""; + + // Bouton de test de connexion + if ($ID > 0) { + echo ""; + echo ""; + echo ""; + echo "
"; + echo ""; + echo ""; + } + + $this->showFormButtons($options); + return true; + } + + /** + * Prepare input for add + */ + function prepareInputForAdd($input) { + if (!empty($input['wazuh_password'])) { + $input['wazuh_password'] = Toolbox::encrypt($input['wazuh_password'], GLPIKEY); + } + if (!empty($input['indexer_password'])) { + $input['indexer_password'] = Toolbox::encrypt($input['indexer_password'], GLPIKEY); + } + return $input; + } + + /** + * Prepare input for update + */ + function prepareInputForUpdate($input) { + if (!empty($input['wazuh_password'])) { + $input['wazuh_password'] = Toolbox::encrypt($input['wazuh_password'], GLPIKEY); + } else { + unset($input['wazuh_password']); + } + + if (!empty($input['indexer_password'])) { + $input['indexer_password'] = Toolbox::encrypt($input['indexer_password'], GLPIKEY); + } else { + unset($input['indexer_password']); + } + + return $input; + } + + /** + * Get decrypted password + */ + function getPassword($field = 'wazuh_password') { + if (!empty($this->fields[$field])) { + return Toolbox::decrypt($this->fields[$field], GLPIKEY); + } + return ''; + } + + /** + * Test connection to Wazuh server + */ + function testConnection() { + if (empty($this->fields['wazuh_url']) || empty($this->fields['wazuh_login'])) { + return ['success' => false, 'message' => __('Missing connection parameters', 'siem-wazuh')]; + } + + $api = new PluginSiemWazuhAPI($this); + return $api->testConnection(); + } + + /** + * Synchronize alerts from this server + */ + function syncAlerts() { + if (!$this->fields['is_active']) { + return ['success' => false, 'message' => __('Server is not active', 'siem-wazuh')]; + } + + $api = new PluginSiemWazuhAPI($this); + $result = $api->getAlerts(); + + if ($result['success']) { + $alert = new PluginSiemWazuhAlert(); + $processed = $alert->processAlerts($this->getID(), $result['data']); + + // Mise à jour de la dernière synchronisation + $this->update([ + 'id' => $this->getID(), + 'last_sync' => $_SESSION['glpi_currenttime'] + ]); + + return [ + 'success' => true, + 'message' => sprintf(__('%d alerts processed', 'siem-wazuh'), $processed) + ]; + } + + return $result; + } + + /** + * Get servers for dropdown + */ + static function getDropdownValues($post) { + global $DB; + + $where = ['is_active' => 1]; + if (isset($post['searchText']) && !empty($post['searchText'])) { + $where[] = ['name' => ['LIKE', '%' . $post['searchText'] . '%']]; + } + + $iterator = $DB->request([ + 'FROM' => 'glpi_plugin_siem_wazuh_servers', + 'WHERE' => $where, + 'ORDER' => 'name' + ]); + + $values = []; + foreach ($iterator as $row) { + $values[] = [ + 'id' => $row['id'], + 'text' => $row['name'] + ]; + } + + return $values; + } + + /** + * Get menu content + */ + static function getMenuContent() { + $menu = []; + if (Session::haveRight(static::$rightname, READ)) { + $menu['title'] = self::getMenuName(); + $menu['page'] = "/plugins/siem-wazuh/front/wazuhserver.php"; + $menu['links']['search'] = "/plugins/siem-wazuh/front/wazuhserver.php"; + if (Session::haveRight(static::$rightname, CREATE)) { + $menu['links']['add'] = "/plugins/siem-wazuh/front/wazuhserver.form.php"; + } + } + return $menu; + } + + /** + * Get menu name + */ + static function getMenuName() { + return self::getTypeName(Session::getPluralNumber()); + } + + /** + * Clean database relations + */ + function cleanDBonPurge() { + $this->deleteChildrenAndRelationsFromDb( + [ + PluginSiemWazuhAlert::class + ] + ); + } + + /** + * Get used configuration + */ + static function getUsedConfig() { + return ['plugin_siem_wazuh_server']; + } +} \ No newline at end of file diff --git a/inc/wazuhtab.class.php b/inc/wazuhtab.class.php new file mode 100644 index 0000000..9231ff5 --- /dev/null +++ b/inc/wazuhtab.class.php @@ -0,0 +1,454 @@ +getType(), ['Computer', 'NetworkEquipment', 'Peripheral', 'Phone', 'Printer'])) { + $nb = 0; + if ($_SESSION['glpishow_count_on_tabs']) { + $nb = self::countForItem($item); + } + return self::createTabEntry(__('Wazuh Alerts', 'siem-wazuh'), $nb); + } + } + return ''; + } + + /** + * Display tab content + */ + static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { + if (in_array($item->getType(), ['Computer', 'NetworkEquipment', 'Peripheral', 'Phone', 'Printer'])) { + self::showForItem($item); + } + return true; + } + + /** + * Count alerts for item + */ + static function countForItem(CommonGLPI $item) { + global $DB; + + $itemtype = $item->getType(); + $items_id = $item->getID(); + + $field_map = [ + 'Computer' => 'computer_id', + 'NetworkEquipment' => 'networkequipment_id', + 'Peripheral' => 'peripheral_id', + 'Phone' => 'phone_id', + 'Printer' => 'printer_id' + ]; + + if (!isset($field_map[$itemtype])) { + return 0; + } + + return countElementsInTable('glpi_plugin_siem_wazuh_alerts', [ + $field_map[$itemtype] => $items_id + ]); + } + + /** + * Show alerts for item + */ + static function showForItem(CommonGLPI $item) { + global $DB, $CFG_GLPI; + + $itemtype = $item->getType(); + $items_id = $item->getID(); + + $field_map = [ + 'Computer' => 'computer_id', + 'NetworkEquipment' => 'networkequipment_id', + 'Peripheral' => 'peripheral_id', + 'Phone' => 'phone_id', + 'Printer' => 'printer_id' + ]; + + if (!isset($field_map[$itemtype])) { + return; + } + + $field = $field_map[$itemtype]; + $canupdate = Session::haveRight(static::$rightname, UPDATE); + + // Récupération des alertes + $iterator = $DB->request([ + 'SELECT' => [ + 'glpi_plugin_siem_wazuh_alerts.*', + 'glpi_plugin_siem_wazuh_servers.name AS server_name' + ], + 'FROM' => 'glpi_plugin_siem_wazuh_alerts', + 'LEFT JOIN' => [ + 'glpi_plugin_siem_wazuh_servers' => [ + 'ON' => [ + 'glpi_plugin_siem_wazuh_alerts' => 'wazuh_server_id', + 'glpi_plugin_siem_wazuh_servers' => 'id' + ] + ] + ], + 'WHERE' => [$field => $items_id], + 'ORDER' => ['timestamp DESC', 'rule_level DESC'], + 'LIMIT' => 100 + ]); + + $alerts = iterator_to_array($iterator); + + echo "
"; + + // Résumé des alertes + self::showAlertsSummary($alerts); + + if (count($alerts) > 0) { + // Filtres + self::showAlertsFilters($item, $field); + + // Tableau des alertes + self::showAlertsTable($alerts, $canupdate); + + // Actions de masse + if ($canupdate && count($alerts) > 0) { + self::showMassActions($item, $field); + } + } else { + echo "
"; + echo "

" . __('No Wazuh alerts found for this item', 'siem-wazuh') . "

"; + echo "
"; + } + + echo "
"; + } + + /** + * Show alerts summary + */ + static function showAlertsSummary($alerts) { + $stats = [ + 'total' => count($alerts), + 'new' => 0, + 'processed' => 0, + 'ticket_created' => 0, + 'ignored' => 0, + 'critical' => 0, + 'high' => 0, + 'medium' => 0, + 'low' => 0 + ]; + + foreach ($alerts as $alert) { + $stats[$alert['status']]++; + $stats[$alert['severity']]++; + } + + echo "
"; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo "
" . __('Alerts Summary', 'siem-wazuh') . "
" . $stats['total'] . "
" . __('Total', 'siem-wazuh') . "
" . $stats['new'] . "
" . __('New', 'siem-wazuh') . "
" . $stats['processed'] . "
" . __('Processed', 'siem-wazuh') . "
" . $stats['ticket_created'] . "
" . __('Ticket Created', 'siem-wazuh') . "
" . $stats['critical'] . "
" . __('Critical', 'siem-wazuh') . "
" . $stats['high'] . "
" . __('High', 'siem-wazuh') . "
" . $stats['medium'] . "
" . __('Medium', 'siem-wazuh') . "
" . $stats['low'] . "
" . __('Low', 'siem-wazuh') . "
"; + echo "
"; + echo "
"; + } + + /** + * Show alerts filters + */ + static function showAlertsFilters($item, $field) { + echo "
"; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + + echo "
" . __('Filters', 'siem-wazuh') . "
" . __('Status') . ""; + $statuses = ['all' => __('All')] + PluginSiemWazuhAlert::getStatusArray(); + Dropdown::showFromArray('filter_status', $statuses, [ + 'value' => $_GET['filter_status'] ?? 'all' + ]); + echo "" . __('Severity', 'siem-wazuh') . ""; + $severities = ['all' => __('All')] + PluginSiemWazuhAlert::getSeverityArray(); + Dropdown::showFromArray('filter_severity', $severities, [ + 'value' => $_GET['filter_severity'] ?? 'all' + ]); + echo "
" . __('Date from', 'siem-wazuh') . ""; + Html::showDateField('filter_date_from', [ + 'value' => $_GET['filter_date_from'] ?? '' + ]); + echo "" . __('Date to', 'siem-wazuh') . ""; + Html::showDateField('filter_date_to', [ + 'value' => $_GET['filter_date_to'] ?? '' + ]); + echo "
"; + echo ""; + echo " "; + echo ""; + echo "
"; + echo ""; + echo "
"; + echo "
"; + } + + /** + * Show alerts table + */ + static function showAlertsTable($alerts, $canupdate) { + echo "
"; + echo "
"; + echo ""; + + $header = ""; + if ($canupdate) { + $header .= ""; + } + $header .= ""; + $header .= ""; + $header .= ""; + $header .= ""; + $header .= ""; + $header .= ""; + $header .= ""; + $header .= ""; + $header .= ""; + $header .= ""; + + echo $header; + + if (count($alerts) == 0) { + echo ""; + echo ""; + echo ""; + } + + $statuses = PluginSiemWazuhAlert::getStatusArray(); + $severities = PluginSiemWazuhAlert::getSeverityArray(); + + foreach ($alerts as $alert) { + echo ""; + + if ($canupdate) { + echo ""; + } + + echo ""; + + echo ""; + + echo ""; + + echo ""; + + echo ""; + + echo ""; + + echo ""; + + echo ""; + + echo ""; + + echo ""; + } + + echo "
"; + $header .= ""; + $header .= "" . __('Alert ID', 'siem-wazuh') . "" . __('Server', 'siem-wazuh') . "" . __('Rule', 'siem-wazuh') . "" . __('Level', 'siem-wazuh') . "" . __('Agent', 'siem-wazuh') . "" . __('Timestamp', 'siem-wazuh') . "" . __('Status') . "" . __('Severity', 'siem-wazuh') . "" . __('Ticket') . "
"; + echo __('No alerts found', 'siem-wazuh'); + echo "
"; + echo ""; + echo ""; + echo ""; + echo $alert['alert_id']; + echo ""; + echo "" . $alert['server_name'] . ""; + echo "" . $alert['rule_id'] . "
"; + echo "" . Html::clean($alert['rule_description']) . ""; + echo "
"; + echo ""; + echo $alert['rule_level']; + echo ""; + echo ""; + echo $alert['agent_name'] . "
"; + echo "" . $alert['agent_ip'] . ""; + echo "
" . Html::convDateTime($alert['timestamp']) . ""; + echo ""; + echo $statuses[$alert['status']]; + echo ""; + echo ""; + echo ""; + echo $severities[$alert['severity']]; + echo ""; + echo ""; + if ($alert['ticket_id']) { + $ticket = new Ticket(); + if ($ticket->getFromDB($alert['ticket_id'])) { + echo $ticket->getLink(); + } + } else { + echo "-"; + } + echo "
"; + + if ($canupdate && count($alerts) > 0) { + echo "
"; + echo ""; + echo " "; + echo ""; + echo " "; + echo ""; + echo "
"; + } + + echo Html::closeForm(); + echo "
"; + } + + /** + * Show mass actions + */ + static function showMassActions($item, $field) { + echo "
"; + echo "
"; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + + echo "
" . __('Mass Actions', 'siem-wazuh') . "
" . __('Sync new alerts', 'siem-wazuh') . ""; + echo ""; + echo "
" . __('Export alerts', 'siem-wazuh') . ""; + echo ""; + echo __('Export CSV', 'siem-wazuh'); + echo ""; + echo "
"; + echo "
"; + } + + /** + * Get search options for item + */ + static function getSearchOptionsForItem($itemtype) { + $tab = []; + + $tab[5150]['table'] = 'glpi_plugin_siem_wazuh_alerts'; + $tab[5150]['field'] = 'id'; + $tab[5150]['name'] = __('Wazuh Alerts', 'siem-wazuh'); + $tab[5150]['forcegroupby'] = true; + $tab[5150]['usehaving'] = true; + $tab[5150]['datatype'] = 'count'; + $tab[5150]['massiveaction'] = false; + + $field_map = [ + 'Computer' => 'computer_id', + 'NetworkEquipment' => 'networkequipment_id', + 'Peripheral' => 'peripheral_id', + 'Phone' => 'phone_id', + 'Printer' => 'printer_id' + ]; + + if (isset($field_map[$itemtype])) { + $tab[5150]['linkfield'] = $field_map[$itemtype]; + } + + return $tab; + } +} + +// JavaScript pour les fonctionnalités interactives +echo ""; \ No newline at end of file diff --git a/js/config.js b/js/config.js new file mode 100644 index 0000000..e69de29 diff --git a/js/wazuh.js b/js/wazuh.js new file mode 100644 index 0000000..c6170d9 --- /dev/null +++ b/js/wazuh.js @@ -0,0 +1,487 @@ +/** + * SIEM-Wazuh Plugin for GLPI - Main JavaScript + * Handles all interactive features for the Wazuh plugin + */ + +// Plugin namespace +var WazuhPlugin = WazuhPlugin || {}; + +/** + * Initialize the plugin + */ +WazuhPlugin.init = function() { + // Initialize tooltips + this.initTooltips(); + + // Initialize auto-refresh + this.initAutoRefresh(); + + // Initialize form validation + this.initFormValidation(); + + // Initialize keyboard shortcuts + this.initKeyboardShortcuts(); + + console.log('SIEM-Wazuh Plugin initialized'); +}; + +/** + * Test connection to Wazuh server + */ +WazuhPlugin.testConnection = function(serverId) { + const button = document.querySelector('.test-connection-' + serverId); + const resultDiv = document.getElementById('test-result-' + serverId); + + if (!button || !resultDiv) { + console.error('Test connection elements not found'); + return; + } + + // Show loading state + button.disabled = true; + button.innerHTML = '' + LANG.TESTING; + + const formData = new FormData(); + formData.append('server_id', serverId); + formData.append('_glpi_csrf_token', getAjaxCsrfToken()); + + fetch(CFG_GLPI.root_doc + '/plugins/siem-wazuh/ajax/test_connection.php', { + method: 'POST', + body: formData + }) + .then(response => { + if (!response.ok) { + throw new Error('HTTP ' + response.status); + } + return response.json(); + }) + .then(data => { + let alertClass = data.success ? 'alert-success' : 'alert-danger'; + let message = data.message; + + if (data.success && data.server_info) { + message += '
Server Info:
'; + message += 'Version: ' + (data.server_info.api_version || 'Unknown') + '
'; + message += 'Timestamp: ' + data.timestamp + '
'; + } + + resultDiv.innerHTML = '
' + message + '
'; + + // Auto-hide after 10 seconds for success messages + if (data.success) { + setTimeout(() => { + resultDiv.innerHTML = ''; + }, 10000); + } + }) + .catch(error => { + console.error('Connection test error:', error); + resultDiv.innerHTML = '
' + LANG.CONNECTION_TEST_FAILED + ': ' + error.message + '
'; + }) + .finally(() => { + button.disabled = false; + button.innerHTML = LANG.TEST_CONNECTION; + }); +}; + +/** + * Synchronize alerts from Wazuh server + */ +WazuhPlugin.syncAlerts = function(serverId, showConfirm = true) { + if (showConfirm && !confirm(LANG.CONFIRM_SYNC_ALERTS)) { + return; + } + + const button = document.querySelector('.sync-alerts-' + serverId); + if (button) { + button.disabled = true; + button.innerHTML = '' + LANG.SYNCHRONIZING; + } + + const formData = new FormData(); + formData.append('server_id', serverId); + formData.append('action', 'sync'); + formData.append('_glpi_csrf_token', getAjaxCsrfToken()); + + fetch(CFG_GLPI.root_doc + '/plugins/siem-wazuh/ajax/sync_alerts.php', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + this.showNotification(data.message, 'success'); + + // Update statistics if available + if (data.stats) { + this.updateStatistics(data.stats); + } + + // Refresh the page after 2 seconds + setTimeout(() => { + location.reload(); + }, 2000); + } else { + this.showNotification(LANG.SYNC_FAILED + ' ' + data.message, 'error'); + } + }) + .catch(error => { + console.error('Sync error:', error); + this.showNotification(LANG.SYNC_ERROR, 'error'); + }) + .finally(() => { + if (button) { + button.disabled = false; + button.innerHTML = LANG.SYNC_ALERTS; + } + }); +}; + +/** + * Sync alerts for specific item + */ +WazuhPlugin.syncAlertsForItem = function(itemId, itemType) { + if (!confirm(LANG.CONFIRM_SYNC_ITEM)) { + return; + } + + const formData = new FormData(); + formData.append('item_id', itemId); + formData.append('item_type', itemType); + formData.append('action', 'sync_for_item'); + formData.append('_glpi_csrf_token', getAjaxCsrfToken()); + + fetch(CFG_GLPI.root_doc + '/plugins/siem-wazuh/ajax/sync_alerts.php', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + this.showNotification(data.message, 'success'); + setTimeout(() => location.reload(), 2000); + } else { + this.showNotification(LANG.SYNC_FAILED + ' ' + data.message, 'error'); + } + }) + .catch(error => { + console.error('Item sync error:', error); + this.showNotification(LANG.SYNC_ERROR, 'error'); + }); +}; + +/** + * Toggle server active status + */ +WazuhPlugin.toggleServerStatus = function(serverId) { + if (confirm(LANG.CONFIRM_TOGGLE_STATUS)) { + window.location.href = '?action=toggle_active&id=' + serverId; + } +}; + +/** + * Check/uncheck all alerts + */ +WazuhPlugin.checkAllAlerts = function(checkbox) { + const checkboxes = document.querySelectorAll('input[name="selected_alerts[]"]'); + for (let i = 0; i < checkboxes.length; i++) { + checkboxes[i].checked = checkbox.checked; + } + this.updateBulkActionButtons(); +}; + +/** + * Update bulk action buttons based on selection + */ +WazuhPlugin.updateBulkActionButtons = function() { + const checkboxes = document.querySelectorAll('input[name="selected_alerts[]"]:checked'); + const bulkActions = document.querySelectorAll('.bulk-action-btn'); + + bulkActions.forEach(button => { + button.disabled = checkboxes.length === 0; + }); + + // Update counter if exists + const counter = document.getElementById('selected-count'); + if (counter) { + counter.textContent = checkboxes.length; + } +}; + +/** + * Filter alerts table + */ +WazuhPlugin.filterAlerts = function(filters) { + const table = document.querySelector('.alert-list table'); + if (!table) return; + + const rows = table.querySelectorAll('tbody tr'); + + rows.forEach(row => { + let visible = true; + + // Apply filters + Object.keys(filters).forEach(filterKey => { + const filterValue = filters[filterKey]; + if (filterValue && filterValue !== 'all') { + const cell = row.querySelector('[data-' + filterKey + ']'); + if (cell && cell.getAttribute('data-' + filterKey) !== filterValue) { + visible = false; + } + } + }); + + row.style.display = visible ? '' : 'none'; + }); + + this.updateFilterStats(); +}; + +/** + * Update filter statistics + */ +WazuhPlugin.updateFilterStats = function() { + const visibleRows = document.querySelectorAll('.alert-list tbody tr:not([style*="none"])'); + const totalRows = document.querySelectorAll('.alert-list tbody tr'); + + const statsElement = document.getElementById('filter-stats'); + if (statsElement) { + statsElement.textContent = `${visibleRows.length} of ${totalRows.length} alerts shown`; + } +}; + +/** + * Export alerts to CSV + */ +WazuhPlugin.exportAlerts = function(params = {}) { + const url = new URL(CFG_GLPI.root_doc + '/plugins/siem-wazuh/front/wazuhalert.php'); + url.searchParams.append('export', '1'); + + Object.keys(params).forEach(key => { + url.searchParams.append(key, params[key]); + }); + + window.open(url.toString(), '_blank'); +}; + +/** + * Show notification message + */ +WazuhPlugin.showNotification = function(message, type = 'info', duration = 5000) { + // Try to use GLPI's notification system first + if (typeof displayAjaxMessageAfterRedirect === 'function') { + displayAjaxMessageAfterRedirect(); + return; + } + + // Fallback to custom notification + const notification = document.createElement('div'); + notification.className = `alert alert-${type === 'error' ? 'danger' : type} wazuh-notification`; + notification.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + z-index: 9999; + max-width: 400px; + animation: slideIn 0.3s ease-out; + `; + notification.innerHTML = ` + + ${message} + `; + + document.body.appendChild(notification); + + // Auto-remove after duration + if (duration > 0) { + setTimeout(() => { + if (notification.parentNode) { + notification.remove(); + } + }, duration); + } +}; + +/** + * Update statistics display + */ +WazuhPlugin.updateStatistics = function(stats) { + Object.keys(stats).forEach(key => { + const element = document.getElementById('stat-' + key); + if (element) { + element.textContent = stats[key]; + } + }); +}; + +/** + * Initialize tooltips + */ +WazuhPlugin.initTooltips = function() { + // Simple tooltip implementation + const tooltipElements = document.querySelectorAll('[data-tooltip]'); + + tooltipElements.forEach(element => { + element.addEventListener('mouseenter', function() { + const tooltip = document.createElement('div'); + tooltip.className = 'wazuh-tooltip-popup'; + tooltip.textContent = this.getAttribute('data-tooltip'); + tooltip.style.cssText = ` + position: absolute; + background: #333; + color: white; + padding: 5px 10px; + border-radius: 4px; + font-size: 12px; + z-index: 1000; + pointer-events: none; + `; + + document.body.appendChild(tooltip); + + // Position tooltip + const rect = this.getBoundingClientRect(); + tooltip.style.left = (rect.left + rect.width / 2 - tooltip.offsetWidth / 2) + 'px'; + tooltip.style.top = (rect.top - tooltip.offsetHeight - 5) + 'px'; + + this._tooltip = tooltip; + }); + + element.addEventListener('mouseleave', function() { + if (this._tooltip) { + this._tooltip.remove(); + this._tooltip = null; + } + }); + }); +}; + +/** + * Initialize auto-refresh functionality + */ +WazuhPlugin.initAutoRefresh = function() { + const refreshInterval = parseInt(document.body.getAttribute('data-refresh-interval')) || 0; + + if (refreshInterval > 0) { + setInterval(() => { + // Only refresh if page is visible + if (!document.hidden) { + this.refreshData(); + } + }, refreshInterval * 1000); + } +}; + +/** + * Refresh data without full page reload + */ +WazuhPlugin.refreshData = function() { + // Update statistics + fetch(CFG_GLPI.root_doc + '/plugins/siem-wazuh/ajax/sync_alerts.php?action=status') + .then(response => response.json()) + .then(data => { + if (data.success) { + this.updateStatistics(data.stats); + } + }) + .catch(error => console.warn('Auto-refresh failed:', error)); +}; + +/** + * Initialize form validation + */ +WazuhPlugin.initFormValidation = function() { + // Validate server URLs + const urlInputs = document.querySelectorAll('input[name="wazuh_url"], input[name="indexer_url"]'); + urlInputs.forEach(input => { + input.addEventListener('blur', function() { + if (this.value && !this.value.match(/^https?:\/\/.+/)) { + this.setCustomValidity('Please enter a valid URL starting with http:// or https://'); + } else { + this.setCustomValidity(''); + } + }); + }); + + // Validate port numbers + const portInputs = document.querySelectorAll('input[name="wazuh_port"], input[name="indexer_port"]'); + portInputs.forEach(input => { + input.addEventListener('blur', function() { + const port = parseInt(this.value); + if (this.value && (isNaN(port) || port < 1 || port > 65535)) { + this.setCustomValidity('Please enter a valid port number (1-65535)'); + } else { + this.setCustomValidity(''); + } + }); + }); +}; + +/** + * Initialize keyboard shortcuts + */ +WazuhPlugin.initKeyboardShortcuts = function() { + document.addEventListener('keydown', function(e) { + // Ctrl+Shift+S: Sync all servers + if (e.ctrlKey && e.shiftKey && e.key === 'S') { + e.preventDefault(); + const syncButton = document.querySelector('.sync-all-btn'); + if (syncButton) { + syncButton.click(); + } + } + + // Ctrl+Shift+T: Test connection + if (e.ctrlKey && e.shiftKey && e.key === 'T') { + e.preventDefault(); + const testButton = document.querySelector('.test-connection-btn'); + if (testButton) { + testButton.click(); + } + } + + // Escape: Close modals/notifications + if (e.key === 'Escape') { + const notifications = document.querySelectorAll('.wazuh-notification'); + notifications.forEach(notification => notification.remove()); + } + }); +}; + +/** + * Utility function to get CSRF token + */ +function getAjaxCsrfToken() { + return $('meta[name="glpi-csrf-token"]').attr('content') || ''; +} + +/** + * Language strings (will be populated by PHP) + */ +var LANG = LANG || { + TESTING: 'Testing...', + SYNCHRONIZING: 'Synchronizing...', + TEST_CONNECTION: 'Test Connection', + SYNC_ALERTS: 'Sync Alerts', + CONNECTION_TEST_FAILED: 'Connection test failed', + SYNC_FAILED: 'Sync failed:', + SYNC_ERROR: 'An error occurred during synchronization', + CONFIRM_SYNC_ALERTS: 'Are you sure you want to synchronize alerts from this server?', + CONFIRM_SYNC_ITEM: 'Are you sure you want to sync alerts for this item?', + CONFIRM_TOGGLE_STATUS: 'Are you sure you want to change the status of this server?' +}; + +// Initialize when DOM is ready +document.addEventListener('DOMContentLoaded', function() { + WazuhPlugin.init(); +}); + +// Expose to global scope for legacy compatibility +window.WazuhPlugin = WazuhPlugin; +window.testWazuhConnection = WazuhPlugin.testConnection.bind(WazuhPlugin); +window.syncWazuhAlerts = WazuhPlugin.syncAlerts.bind(WazuhPlugin); +window.syncAlertsForItem = WazuhPlugin.syncAlertsForItem.bind(WazuhPlugin); +window.toggleServerStatus = WazuhPlugin.toggleServerStatus.bind(WazuhPlugin); +window.checkAll = WazuhPlugin.checkAllAlerts.bind(WazuhPlugin); \ No newline at end of file diff --git a/locales/de_DE.mo b/locales/de_DE.mo new file mode 100644 index 0000000..e69de29 diff --git a/locales/de_DE.po b/locales/de_DE.po new file mode 100644 index 0000000..f06bc93 --- /dev/null +++ b/locales/de_DE.po @@ -0,0 +1,188 @@ +# SIEM-Wazuh Plugin for GLPI - German Translation +# Copyright (C) 2024 SIEM-Wazuh Team +# This file is distributed under the same license as GLPI. +# +msgid "" +msgstr "" +"Project-Id-Version: GLPI Plugin SIEM-Wazuh 1.0.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-01-01 00:00+0000\n" +"PO-Revision-Date: 2024-01-01 00:00+0000\n" +"Last-Translator: SIEM-Wazuh Team\n" +"Language-Team: German\n" +"Language: de_DE\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +# Plugin information +msgid "SIEM - Wazuh" +msgstr "SIEM - Wazuh" + +msgid "Wazuh Server" +msgid_plural "Wazuh Servers" +msgstr[0] "Wazuh Server" +msgstr[1] "Wazuh Server" + +msgid "Wazuh Alert" +msgid_plural "Wazuh Alerts" +msgstr[0] "Wazuh Warnung" +msgstr[1] "Wazuh Warnungen" + +msgid "SIEM Wazuh Configuration" +msgstr "SIEM Wazuh Konfiguration" + +# Server configuration +msgid "Wazuh Server URL" +msgstr "Wazuh Server URL" + +msgid "Wazuh API Port" +msgstr "Wazuh API Port" + +msgid "Wazuh API Login" +msgstr "Wazuh API Anmeldung" + +msgid "Wazuh API Password" +msgstr "Wazuh API Passwort" + +msgid "Indexer Server URL" +msgstr "Indexer Server URL" + +msgid "Sync Interval (seconds)" +msgstr "Synchronisationsintervall (Sekunden)" + +msgid "Last Sync" +msgstr "Letzte Synchronisation" + +msgid "Default Ticket Type" +msgstr "Standard Ticket-Typ" + +msgid "Default Ticket Category" +msgstr "Standard Ticket-Kategorie" + +# Alerts +msgid "Alert ID" +msgstr "Warn-ID" + +msgid "Rule ID" +msgstr "Regel-ID" + +msgid "Rule Level" +msgstr "Regelebene" + +msgid "Rule Description" +msgstr "Regelbeschreibung" + +msgid "Agent Name" +msgstr "Agent Name" + +msgid "Agent IP" +msgstr "Agent IP" + +msgid "Alert Timestamp" +msgstr "Warnzeit" + +msgid "Severity" +msgstr "Schweregrad" + +# Status values +msgid "New" +msgstr "Neu" + +msgid "Processed" +msgstr "Bearbeitet" + +msgid "Ignored" +msgstr "Ignoriert" + +msgid "Ticket Created" +msgstr "Ticket erstellt" + +# Severity values +msgid "Low" +msgstr "Niedrig" + +msgid "Medium" +msgstr "Mittel" + +msgid "High" +msgstr "Hoch" + +msgid "Critical" +msgstr "Kritisch" + +# Actions +msgid "Test Connection" +msgstr "Verbindung testen" + +msgid "Sync Alerts" +msgstr "Warnungen synchronisieren" + +msgid "Sync Now" +msgstr "Jetzt synchronisieren" + +msgid "Export CSV" +msgstr "CSV exportieren" + +# Configuration +msgid "General Configuration" +msgstr "Allgemeine Konfiguration" + +msgid "Enable synchronization" +msgstr "Synchronisation aktivieren" + +msgid "Auto-create tickets" +msgstr "Tickets automatisch erstellen" + +msgid "Maximum alerts per sync" +msgstr "Maximale Warnungen pro Synchronisation" + +msgid "Alert retention (days)" +msgstr "Warnungsaufbewahrung (Tage)" + +msgid "Configuration" +msgstr "Konfiguration" + +msgid "Servers" +msgstr "Server" + +msgid "Alerts" +msgstr "Warnungen" + +# Messages +msgid "Connection successful" +msgstr "Verbindung erfolgreich" + +msgid "Connection failed:" +msgstr "Verbindung fehlgeschlagen:" + +msgid "Server activated" +msgstr "Server aktiviert" + +msgid "Server deactivated" +msgstr "Server deaktiviert" + +msgid "Testing..." +msgstr "Teste..." + +msgid "Synchronizing..." +msgstr "Synchronisiere..." + +msgid "Never" +msgstr "Nie" + +msgid "Statistics" +msgstr "Statistiken" + +msgid "Total servers" +msgstr "Server insgesamt" + +msgid "Active servers" +msgstr "Aktive Server" + +msgid "Total alerts" +msgstr "Warnungen insgesamt" + +msgid "New alerts" +msgstr "Neue Warnungen" \ No newline at end of file diff --git a/locales/en_GB.mo b/locales/en_GB.mo new file mode 100644 index 0000000..e69de29 diff --git a/locales/en_GB.po b/locales/en_GB.po new file mode 100644 index 0000000..1c63217 --- /dev/null +++ b/locales/en_GB.po @@ -0,0 +1,543 @@ +# SIEM-Wazuh Plugin for GLPI - English Translation +# Copyright (C) 2024 SIEM-Wazuh Team +# This file is distributed under the same license as GLPI. +# +msgid "" +msgstr "" +"Project-Id-Version: GLPI Plugin SIEM-Wazuh 1.0.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-01-01 00:00+0000\n" +"PO-Revision-Date: 2024-01-01 00:00+0000\n" +"Last-Translator: SIEM-Wazuh Team\n" +"Language-Team: English\n" +"Language: en_GB\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +# Plugin information +msgid "SIEM - Wazuh" +msgstr "SIEM - Wazuh" + +msgid "Wazuh Server" +msgid_plural "Wazuh Servers" +msgstr[0] "Wazuh Server" +msgstr[1] "Wazuh Servers" + +msgid "Wazuh Alert" +msgid_plural "Wazuh Alerts" +msgstr[0] "Wazuh Alert" +msgstr[1] "Wazuh Alerts" + +msgid "SIEM Wazuh Configuration" +msgstr "SIEM Wazuh Configuration" + +# Server configuration +msgid "Wazuh Server URL" +msgstr "Wazuh Server URL" + +msgid "Wazuh API Port" +msgstr "Wazuh API Port" + +msgid "Wazuh API Login" +msgstr "Wazuh API Login" + +msgid "Wazuh API Password" +msgstr "Wazuh API Password" + +msgid "Indexer Server URL" +msgstr "Indexer Server URL" + +msgid "Indexer API Port" +msgstr "Indexer API Port" + +msgid "Indexer API Login" +msgstr "Indexer API Login" + +msgid "Indexer API Password" +msgstr "Indexer API Password" + +msgid "Sync Interval (seconds)" +msgstr "Sync Interval (seconds)" + +msgid "Last Sync" +msgstr "Last Sync" + +msgid "Default Ticket Type" +msgstr "Default Ticket Type" + +msgid "Default Ticket Category" +msgstr "Default Ticket Category" + +msgid "Leave empty to keep current password" +msgstr "Leave empty to keep current password" + +# Alerts +msgid "Alert ID" +msgstr "Alert ID" + +msgid "Rule ID" +msgstr "Rule ID" + +msgid "Rule Level" +msgstr "Rule Level" + +msgid "Rule Description" +msgstr "Rule Description" + +msgid "Agent Name" +msgstr "Agent Name" + +msgid "Agent IP" +msgstr "Agent IP" + +msgid "Alert Timestamp" +msgstr "Alert Timestamp" + +msgid "Severity" +msgstr "Severity" + +msgid "Associated Computer" +msgstr "Associated Computer" + +msgid "Associated Network Equipment" +msgstr "Associated Network Equipment" + +msgid "Associated Ticket" +msgstr "Associated Ticket" + +msgid "Raw Alert Data" +msgstr "Raw Alert Data" + +# Status values +msgid "New" +msgstr "New" + +msgid "Processed" +msgstr "Processed" + +msgid "Ignored" +msgstr "Ignored" + +msgid "Ticket Created" +msgstr "Ticket Created" + +# Severity values +msgid "Low" +msgstr "Low" + +msgid "Medium" +msgstr "Medium" + +msgid "High" +msgstr "High" + +msgid "Critical" +msgstr "Critical" + +# Actions +msgid "Test Connection" +msgstr "Test Connection" + +msgid "Sync Alerts" +msgstr "Sync Alerts" + +msgid "Sync Now" +msgstr "Sync Now" + +msgid "Export CSV" +msgstr "Export CSV" + +msgid "Mark as Processed" +msgstr "Mark as Processed" + +msgid "Mark as Ignored" +msgstr "Mark as Ignored" + +msgid "Create Tickets" +msgstr "Create Tickets" + +# Configuration +msgid "General Configuration" +msgstr "General Configuration" + +msgid "Enable synchronization" +msgstr "Enable synchronization" + +msgid "Auto-create tickets" +msgstr "Auto-create tickets" + +msgid "Maximum alerts per sync" +msgstr "Maximum alerts per sync" + +msgid "Alert retention (days)" +msgstr "Alert retention (days)" + +msgid "Minimum rule level" +msgstr "Minimum rule level" + +msgid "Enable notifications" +msgstr "Enable notifications" + +msgid "Default Ticket Settings" +msgstr "Default Ticket Settings" + +msgid "Default Priority" +msgstr "Default Priority" + +msgid "Default Urgency" +msgstr "Default Urgency" + +msgid "Default Impact" +msgstr "Default Impact" + +msgid "Auto-assign tickets" +msgstr "Auto-assign tickets" + +msgid "Default Assignee" +msgstr "Default Assignee" + +msgid "Default Group" +msgstr "Default Group" + +msgid "Asset Mapping Configuration" +msgstr "Asset Mapping Configuration" + +msgid "Match by hostname" +msgstr "Match by hostname" + +msgid "Match by IP address" +msgstr "Match by IP address" + +msgid "Case sensitive matching" +msgstr "Case sensitive matching" + +msgid "Create unknown assets" +msgstr "Create unknown assets" + +msgid "Debug Configuration" +msgstr "Debug Configuration" + +msgid "Enable debug mode" +msgstr "Enable debug mode" + +msgid "Log level" +msgstr "Log level" + +msgid "Keep debug logs (days)" +msgstr "Keep debug logs (days)" + +msgid "Error only" +msgstr "Error only" + +msgid "Warning and above" +msgstr "Warning and above" + +msgid "Info and above" +msgstr "Info and above" + +msgid "All messages" +msgstr "All messages" + +# Statistics +msgid "Statistics" +msgstr "Statistics" + +msgid "Total servers" +msgstr "Total servers" + +msgid "Active servers" +msgstr "Active servers" + +msgid "Total alerts" +msgstr "Total alerts" + +msgid "New alerts" +msgstr "New alerts" + +msgid "Processed alerts" +msgstr "Processed alerts" + +msgid "Tickets created" +msgstr "Tickets created" + +msgid "%s alerts" +msgstr "%s alerts" + +msgid "Last global sync" +msgstr "Last global sync" + +msgid "Never" +msgstr "Never" + +# Tab interface +msgid "Wazuh Alerts" +msgstr "Wazuh Alerts" + +msgid "Alerts Summary" +msgstr "Alerts Summary" + +msgid "Total" +msgstr "Total" + +msgid "Filters" +msgstr "Filters" + +msgid "Date from" +msgstr "Date from" + +msgid "Date to" +msgstr "Date to" + +msgid "Reset" +msgstr "Reset" + +msgid "Filter" +msgstr "Filter" + +msgid "No Wazuh alerts found for this item" +msgstr "No Wazuh alerts found for this item" + +msgid "No alerts found" +msgstr "No alerts found" + +msgid "Mass Actions" +msgstr "Mass Actions" + +msgid "Sync new alerts" +msgstr "Sync new alerts" + +msgid "Export alerts" +msgstr "Export alerts" + +# Messages +msgid "Connection successful" +msgstr "Connection successful" + +msgid "Connection failed:" +msgstr "Connection failed:" + +msgid "Authentication failed:" +msgstr "Authentication failed:" + +msgid "Connection error:" +msgstr "Connection error:" + +msgid "Missing connection parameters" +msgstr "Missing connection parameters" + +msgid "Server is not active" +msgstr "Server is not active" + +msgid "%d alerts processed" +msgstr "%d alerts processed" + +msgid "Configuration updated successfully" +msgstr "Configuration updated successfully" + +msgid "Server activated" +msgstr "Server activated" + +msgid "Server deactivated" +msgstr "Server deactivated" + +msgid "Testing..." +msgstr "Testing..." + +msgid "Connection test failed" +msgstr "Connection test failed" + +msgid "Synchronizing..." +msgstr "Synchronizing..." + +msgid "Sync completed successfully" +msgstr "Sync completed successfully" + +msgid "Sync failed:" +msgstr "Sync failed:" + +msgid "An error occurred during sync" +msgstr "An error occurred during sync" + +msgid "An error occurred during synchronization" +msgstr "An error occurred during synchronization" + +msgid "Are you sure you want to change the status of this server?" +msgstr "Are you sure you want to change the status of this server?" + +msgid "Are you sure you want to synchronize alerts from this server?" +msgstr "Are you sure you want to synchronize alerts from this server?" + +msgid "Are you sure you want to sync alerts for this item?" +msgstr "Are you sure you want to sync alerts for this item?" + +# Ticket content +msgid "Wazuh Alert: %s" +msgstr "Wazuh Alert: %s" + +msgid "Alert ID: %s" +msgstr "Alert ID: %s" + +msgid "Rule ID: %s" +msgstr "Rule ID: %s" + +msgid "Rule Level: %s" +msgstr "Rule Level: %s" + +msgid "Agent: %s (%s)" +msgstr "Agent: %s (%s)" + +msgid "Timestamp: %s" +msgstr "Timestamp: %s" + +msgid "Description: %s" +msgstr "Description: %s" + +msgid "Raw Alert Data:" +msgstr "Raw Alert Data:" + +# Cron tasks +msgid "Synchronize Wazuh alerts" +msgstr "Synchronize Wazuh alerts" + +msgid "Cleanup old alerts" +msgstr "Cleanup old alerts" + +# Rights +msgid "SIEM Wazuh Rights" +msgstr "SIEM Wazuh Rights" + +msgid "Configuration" +msgstr "Configuration" + +msgid "Servers" +msgstr "Servers" + +msgid "Alerts" +msgstr "Alerts" + +# Error messages +msgid "Access denied" +msgstr "Access denied" + +msgid "Invalid CSRF token" +msgstr "Invalid CSRF token" + +msgid "Invalid server ID" +msgstr "Invalid server ID" + +msgid "Server not found" +msgstr "Server not found" + +msgid "Invalid item parameters" +msgstr "Invalid item parameters" + +msgid "Invalid item type" +msgstr "Invalid item type" + +msgid "Item not found" +msgstr "Item not found" + +msgid "Unknown action" +msgstr "Unknown action" + +msgid "Request failed after maximum retries" +msgstr "Request failed after maximum retries" + +msgid "Indexer not configured" +msgstr "Indexer not configured" + +msgid "Authentication failed" +msgstr "Authentication failed" + +msgid "Status retrieved successfully" +msgstr "Status retrieved successfully" + +# Additional configuration +msgid "Asset Mapping" +msgstr "Asset Mapping" + +msgid "Notifications" +msgstr "Notifications" + +msgid "Debug & Logs" +msgstr "Debug & Logs" + +msgid "Email Notifications" +msgstr "Email Notifications" + +msgid "Enable email notifications" +msgstr "Enable email notifications" + +msgid "Notification for critical alerts only" +msgstr "Notification for critical alerts only" + +msgid "Default notification recipients" +msgstr "Default notification recipients" + +msgid "Comma-separated email addresses" +msgstr "Comma-separated email addresses" + +msgid "Asset Detection Rules" +msgstr "Asset Detection Rules" + +msgid "Match computers by hostname" +msgstr "Match computers by hostname" + +msgid "Match network equipment by hostname" +msgstr "Match network equipment by hostname" + +msgid "Hostname patterns to ignore" +msgstr "Hostname patterns to ignore" + +msgid "One pattern per line. Use * as wildcard." +msgstr "One pattern per line. Use * as wildcard." + +msgid "Test Asset Mapping" +msgstr "Test Asset Mapping" + +msgid "Agent name or IP" +msgstr "Agent name or IP" + +msgid "Test" +msgstr "Test" + +msgid "Please enter an agent name or IP address" +msgstr "Please enter an agent name or IP address" + +msgid "Test failed" +msgstr "Test failed" + +msgid "Log API requests" +msgstr "Log API requests" + +msgid "Keep logs for (days)" +msgstr "Keep logs for (days)" + +msgid "Clear All Logs" +msgstr "Clear All Logs" + +msgid "Are you sure you want to clear all logs?" +msgstr "Are you sure you want to clear all logs?" + +msgid "Logs cleared successfully" +msgstr "Logs cleared successfully" + +msgid "Failed to clear logs" +msgstr "Failed to clear logs" + +msgid "Recent Logs" +msgstr "Recent Logs" + +msgid "Level" +msgstr "Level" + +msgid "Message" +msgstr "Message" + +msgid "System" +msgstr "System" + +msgid "No logs found" +msgstr "No logs found" \ No newline at end of file diff --git a/locales/es_ES.mo b/locales/es_ES.mo new file mode 100644 index 0000000..e69de29 diff --git a/locales/es_ES.po b/locales/es_ES.po new file mode 100644 index 0000000..e69de29 diff --git a/locales/fr_FR.mo b/locales/fr_FR.mo new file mode 100644 index 0000000..e69de29 diff --git a/locales/fr_FR.po b/locales/fr_FR.po new file mode 100644 index 0000000..9eb3a35 --- /dev/null +++ b/locales/fr_FR.po @@ -0,0 +1,543 @@ +# SIEM-Wazuh Plugin for GLPI - French Translation +# Copyright (C) 2024 SIEM-Wazuh Team +# This file is distributed under the same license as GLPI. +# +msgid "" +msgstr "" +"Project-Id-Version: GLPI Plugin SIEM-Wazuh 1.0.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-01-01 00:00+0000\n" +"PO-Revision-Date: 2024-01-01 00:00+0000\n" +"Last-Translator: SIEM-Wazuh Team\n" +"Language-Team: French\n" +"Language: fr_FR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +# Plugin information +msgid "SIEM - Wazuh" +msgstr "SIEM - Wazuh" + +msgid "Wazuh Server" +msgid_plural "Wazuh Servers" +msgstr[0] "Serveur Wazuh" +msgstr[1] "Serveurs Wazuh" + +msgid "Wazuh Alert" +msgid_plural "Wazuh Alerts" +msgstr[0] "Alerte Wazuh" +msgstr[1] "Alertes Wazuh" + +msgid "SIEM Wazuh Configuration" +msgstr "Configuration SIEM Wazuh" + +# Server configuration +msgid "Wazuh Server URL" +msgstr "URL du serveur Wazuh" + +msgid "Wazuh API Port" +msgstr "Port API Wazuh" + +msgid "Wazuh API Login" +msgstr "Login API Wazuh" + +msgid "Wazuh API Password" +msgstr "Mot de passe API Wazuh" + +msgid "Indexer Server URL" +msgstr "URL du serveur Indexer" + +msgid "Indexer API Port" +msgstr "Port API Indexer" + +msgid "Indexer API Login" +msgstr "Login API Indexer" + +msgid "Indexer API Password" +msgstr "Mot de passe API Indexer" + +msgid "Sync Interval (seconds)" +msgstr "Intervalle de synchronisation (secondes)" + +msgid "Last Sync" +msgstr "Dernière synchronisation" + +msgid "Default Ticket Type" +msgstr "Type de ticket par défaut" + +msgid "Default Ticket Category" +msgstr "Catégorie de ticket par défaut" + +msgid "Leave empty to keep current password" +msgstr "Laisser vide pour conserver le mot de passe actuel" + +# Alerts +msgid "Alert ID" +msgstr "ID d'alerte" + +msgid "Rule ID" +msgstr "ID de règle" + +msgid "Rule Level" +msgstr "Niveau de règle" + +msgid "Rule Description" +msgstr "Description de la règle" + +msgid "Agent Name" +msgstr "Nom de l'agent" + +msgid "Agent IP" +msgstr "IP de l'agent" + +msgid "Alert Timestamp" +msgstr "Horodatage de l'alerte" + +msgid "Severity" +msgstr "Sévérité" + +msgid "Associated Computer" +msgstr "Ordinateur associé" + +msgid "Associated Network Equipment" +msgstr "Équipement réseau associé" + +msgid "Associated Ticket" +msgstr "Ticket associé" + +msgid "Raw Alert Data" +msgstr "Données brutes de l'alerte" + +# Status values +msgid "New" +msgstr "Nouveau" + +msgid "Processed" +msgstr "Traité" + +msgid "Ignored" +msgstr "Ignoré" + +msgid "Ticket Created" +msgstr "Ticket créé" + +# Severity values +msgid "Low" +msgstr "Faible" + +msgid "Medium" +msgstr "Moyen" + +msgid "High" +msgstr "Élevé" + +msgid "Critical" +msgstr "Critique" + +# Actions +msgid "Test Connection" +msgstr "Tester la connexion" + +msgid "Sync Alerts" +msgstr "Synchroniser les alertes" + +msgid "Sync Now" +msgstr "Synchroniser maintenant" + +msgid "Export CSV" +msgstr "Exporter CSV" + +msgid "Mark as Processed" +msgstr "Marquer comme traité" + +msgid "Mark as Ignored" +msgstr "Marquer comme ignoré" + +msgid "Create Tickets" +msgstr "Créer des tickets" + +# Configuration +msgid "General Configuration" +msgstr "Configuration générale" + +msgid "Enable synchronization" +msgstr "Activer la synchronisation" + +msgid "Auto-create tickets" +msgstr "Création automatique de tickets" + +msgid "Maximum alerts per sync" +msgstr "Maximum d'alertes par synchronisation" + +msgid "Alert retention (days)" +msgstr "Rétention des alertes (jours)" + +msgid "Minimum rule level" +msgstr "Niveau de règle minimum" + +msgid "Enable notifications" +msgstr "Activer les notifications" + +msgid "Default Ticket Settings" +msgstr "Paramètres de ticket par défaut" + +msgid "Default Priority" +msgstr "Priorité par défaut" + +msgid "Default Urgency" +msgstr "Urgence par défaut" + +msgid "Default Impact" +msgstr "Impact par défaut" + +msgid "Auto-assign tickets" +msgstr "Attribution automatique des tickets" + +msgid "Default Assignee" +msgstr "Assigné par défaut" + +msgid "Default Group" +msgstr "Groupe par défaut" + +msgid "Asset Mapping Configuration" +msgstr "Configuration du mapping des assets" + +msgid "Match by hostname" +msgstr "Correspondance par nom d'hôte" + +msgid "Match by IP address" +msgstr "Correspondance par adresse IP" + +msgid "Case sensitive matching" +msgstr "Correspondance sensible à la casse" + +msgid "Create unknown assets" +msgstr "Créer les assets inconnus" + +msgid "Debug Configuration" +msgstr "Configuration de débogage" + +msgid "Enable debug mode" +msgstr "Activer le mode débogage" + +msgid "Log level" +msgstr "Niveau de log" + +msgid "Keep debug logs (days)" +msgstr "Conserver les logs de débogage (jours)" + +msgid "Error only" +msgstr "Erreurs seulement" + +msgid "Warning and above" +msgstr "Avertissements et plus" + +msgid "Info and above" +msgstr "Informations et plus" + +msgid "All messages" +msgstr "Tous les messages" + +# Statistics +msgid "Statistics" +msgstr "Statistiques" + +msgid "Total servers" +msgstr "Total des serveurs" + +msgid "Active servers" +msgstr "Serveurs actifs" + +msgid "Total alerts" +msgstr "Total des alertes" + +msgid "New alerts" +msgstr "Nouvelles alertes" + +msgid "Processed alerts" +msgstr "Alertes traitées" + +msgid "Tickets created" +msgstr "Tickets créés" + +msgid "%s alerts" +msgstr "Alertes %s" + +msgid "Last global sync" +msgstr "Dernière synchronisation globale" + +msgid "Never" +msgstr "Jamais" + +# Tab interface +msgid "Wazuh Alerts" +msgstr "Alertes Wazuh" + +msgid "Alerts Summary" +msgstr "Résumé des alertes" + +msgid "Total" +msgstr "Total" + +msgid "Filters" +msgstr "Filtres" + +msgid "Date from" +msgstr "Date de début" + +msgid "Date to" +msgstr "Date de fin" + +msgid "Reset" +msgstr "Réinitialiser" + +msgid "Filter" +msgstr "Filtrer" + +msgid "No Wazuh alerts found for this item" +msgstr "Aucune alerte Wazuh trouvée pour cet élément" + +msgid "No alerts found" +msgstr "Aucune alerte trouvée" + +msgid "Mass Actions" +msgstr "Actions de masse" + +msgid "Sync new alerts" +msgstr "Synchroniser les nouvelles alertes" + +msgid "Export alerts" +msgstr "Exporter les alertes" + +# Messages +msgid "Connection successful" +msgstr "Connexion réussie" + +msgid "Connection failed:" +msgstr "Échec de la connexion :" + +msgid "Authentication failed:" +msgstr "Échec de l'authentification :" + +msgid "Connection error:" +msgstr "Erreur de connexion :" + +msgid "Missing connection parameters" +msgstr "Paramètres de connexion manquants" + +msgid "Server is not active" +msgstr "Le serveur n'est pas actif" + +msgid "%d alerts processed" +msgstr "%d alertes traitées" + +msgid "Configuration updated successfully" +msgstr "Configuration mise à jour avec succès" + +msgid "Server activated" +msgstr "Serveur activé" + +msgid "Server deactivated" +msgstr "Serveur désactivé" + +msgid "Testing..." +msgstr "Test en cours..." + +msgid "Connection test failed" +msgstr "Échec du test de connexion" + +msgid "Synchronizing..." +msgstr "Synchronisation en cours..." + +msgid "Sync completed successfully" +msgstr "Synchronisation terminée avec succès" + +msgid "Sync failed:" +msgstr "Échec de la synchronisation :" + +msgid "An error occurred during sync" +msgstr "Une erreur s'est produite lors de la synchronisation" + +msgid "An error occurred during synchronization" +msgstr "Une erreur s'est produite lors de la synchronisation" + +msgid "Are you sure you want to change the status of this server?" +msgstr "Êtes-vous sûr de vouloir changer le statut de ce serveur ?" + +msgid "Are you sure you want to synchronize alerts from this server?" +msgstr "Êtes-vous sûr de vouloir synchroniser les alertes de ce serveur ?" + +msgid "Are you sure you want to sync alerts for this item?" +msgstr "Êtes-vous sûr de vouloir synchroniser les alertes pour cet élément ?" + +# Ticket content +msgid "Wazuh Alert: %s" +msgstr "Alerte Wazuh : %s" + +msgid "Alert ID: %s" +msgstr "ID d'alerte : %s" + +msgid "Rule ID: %s" +msgstr "ID de règle : %s" + +msgid "Rule Level: %s" +msgstr "Niveau de règle : %s" + +msgid "Agent: %s (%s)" +msgstr "Agent : %s (%s)" + +msgid "Timestamp: %s" +msgstr "Horodatage : %s" + +msgid "Description: %s" +msgstr "Description : %s" + +msgid "Raw Alert Data:" +msgstr "Données brutes de l'alerte :" + +# Cron tasks +msgid "Synchronize Wazuh alerts" +msgstr "Synchroniser les alertes Wazuh" + +msgid "Cleanup old alerts" +msgstr "Nettoyer les anciennes alertes" + +# Rights +msgid "SIEM Wazuh Rights" +msgstr "Droits SIEM Wazuh" + +msgid "Configuration" +msgstr "Configuration" + +msgid "Servers" +msgstr "Serveurs" + +msgid "Alerts" +msgstr "Alertes" + +# Error messages +msgid "Access denied" +msgstr "Accès refusé" + +msgid "Invalid CSRF token" +msgstr "Token CSRF invalide" + +msgid "Invalid server ID" +msgstr "ID de serveur invalide" + +msgid "Server not found" +msgstr "Serveur non trouvé" + +msgid "Invalid item parameters" +msgstr "Paramètres d'élément invalides" + +msgid "Invalid item type" +msgstr "Type d'élément invalide" + +msgid "Item not found" +msgstr "Élément non trouvé" + +msgid "Unknown action" +msgstr "Action inconnue" + +msgid "Request failed after maximum retries" +msgstr "Échec de la requête après le nombre maximum de tentatives" + +msgid "Indexer not configured" +msgstr "Indexer non configuré" + +msgid "Authentication failed" +msgstr "Échec de l'authentification" + +msgid "Status retrieved successfully" +msgstr "Statut récupéré avec succès" + +# Additional configuration +msgid "Asset Mapping" +msgstr "Mapping des Assets" + +msgid "Notifications" +msgstr "Notifications" + +msgid "Debug & Logs" +msgstr "Debug et Logs" + +msgid "Email Notifications" +msgstr "Notifications par Email" + +msgid "Enable email notifications" +msgstr "Activer les notifications par email" + +msgid "Notification for critical alerts only" +msgstr "Notifications pour les alertes critiques uniquement" + +msgid "Default notification recipients" +msgstr "Destinataires par défaut des notifications" + +msgid "Comma-separated email addresses" +msgstr "Adresses email séparées par des virgules" + +msgid "Asset Detection Rules" +msgstr "Règles de détection des assets" + +msgid "Match computers by hostname" +msgstr "Correspondance ordinateurs par nom d'hôte" + +msgid "Match network equipment by hostname" +msgstr "Correspondance équipements réseau par nom d'hôte" + +msgid "Hostname patterns to ignore" +msgstr "Motifs de nom d'hôte à ignorer" + +msgid "One pattern per line. Use * as wildcard." +msgstr "Un motif par ligne. Utilisez * comme caractère générique." + +msgid "Test Asset Mapping" +msgstr "Tester le mapping des assets" + +msgid "Agent name or IP" +msgstr "Nom d'agent ou IP" + +msgid "Test" +msgstr "Tester" + +msgid "Please enter an agent name or IP address" +msgstr "Veuillez saisir un nom d'agent ou une adresse IP" + +msgid "Test failed" +msgstr "Échec du test" + +msgid "Log API requests" +msgstr "Journaliser les requêtes API" + +msgid "Keep logs for (days)" +msgstr "Conserver les logs pendant (jours)" + +msgid "Clear All Logs" +msgstr "Effacer tous les logs" + +msgid "Are you sure you want to clear all logs?" +msgstr "Êtes-vous sûr de vouloir effacer tous les logs ?" + +msgid "Logs cleared successfully" +msgstr "Logs effacés avec succès" + +msgid "Failed to clear logs" +msgstr "Échec de l'effacement des logs" + +msgid "Recent Logs" +msgstr "Logs récents" + +msgid "Level" +msgstr "Niveau" + +msgid "Message" +msgstr "Message" + +msgid "System" +msgstr "Système" + +msgid "No logs found" +msgstr "Aucun log trouvé" \ No newline at end of file diff --git a/locales/it_IT.mo b/locales/it_IT.mo new file mode 100644 index 0000000..e69de29 diff --git a/locales/it_IT.po b/locales/it_IT.po new file mode 100644 index 0000000..e69de29 diff --git a/locales/pl_PL.mo b/locales/pl_PL.mo new file mode 100644 index 0000000..e69de29 diff --git a/locales/pl_PL.po b/locales/pl_PL.po new file mode 100644 index 0000000..e69de29 diff --git a/locales/pt_BR.mo b/locales/pt_BR.mo new file mode 100644 index 0000000..e69de29 diff --git a/locales/pt_BR.po b/locales/pt_BR.po new file mode 100644 index 0000000..e69de29 diff --git a/setup.php b/setup.php new file mode 100644 index 0000000..1caac9a --- /dev/null +++ b/setup.php @@ -0,0 +1,235 @@ + 'admin', + 'linktext' => __('Wazuh Servers', 'siem-wazuh'), + 'icon' => 'fas fa-shield-alt' + ]); + + Plugin::registerClass('PluginSiemWazuhConfig', [ + 'linkgroup' => 'tools', + 'linktext' => __('SIEM Wazuh Configuration', 'siem-wazuh'), + 'icon' => 'fas fa-cogs' + ]); + + Plugin::registerClass('PluginSiemWazuhAlert'); + + // Ajout des menus + if (Session::haveRight('plugin_siem_wazuh_server', READ)) { + $PLUGIN_HOOKS['menu_toadd']['siem-wazuh']['admin'] = 'PluginSiemWazuhServer'; + } + + if (Session::haveRight('plugin_siem_wazuh_config', READ)) { + $PLUGIN_HOOKS['menu_toadd']['siem-wazuh']['tools'] = 'PluginSiemWazuhConfig'; + } + + // Ajout des onglets sur les éléments + $PLUGIN_HOOKS['item_add_targets']['siem-wazuh'] = [ + 'Computer' => ['PluginSiemWazuhTab'], + 'NetworkEquipment' => ['PluginSiemWazuhTab'], + 'Peripheral' => ['PluginSiemWazuhTab'], + 'Phone' => ['PluginSiemWazuhTab'], + 'Printer' => ['PluginSiemWazuhTab'] + ]; + + // Hook pour l'affichage des onglets + $PLUGIN_HOOKS['display_item']['siem-wazuh'] = 'plugin_siem_wazuh_display_item'; + + // Hook pour les actions automatiques (cron) + $PLUGIN_HOOKS['cron']['siem-wazuh'] = 1; + + // Hook pour les droits + $PLUGIN_HOOKS['change_profile']['siem-wazuh'] = ['PluginSiemWazuhProfile', 'changeProfile']; + $PLUGIN_HOOKS['init_profile']['siem-wazuh'] = ['PluginSiemWazuhProfile', 'initProfile']; + + // CSS et JS + $PLUGIN_HOOKS['add_css']['siem-wazuh'][] = 'css/style.css'; + $PLUGIN_HOOKS['add_javascript']['siem-wazuh'][] = 'js/wazuh.js'; + + // Import/Export + $PLUGIN_HOOKS['import_item']['siem-wazuh'] = ['Computer', 'NetworkEquipment']; + + // Notification + $PLUGIN_HOOKS['item_get_events']['siem-wazuh'] = [ + 'PluginSiemWazuhAlert' => ['PluginSiemWazuhAlert', 'getEvents'] + ]; +} + +/** + * Plugin version function + */ +function plugin_version_siem_wazuh() { + return [ + 'name' => 'SIEM - Wazuh', + 'version' => PLUGIN_SIEM_WAZUH_VERSION, + 'author' => 'SIEM-Wazuh Team', + 'license' => 'GPLv2+', + 'homepage' => 'https://github.com/siem-wazuh/glpi-plugin', + 'requirements' => [ + 'glpi' => [ + 'min' => PLUGIN_SIEM_WAZUH_MIN_GLPI, + 'max' => PLUGIN_SIEM_WAZUH_MAX_GLPI, + ], + 'php' => [ + 'min' => '7.4', + ], + 'params' => [ + 'check_prerequisites' => true, + ] + ] + ]; +} + +/** + * Check plugin prerequisites + */ +function plugin_siem_wazuh_check_prerequisites() { + // Vérification de la version PHP + if (version_compare(PHP_VERSION, '7.4', '<')) { + echo "Ce plugin nécessite PHP 7.4 ou supérieur"; + return false; + } + + // Vérification de la version GLPI + if (!method_exists('Plugin', 'checkGlpiVersion')) { + echo "Cette version de GLPI n'est pas supportée"; + return false; + } + + if (!Plugin::checkGlpiVersion(PLUGIN_SIEM_WAZUH_MIN_GLPI, PLUGIN_SIEM_WAZUH_MAX_GLPI)) { + echo "Ce plugin nécessite GLPI >= " . PLUGIN_SIEM_WAZUH_MIN_GLPI . " et < " . PLUGIN_SIEM_WAZUH_MAX_GLPI; + return false; + } + + // Vérification des extensions PHP nécessaires + $required_extensions = ['curl', 'json', 'mbstring', 'openssl']; + foreach ($required_extensions as $ext) { + if (!extension_loaded($ext)) { + echo "Extension PHP manquante: $ext"; + return false; + } + } + + return true; +} + +/** + * Check plugin configuration + */ +function plugin_siem_wazuh_check_config() { + return true; +} + +/** + * Plugin display item hook + */ +function plugin_siem_wazuh_display_item($item) { + if (in_array($item->getType(), ['Computer', 'NetworkEquipment', 'Peripheral', 'Phone', 'Printer'])) { + if (Session::haveRight('plugin_siem_wazuh_alert', READ)) { + $tab = new PluginSiemWazuhTab(); + $tab->showForItem($item); + } + } +} + +/** + * Get cron description + */ +function plugin_siem_wazuh_cron_description($name) { + switch ($name) { + case 'sync_alerts': + return __('Synchronize Wazuh alerts', 'siem-wazuh'); + case 'cleanup_old_alerts': + return __('Cleanup old alerts', 'siem-wazuh'); + default: + return ''; + } +} + +/** + * Execute cron task + */ +function plugin_siem_wazuh_cron($name) { + global $DB; + + switch ($name) { + case 'sync_alerts': + return PluginSiemWazuhAlert::cronSyncAlerts(); + + case 'cleanup_old_alerts': + return PluginSiemWazuhAlert::cronCleanupOldAlerts(); + + default: + return false; + } +} + +/** + * Get additional menu entries + */ +function plugin_siem_wazuh_get_additional_menu_entries($forcetab = '') { + $entries = []; + + if (Session::haveRight('plugin_siem_wazuh_server', READ)) { + $entries[' ' . + __('Wazuh Servers', 'siem-wazuh')] = '/plugins/siem-wazuh/front/wazuhserver.php'; + } + + return $entries; +} + +/** + * Get dropdown values + */ +function plugin_siem_wazuh_get_dropdown_values($post, $dropdown_name = '') { + switch ($dropdown_name) { + case 'PluginSiemWazuhServer': + return PluginSiemWazuhServer::getDropdownValues($post); + default: + return []; + } +} + +/** + * Get search options + */ +function plugin_siem_wazuh_getAddSearchOptions($itemtype) { + $sopt = []; + + switch ($itemtype) { + case 'Computer': + case 'NetworkEquipment': + $sopt[5150]['table'] = 'glpi_plugin_siem_wazuh_alerts'; + $sopt[5150]['field'] = 'id'; + $sopt[5150]['name'] = __('Wazuh Alerts', 'siem-wazuh'); + $sopt[5150]['forcegroupby'] = true; + $sopt[5150]['usehaving'] = true; + $sopt[5150]['datatype'] = 'count'; + $sopt[5150]['massiveaction'] = false; + $sopt[5150]['joinparams'] = [ + 'jointype' => 'itemtype_item', + 'specific_itemtype' => $itemtype + ]; + break; + } + + return $sopt; +} \ No newline at end of file diff --git a/sql/install.sql b/sql/install.sql new file mode 100644 index 0000000..6a4e3b9 --- /dev/null +++ b/sql/install.sql @@ -0,0 +1,148 @@ +-- Plugin SIEM-Wazuh - Installation script + +-- Table des serveurs Wazuh +CREATE TABLE IF NOT EXISTS `glpi_plugin_siem_wazuh_servers` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL COMMENT 'Nom de la connexion', + `wazuh_url` VARCHAR(255) NOT NULL COMMENT 'URL du serveur Wazuh', + `wazuh_port` INT(11) DEFAULT 55000 COMMENT 'Port API Wazuh', + `wazuh_login` VARCHAR(255) NOT NULL COMMENT 'Login API Wazuh', + `wazuh_password` TEXT NOT NULL COMMENT 'Mot de passe API Wazuh (crypté)', + `indexer_url` VARCHAR(255) DEFAULT NULL COMMENT 'URL du serveur Indexer', + `indexer_port` INT(11) DEFAULT 9200 COMMENT 'Port API Indexer', + `indexer_login` VARCHAR(255) DEFAULT NULL COMMENT 'Login API Indexer', + `indexer_password` TEXT DEFAULT NULL COMMENT 'Mot de passe API Indexer (crypté)', + `sync_interval` INT(11) DEFAULT 300 COMMENT 'Intervalle de synchronisation en secondes', + `is_active` TINYINT(1) DEFAULT 1 COMMENT 'Serveur actif', + `last_sync` DATETIME DEFAULT NULL COMMENT 'Dernière synchronisation', + `ticket_type` INT(11) DEFAULT 1 COMMENT 'Type de ticket par défaut', + `ticket_category` INT(11) DEFAULT NULL COMMENT 'Catégorie de ticket par défaut', + `date_creation` DATETIME DEFAULT CURRENT_TIMESTAMP, + `date_mod` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + INDEX `is_active` (`is_active`), + INDEX `last_sync` (`last_sync`), + INDEX `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Table des alertes Wazuh +CREATE TABLE IF NOT EXISTS `glpi_plugin_siem_wazuh_alerts` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `wazuh_server_id` INT(11) NOT NULL COMMENT 'ID du serveur Wazuh', + `alert_id` VARCHAR(255) NOT NULL COMMENT 'ID unique de l\'alerte Wazuh', + `rule_id` INT(11) DEFAULT NULL COMMENT 'ID de la règle Wazuh', + `rule_level` INT(11) DEFAULT NULL COMMENT 'Niveau de la règle', + `rule_description` TEXT DEFAULT NULL COMMENT 'Description de la règle', + `agent_id` VARCHAR(255) DEFAULT NULL COMMENT 'ID de l\'agent Wazuh', + `agent_name` VARCHAR(255) DEFAULT NULL COMMENT 'Nom de l\'agent Wazuh', + `agent_ip` VARCHAR(45) DEFAULT NULL COMMENT 'IP de l\'agent', + `timestamp` DATETIME NOT NULL COMMENT 'Timestamp de l\'alerte', + `raw_data` LONGTEXT DEFAULT NULL COMMENT 'Données brutes de l\'alerte (JSON)', + `computer_id` INT(11) DEFAULT NULL COMMENT 'ID de l\'ordinateur GLPI associé', + `networkequipment_id` INT(11) DEFAULT NULL COMMENT 'ID de l\'équipement réseau GLPI associé', + `peripheral_id` INT(11) DEFAULT NULL COMMENT 'ID du périphérique GLPI associé', + `phone_id` INT(11) DEFAULT NULL COMMENT 'ID du téléphone GLPI associé', + `printer_id` INT(11) DEFAULT NULL COMMENT 'ID de l\'imprimante GLPI associée', + `ticket_id` INT(11) DEFAULT NULL COMMENT 'ID du ticket GLPI créé', + `status` ENUM('new', 'processed', 'ignored', 'ticket_created') DEFAULT 'new', + `severity` ENUM('low', 'medium', 'high', 'critical') DEFAULT 'medium', + `date_creation` DATETIME DEFAULT CURRENT_TIMESTAMP, + `date_mod` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `unique_alert` (`wazuh_server_id`, `alert_id`), + INDEX `agent_name` (`agent_name`), + INDEX `agent_ip` (`agent_ip`), + INDEX `status` (`status`), + INDEX `severity` (`severity`), + INDEX `timestamp` (`timestamp`), + INDEX `rule_level` (`rule_level`), + INDEX `computer_id` (`computer_id`), + INDEX `networkequipment_id` (`networkequipment_id`), + INDEX `peripheral_id` (`peripheral_id`), + INDEX `phone_id` (`phone_id`), + INDEX `printer_id` (`printer_id`), + INDEX `ticket_id` (`ticket_id`), + FOREIGN KEY (`wazuh_server_id`) REFERENCES `glpi_plugin_siem_wazuh_servers` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Table de configuration +CREATE TABLE IF NOT EXISTS `glpi_plugin_siem_wazuh_config` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL COMMENT 'Nom du paramètre', + `value` TEXT DEFAULT NULL COMMENT 'Valeur du paramètre', + `context` VARCHAR(100) DEFAULT 'global' COMMENT 'Contexte du paramètre', + `date_creation` DATETIME DEFAULT CURRENT_TIMESTAMP, + `date_mod` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `name_context` (`name`, `context`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Table des profils/droits +CREATE TABLE IF NOT EXISTS `glpi_plugin_siem_wazuh_profiles` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `profiles_id` INT(11) NOT NULL COMMENT 'ID du profil GLPI', + `wazuh_config` CHAR(1) DEFAULT NULL COMMENT 'Droit configuration Wazuh', + `wazuh_server` CHAR(1) DEFAULT NULL COMMENT 'Droit serveur Wazuh', + `wazuh_alert` CHAR(1) DEFAULT NULL COMMENT 'Droit alerte Wazuh', + PRIMARY KEY (`id`), + UNIQUE KEY `profiles_id` (`profiles_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Table de logs +CREATE TABLE IF NOT EXISTS `glpi_plugin_siem_wazuh_logs` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `wazuh_server_id` INT(11) DEFAULT NULL COMMENT 'ID du serveur Wazuh', + `level` ENUM('debug', 'info', 'warning', 'error', 'critical') DEFAULT 'info', + `message` TEXT NOT NULL, + `context` JSON DEFAULT NULL COMMENT 'Contexte supplémentaire', + `date_creation` DATETIME DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + INDEX `level` (`level`), + INDEX `wazuh_server_id` (`wazuh_server_id`), + INDEX `date_creation` (`date_creation`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Table des règles de correspondance +CREATE TABLE IF NOT EXISTS `glpi_plugin_siem_wazuh_rules` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL COMMENT 'Nom de la règle', + `wazuh_rule_id` INT(11) DEFAULT NULL COMMENT 'ID de la règle Wazuh', + `wazuh_rule_level` INT(11) DEFAULT NULL COMMENT 'Niveau de la règle Wazuh', + `pattern_agent_name` VARCHAR(255) DEFAULT NULL COMMENT 'Pattern nom agent', + `pattern_agent_ip` VARCHAR(255) DEFAULT NULL COMMENT 'Pattern IP agent', + `ticket_type` INT(11) DEFAULT 1 COMMENT 'Type de ticket à créer', + `ticket_category` INT(11) DEFAULT NULL COMMENT 'Catégorie de ticket', + `ticket_priority` INT(11) DEFAULT 3 COMMENT 'Priorité du ticket', + `ticket_urgency` INT(11) DEFAULT 3 COMMENT 'Urgence du ticket', + `ticket_impact` INT(11) DEFAULT 3 COMMENT 'Impact du ticket', + `auto_assign` TINYINT(1) DEFAULT 0 COMMENT 'Assignment automatique', + `assigned_to` INT(11) DEFAULT NULL COMMENT 'Utilisateur assigné', + `assigned_group` INT(11) DEFAULT NULL COMMENT 'Groupe assigné', + `is_active` TINYINT(1) DEFAULT 1, + `date_creation` DATETIME DEFAULT CURRENT_TIMESTAMP, + `date_mod` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + INDEX `wazuh_rule_id` (`wazuh_rule_id`), + INDEX `wazuh_rule_level` (`wazuh_rule_level`), + INDEX `is_active` (`is_active`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Insertion des données par défaut +INSERT INTO `glpi_plugin_siem_wazuh_config` (`name`, `value`, `context`) VALUES +('auto_create_ticket', '1', 'global'), +('default_ticket_priority', '3', 'global'), +('default_ticket_urgency', '3', 'global'), +('default_ticket_impact', '3', 'global'), +('alert_retention_days', '90', 'global'), +('sync_enabled', '1', 'global'), +('max_alerts_per_sync', '100', 'global'), +('notification_enabled', '1', 'global'), +('min_rule_level', '5', 'global'), +('debug_mode', '0', 'global'); + +-- Insertion des règles par défaut +INSERT INTO `glpi_plugin_siem_wazuh_rules` (`name`, `wazuh_rule_level`, `ticket_type`, `ticket_priority`, `is_active`) VALUES +('Alertes critiques (niveau 12+)', 12, 1, 5, 1), +('Alertes élevées (niveau 10-11)', 10, 1, 4, 1), +('Alertes moyennes (niveau 7-9)', 7, 1, 3, 1), +('Alertes faibles (niveau 5-6)', 5, 1, 2, 0); \ No newline at end of file diff --git a/sql/uninstall.sql b/sql/uninstall.sql new file mode 100644 index 0000000..0a918b8 --- /dev/null +++ b/sql/uninstall.sql @@ -0,0 +1,46 @@ +-- Plugin SIEM-Wazuh - Uninstallation script + +-- Désactivation des contraintes de clés étrangères temporairement +SET FOREIGN_KEY_CHECKS = 0; + +-- Suppression des tables dans l'ordre inverse des dépendances +DROP TABLE IF EXISTS `glpi_plugin_siem_wazuh_logs`; +DROP TABLE IF EXISTS `glpi_plugin_siem_wazuh_rules`; +DROP TABLE IF EXISTS `glpi_plugin_siem_wazuh_alerts`; +DROP TABLE IF EXISTS `glpi_plugin_siem_wazuh_profiles`; +DROP TABLE IF EXISTS `glpi_plugin_siem_wazuh_config`; +DROP TABLE IF EXISTS `glpi_plugin_siem_wazuh_servers`; + +-- Réactivation des contraintes de clés étrangères +SET FOREIGN_KEY_CHECKS = 1; + +-- Suppression des droits du plugin +DELETE FROM `glpi_profilerights` WHERE `name` LIKE 'plugin_siem_wazuh_%'; + +-- Suppression des tâches cron +DELETE FROM `glpi_crontasks` WHERE `itemtype` LIKE 'PluginSiemWazuh%'; + +-- Suppression des notifications +DELETE FROM `glpi_notifications` WHERE `itemtype` LIKE 'PluginSiemWazuh%'; +DELETE FROM `glpi_notificationtemplates` WHERE `itemtype` LIKE 'PluginSiemWazuh%'; + +-- Suppression des événements de log +DELETE FROM `glpi_events` WHERE `type` = 'siem-wazuh'; + +-- Suppression des configurations dans la table des configurations générales +DELETE FROM `glpi_configs` WHERE `name` LIKE 'plugin_siem_wazuh_%'; + +-- Suppression des préférences utilisateur liées au plugin +DELETE FROM `glpi_users_configs` WHERE `name` LIKE 'plugin_siem_wazuh_%'; + +-- Nettoyage des liens avec les tickets (optionnel - conserve les tickets créés) +-- UPDATE `glpi_tickets` SET `content` = REPLACE(`content`, '[WAZUH-ALERT]', '') WHERE `content` LIKE '%[WAZUH-ALERT]%'; + +-- Nettoyage des recherches sauvegardées liées au plugin +DELETE FROM `glpi_savedsearches` WHERE `type` LIKE 'PluginSiemWazuh%'; + +-- Suppression des affichages personnalisés +DELETE FROM `glpi_displaypreferences` WHERE `itemtype` LIKE 'PluginSiemWazuh%'; + +-- Suppression des options de recherche personnalisées +DELETE FROM `glpi_searchoptions` WHERE `itemtype` LIKE 'PluginSiemWazuh%'; \ No newline at end of file diff --git a/wazuhapi.class.php b/wazuhapi.class.php new file mode 100644 index 0000000..e69de29