Sous le capot : Comment parser des fichiers XML de 500 Mo sans crasher ?

Découvrez l'anatomie du format .nessus et les défis d'ingénierie liés au parsing de fichiers XML massifs. Pourquoi les scripts classiques échouent (OOM), comment l'approche événementielle (Pull parsers) résout le problème, et comment surmonter les limites techniques d'Excel lors de la restitution.

Savinien. G

4/5/20266 min temps de lecture

Sous le capot : Comment parser des fichiers XML de 500 Mo sans crasher ?

La production d'un livrable d'audit de sécurité implique inévitablement l'extraction des données brutes générées par les scanners. Pour les utilisateurs des solutions Tenable, le format de prédilection est le fichier .nessus. Sous cette extension se cache en réalité un fichier XML standard.

Sur le papier, extraire des données d'un fichier XML est une opération triviale abordée dans les premiers cours d'informatique. Dans la réalité d'un audit de conformité ou d'un test d'intrusion à grande échelle, parser un fichier .nessus est un véritable défi d'ingénierie.

Cet article détaille l'anatomie de ces fichiers, les limites des méthodes d'extraction traditionnelles, et les architectures logicielles nécessaires pour traiter des volumes massifs de données sans saturer la mémoire (RAM), jusqu'à la génération d'un livrable Excel performant.

1. L'anatomie hiérarchique du format .nessus

Pour automatiser un export Nessus, il est indispensable de comprendre comment la donnée est structurée. Le format .nessus repose sur une hiérarchie stricte, encapsulée sous une balise racine <NessusClientData_v2>.

La structure typique se décline ainsi :

  • Report : Le conteneur principal du scan.

  • ReportHost : Créé pour chaque machine scannée. Il contient les métadonnées de l'hôte via un bloc <HostProperties> qui regroupe des balises <tag> nommées (ex: <tag name="operating-system">, <tag name="host-ip">).

  • ReportItem : L'élément central. Il y a un ReportItem par vulnérabilité ou point de contrôle identifié sur l'hôte.

C'est à l'intérieur de ce <ReportItem> que l'on retrouve les attributs clés (pluginID, severity, port, protocol) et les éléments enfants critiques pour l'auditeur : <description>, <solution>, <cve>, ou encore les données de conformité sous l'espace de noms cm:compliance-*.

Le problème de l'échelle : Si cette arborescence est lisible, elle devient rapidement gargantuesque. Un scan d'entreprise couvrant plusieurs milliers d'hôtes, avec des milliers de findings par hôte, génère un fichier XML pouvant aisément dépasser les 500 Mo, voire atteindre le gigaoctet.

2. Le piège de la mémoire : DOM vs Streaming

C'est face à ces fichiers massifs que les scripts "faits maison" (généralement en Python ou PowerShell) montrent leurs limites. La majorité des développeurs utilisent par défaut l'approche DOM (Document Object Model) pour parser du XML.

L'approche DOM : L'erreur Out Of Memory (OOM)

Le parsing DOM charge l'intégralité du document XML en mémoire vive pour construire un arbre d'objets. Si cette méthode permet de naviguer facilement dans l'arborescence, elle est fatale pour les performances. Un fichier XML parsé en DOM consomme généralement un volume de RAM représentant 5 à 10 fois la taille du fichier initial, selon l'implémentation (soit plusieurs gigaoctets pour un fichier de 500 Mo). C'est la cause principale des crashs par saturation mémoire.

L'approche événementielle : La consommation constante

Pour lire un gros fichier XML sans crash, l'ingénierie logicielle impose de passer à un parsing événementiel. Historiquement connue sous le nom de SAX, cette approche s'implémente aujourd'hui plus élégamment via des itérateurs d'événements (pull parsers, comme XmlReader en .NET).

Au lieu de construire un arbre, le parser lit le fichier séquentiellement, comme un flux. Il réagit à chaque ouverture de balise, lecture de texte, et fermeture de balise. L'empreinte mémoire reste ainsi parfaitement constante, que le fichier pèse 10 Mo ou 2 Go. L'information est lue, extraite si nécessaire, puis immédiatement purgée de la mémoire.

3. Extraction intelligente : Machines à états et cas limites

Le parsing événementiel pose un nouveau défi : comme on ne stocke pas l'arbre en mémoire, le programme perd la notion de "contexte". Lorsqu'il lit une balise <description>, il doit savoir s'il se trouve dans les métadonnées globales ou dans une vulnérabilité spécifique.

La machine à états

La solution technique repose sur l'implémentation d'une machine à états finis. Le processus transite dynamiquement au fil de la lecture :

  1. État initial → Attente de la racine.

  2. État Rapport → Attente d'un ReportHost.

  3. État Hôte → Attente d'un ReportItem.

  4. État Finding → Capture des données spécifiques.

