Qualys: - Recherche API temps réel + cache 24h base locale - Tags: liste DYN/STAT, mapping V3 (DOM-*, TYP-*, APP-*), nb assets cliquable - CRUD tags: créer STAT, supprimer, resync API - Détail asset: infos + décodage nomenclature V3 + tags assignés - Ajout/retrait tag unitaire avec autocomplete filtrable - Bulk add/remove tag en masse avec dropdown filtrable - Tags retirer: charge dynamiquement les STAT assignés aux assets sélectionnés - Resync assets sélectionnés + retour même recherche Contacts: - 50 contacts importés avec 93 scopes (domaine/app/serveur/zone par env) - 13 rôles (responsable_domaine, ra_prod, ra_recette, referent_technique...) - Recherche par nom/email/serveur (affiche contacts liés) - CRUD complet: éditer, scopes, activer/désactiver, supprimer - Serveurs liés calculés dynamiquement depuis les scopes Audit: - Restructuré: Audit général + sous-menu Spécifique - Dernier audit global affiché avec date - Lancer audit général avec exclusions (domaines/zones) et parallélisme - KPIs Qualys KO et S1 KO cliquables - Export CSV Serveurs: - Actions groupées bulk (domaine, env, tier, état, owner, licence) - Dashboard: KPI EOL ajouté - Filtre état: EOL + en décommissionnement ajoutés - 138 serveurs EOL importés depuis Qualys (owner=na, hors périmètre) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
121 lines
6.1 KiB
HTML
121 lines
6.1 KiB
HTML
{% extends 'base.html' %}
|
|
{% block title %}Résultats audit temps réel{% endblock %}
|
|
{% block content %}
|
|
<div class="flex justify-between items-center mb-4">
|
|
<div>
|
|
<a href="/audit" class="text-xs text-gray-500 hover:text-gray-300">← Retour audit</a>
|
|
<h2 class="text-xl font-bold text-cyber-accent">Résultats audit temps réel</h2>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<form method="POST" action="/audit/realtime/save">
|
|
<button class="btn-primary px-4 py-2 text-sm" onclick="return confirm('Mettre à jour la base avec ces résultats ?')">Mettre à jour la base</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats -->
|
|
<div class="grid grid-cols-3 gap-3 mb-4">
|
|
<div class="card p-3 text-center">
|
|
<div class="text-2xl font-bold text-cyber-accent">{{ total }}</div>
|
|
<div class="text-xs text-gray-500">Total</div>
|
|
</div>
|
|
<div class="card p-3 text-center">
|
|
<div class="text-2xl font-bold text-cyber-green">{{ ok }}</div>
|
|
<div class="text-xs text-gray-500">Connectés</div>
|
|
</div>
|
|
<div class="card p-3 text-center">
|
|
<div class="text-2xl font-bold text-cyber-red">{{ failed }}</div>
|
|
<div class="text-xs text-gray-500">Échoués</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Résultats -->
|
|
<div class="card overflow-x-auto">
|
|
<table class="w-full table-cyber text-xs">
|
|
<thead><tr>
|
|
<th class="text-left p-2">Hostname</th>
|
|
<th class="p-2">Statut</th>
|
|
<th class="p-2">FQDN résolu</th>
|
|
<th class="p-2">OS</th>
|
|
<th class="p-2">Kernel</th>
|
|
<th class="p-2">Disque</th>
|
|
<th class="p-2">Qualys</th>
|
|
<th class="p-2">S1</th>
|
|
<th class="p-2">Services</th>
|
|
<th class="p-2">Sans auto</th>
|
|
<th class="p-2">BDD</th>
|
|
<th class="p-2">Containers</th>
|
|
</tr></thead>
|
|
<tbody>
|
|
{% for r in results %}
|
|
<tr class="{% if r.status != 'OK' %}bg-red-900/10{% elif r.disk_alert %}bg-yellow-900/10{% endif %}">
|
|
<td class="p-2 font-mono text-cyber-accent">{{ r.hostname }}</td>
|
|
<td class="p-2 text-center"><span class="badge {% if r.status == 'OK' %}badge-green{% else %}badge-red{% endif %}">{{ r.status[:12] }}</span></td>
|
|
<td class="p-2 text-center text-gray-400">{{ r.resolved_fqdn or '-' }}</td>
|
|
<td class="p-2 text-center text-gray-400" title="{{ r.os_release or '' }}">{{ (r.os_release or '-')[:25] }}</td>
|
|
<td class="p-2 text-center text-gray-400">{{ (r.kernel or '-')[:20] }}</td>
|
|
<td class="p-2 text-center">
|
|
{% if r.disk_alert %}<span class="badge badge-red">ALERTE</span>
|
|
{% elif r.status == 'OK' %}<span class="text-cyber-green">OK</span>
|
|
{% else %}-{% endif %}
|
|
</td>
|
|
<td class="p-2 text-center">{% if r.qualys_active %}<span class="text-cyber-green">OK</span>{% elif r.status == 'OK' %}<span class="text-cyber-red">KO</span>{% else %}-{% endif %}</td>
|
|
<td class="p-2 text-center">{% if r.sentinelone_active %}<span class="text-cyber-green">OK</span>{% elif r.status == 'OK' %}<span class="text-cyber-red">KO</span>{% else %}-{% endif %}</td>
|
|
<td class="p-2 text-center text-gray-400" title="{{ r.services_running or '' }}">{% if r.services_running %}{{ r.services_running.split('\n')|length }}{% else %}-{% endif %}</td>
|
|
<td class="p-2 text-center">{% if r.running_not_enabled and r.running_not_enabled != 'none' %}<span class="text-cyber-yellow">{{ r.running_not_enabled.split('\n')|length }}</span>{% else %}-{% endif %}</td>
|
|
<td class="p-2 text-center text-gray-400">{{ (r.db_detect or '-')[:15] }}</td>
|
|
<td class="p-2 text-center text-gray-400">{{ (r.containers or '-')[:20] }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Détail par serveur -->
|
|
{% for r in results %}
|
|
{% if r.status == 'OK' %}
|
|
<details class="card mt-2">
|
|
<summary class="p-3 cursor-pointer hover:bg-cyber-border/20 font-mono text-sm text-cyber-accent">{{ r.hostname }} — {{ r.resolved_fqdn or '' }}</summary>
|
|
<div class="p-4 grid grid-cols-2 gap-4 text-xs">
|
|
<div>
|
|
<h4 class="text-cyber-accent font-bold mb-1">Espace disque</h4>
|
|
<pre class="bg-cyber-bg p-2 rounded text-gray-400 overflow-x-auto">{{ r.disk_space or 'N/A' }}</pre>
|
|
</div>
|
|
<div>
|
|
<h4 class="text-cyber-accent font-bold mb-1">Applications</h4>
|
|
<pre class="bg-cyber-bg p-2 rounded text-gray-400 overflow-x-auto">{{ r.apps_installed or 'N/A' }}</pre>
|
|
</div>
|
|
<div>
|
|
<h4 class="text-cyber-accent font-bold mb-1">Services actifs</h4>
|
|
<pre class="bg-cyber-bg p-2 rounded text-gray-400 overflow-x-auto" style="max-height:150px">{{ r.services_running or 'N/A' }}</pre>
|
|
</div>
|
|
<div>
|
|
<h4 class="text-cyber-yellow font-bold mb-1">Sans auto-start</h4>
|
|
<pre class="bg-cyber-bg p-2 rounded text-cyber-yellow overflow-x-auto">{{ r.running_not_enabled or 'Aucun' }}</pre>
|
|
</div>
|
|
<div>
|
|
<h4 class="text-cyber-accent font-bold mb-1">Ports</h4>
|
|
<pre class="bg-cyber-bg p-2 rounded text-gray-400 overflow-x-auto">{{ r.listening_ports or 'N/A' }}</pre>
|
|
</div>
|
|
<div>
|
|
<h4 class="text-cyber-accent font-bold mb-1">Agents</h4>
|
|
<pre class="bg-cyber-bg p-2 rounded text-gray-400">{{ r.agents or 'N/A' }}</pre>
|
|
</div>
|
|
{% if r.containers and r.containers != 'none' %}
|
|
<div class="col-span-2">
|
|
<h4 class="text-cyber-accent font-bold mb-1">Containers</h4>
|
|
<pre class="bg-cyber-bg p-2 rounded text-gray-400">{{ r.containers }}</pre>
|
|
</div>
|
|
{% endif %}
|
|
{% if r.failed_services and r.failed_services != 'none' %}
|
|
<div class="col-span-2">
|
|
<h4 class="text-cyber-red font-bold mb-1">Services en échec</h4>
|
|
<pre class="bg-red-900/20 p-2 rounded text-cyber-red">{{ r.failed_services }}</pre>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</details>
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% endblock %}
|