{% extends 'base.html' %} {% block title %}Dashboard Vulnérabilités{% endblock %} {% block content %} {% set msg = request.query_params.get('msg', '') %} {% if is_running or msg == 'refresh_started' %}
Recalcul du dashboard en cours...
Calcul des KPI sur ~1300 assets via API Qualys (durée typique : 3 à 5 minutes). La page se rafraichira automatiquement.
{% elif msg == 'already_running' %}
Un calcul est deja en cours. Patiente puis rafraichis.
{% endif %}

Dashboard Vulnérabilités

{% if data.last_run %} Dernier calcul : {{ data.last_run.run_at.strftime('%Y-%m-%d %H:%M') }} ({{ data.last_run.asset_count }} assets, {{ data.last_run.duration_sec }}s) {% endif %} 📈 Historique
{% if not data.last_run %}

Aucun snapshot disponible. Lance un premier calcul.

{% else %} {% set g = data.global %}
Total
{{ g.total if g else 0 }}
serveurs
🔴 Critique (sev 5)
{{ g.critical if g else 0 }}
{{ ((g.critical * 100 / g.scanned) | round(1)) if g and g.scanned > 0 else 0 }}%
🟠 High (sev 4)
{{ g.high if g else 0 }}
{{ ((g.high * 100 / g.scanned) | round(1)) if g and g.scanned > 0 else 0 }}%
🟡 Medium (sev 3)
{{ g.medium if g else 0 }}
{{ ((g.medium * 100 / g.scanned) | round(1)) if g and g.scanned > 0 else 0 }}%
🟢 Sans vuln
{{ g.sain if g else 0 }}
{{ ((g.sain * 100 / g.scanned) | round(1)) if g and g.scanned > 0 else 0 }}%
⚫ Non scanné
{{ g.non_scanne if g else 0 }}
{{ ((g.non_scanne * 100 / g.total) | round(1)) if g and g.total > 0 else 0 }}% du total
{% macro pivot_table(title, items, tag_prefix='') %}

{{ title }}

{% for it in items|sort(attribute='vuln_total', reverse=true) %} {% endfor %}
Catégorie Total 🔴 Critique 🟠 High 🟡 Medium 🟢 Sain ⚫ Non scanné % vuln
{{ it.name }} {{ it.total }} {% if it.critical > 0 %}{{ it.critical }}{% else %}-{% endif %} {% if it.high > 0 %}{{ it.high }}{% else %}-{% endif %} {% if it.medium > 0 %}{{ it.medium }}{% else %}-{% endif %} {% if it.sain > 0 %}{{ it.sain }}{% else %}-{% endif %} {{ it.non_scanne if it.non_scanne > 0 else '-' }} {{ it.pct_vuln }}%
{% endmacro %} {{ pivot_table('Par environnement (ENV-*)', data.env, 'ENV-') }} {{ pivot_table('Par position réseau (POS-*)', data.pos, 'POS-') }} {{ pivot_table('Par OS (OS-*)', data.os, 'OS-') }} {{ pivot_table('Par domaine AD', data.domain, '') }}

Matrice Environnement × Position (% vuln)

{% for p in pos_list %}{% endfor %} {% for e in env_list %} {% for p in pos_list %} {% set cell = matrix.get((e,p)) %} {% if cell and cell.total > 0 %} {% else %} {% endif %} {% endfor %} {% endfor %}
ENV \ POS{{ p }}
{{ e }}
{{ cell.vuln_total }}/{{ cell.scanned }}
{{ cell.pct_vuln }}%
-

Format : vulnérables / scannés — couleur = % vulnérables (rouge ≥50%, orange ≥25%, jaune >0)

{% endif %} {% endblock %}