Cette approche permet une capture sélective. Le parser ignore le contenu des balises non pertinentes, évitant ainsi de bufferiser du texte inutile, ce qui accélère drastiquement l'exécution.

Gérer la complexité et les subtilités du format

Convertir un fichier Nessus brut implique également de traiter à la volée des irrégularités très spécifiques :

  • Détection de scans mixtes : Un même fichier peut contenir à la fois des vulnérabilités classiques et des audits de conformité (présence de balises cm:compliance-*). La machine à états doit pouvoir les identifier dynamiquement pour les router vers les bonnes structures de données.

  • Extraction intelligente de la conformité : Au lieu d'exporter le nom brut du contrôle (ex: "18.2.2 Ensure 'Do not allow password expiration'..."), le parser applique des expressions régulières pour isoler proprement la référence normative ("18.2.2") de la description, facilitant ainsi la lecture dans Excel.

  • Sections CDATA : Les blocs CDATA sont massivement utilisés dans Nessus pour inclure du texte brut contenant des caractères réservés XML (comme <, >, &) sans avoir à les échapper. Le parser doit les traiter et les concaténer proprement.

  • Éléments multiples : Un seul ReportItem peut contenir plusieurs balises <cve>. Le code doit les agréger au lieu d'écraser la valeur avec la dernière itération lue.

4. Le mur de la restitution : Parser c'est bien, formater c'est une autre histoire

C'est ici que de nombreux outils de conversion se cassent les dents. Avoir un moteur capable de parser plusieurs gigaoctets de données XML dans un temps raisonnable est une prouesse technique, mais elle est inutile si la finalité n'est pas maîtrisée.

Le but n'est pas de recracher la donnée brute, mais de générer un rapport Excel propre. Et c'est là qu'intervient le goulot d'étranglement de Microsoft Excel.

Techniquement, une feuille de calcul Excel est limitée à 1 048 576 lignes. Dans les faits, bien avant d'atteindre cette limite physique, injecter des centaines de milliers de lignes contenant de lourds blocs de texte (les descriptions et solutions des vulnérabilités) fera purement et simplement figer ou crasher l'application Excel lors de son ouverture par le client.

Pour qu'un export de vulnérabilités soit exploitable, il ne suffit pas de le convertir en CSV. Il faut :

  • Agréger et dédupliquer intelligemment les résultats.

  • Générer des tableaux de bord interactifs (répartition par sévérité, Top 10).

  • Injecter des formules dynamiques pour que les graphiques se mettent à jour si l'auditeur requalifie un faux positif.

  • Appliquer des styles conditionnels professionnels.

5. De la théorie à la pratique : L'architecture hybride d'un utilitaire dédié

Développer un pipeline de parsing événementiel, fusionner des données, et formater des fichiers .xlsx complexes nativement demande un effort d'ingénierie qui s'éloigne du cœur de métier du pentester. Maintenir des scripts maison pour générer de beaux Excel dynamiques s'avère rapidement être un cauchemar de dépendances.

C'est ici qu'une application desktop spécialisée comme NtE (Nessus To Excel) prend tout son sens. Sa conception repose sur un choix architectural strict : la séparation des responsabilités via une stack hybride.

  • Une Interface isolée (Flutter) : L'UI est non seulement fluide, mais l'architecture repose sur des isolates (threads séparés). Le parsing lourd tourne en arrière-plan, garantissant que l'interface ne gèle jamais, même lors de l'ingestion d'un fichier de 2 Go.

  • Traitement parallèle et déduplication : L'outil permet le glisser-déposer de multiples fichiers .nessus simultanément. Ils sont parsés en parallèle, et les hôtes dupliqués apparaissant dans plusieurs fichiers sont automatiquement fusionnés pour fournir une vue consolidée au client.

  • Le moteur de traitement (C# / .NET 8) : C'est le cœur du réacteur. Le passage à un langage fortement typé et compilé comme C# a été dicté par une nécessité technique incontournable : l'accès à un écosystème de librairies d'entreprise (Enterprise-grade libraries) extrêmement puissantes. Ces librairies permettent de construire et de manipuler des fichiers .xlsx nativement, d'injecter des formules dynamiques et de générer des dashboards sans avoir besoin d'instancier des processus COM invisibles, et sans même nécessiter qu'Excel soit installé sur la machine de génération.

En définitive, si parser un fichier Nessus semble simple en apparence, le faire de manière résiliente sans saturer la RAM, consolider des scans multiples, et transformer cette masse d'informations en un livrable Excel dynamique justifie amplement l'abandon des scripts artisanaux au profit d'outils d'ingénierie dédiés.