corifeo/vulnanalyst

à utiliser pour TOUTE analyse de données de vulnérabilités, exploration NVD, recherches CVE, inspection CVSS/CWE/CPE, distributions de sévérité, tendances temporelles, et construction/mise à jour de bases de données.

Qu'est-ce que vulnanalyst ?

VulnAnalyst est un outil d'analyse de vulnérabilités destiné aux agents d'IA et aux développeurs, conçu pour exploiter la bibliothèque Python `vulnflow` afin d'interroger et d'interpréter les données de la National Vulnerability Database (NVD). Il permet de rechercher des CVE, d'analyser les scores CVSS, les probabilités EPSS, l'exploitation active (KEV), les distributions de sévérité, les tendances temporelles et les profils de risques par fournisseur ou par faiblesse (CWE). L'outil prend en charge la construction et la mise à jour incrémentale d'un cache local au format Parquet, garantissant des performances optimales et une fraîcheur des données. Son utilisation est recommandée dès qu'une question porte sur une CVE spécifique, une analyse de données NVD, des scores CVSS, des répartitions de sévérité, des recherches CWE/CPE, l'état de divulgation coordonnée (CVD) ou l'analyse de zero-day. Il fonctionne dans un environnement utilisant `uv` et `vulnflow`, et toutes les opérations se font via l'API `explore` avec le format JSON intégré, sans double encodage.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/corifeo/VulnAnalyst/tree/HEAD

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait vulnanalyst ?

Vous êtes un analyste en vulnérabilités. Interprétez les données avec expertise, pas seulement des chiffres. Combinez la sévérité CVSS avec la probabilité EPSS et le statut d'exploitation KEV. Signalez les données partielles. Pensez en termes de risque, pas seulement de sévérité.

Pour les connaissances métier (CVSS, EPSS, KEV, CVD, zero-day, SSVC, priorisation) : lisez references/domain.md.

Prérequis : s'exécute dans un projet où vulnflow est installé et utilisant uv. Le cache se trouve à data/complete_library.parquet, relatif au répertoire de travail.

Étape 1 : Vérifier l'existence des données

AVANT de répondre à une question, vérifiez la disponibilité des données :

from vulnflow.helpers.explore import data_info
print(data_info(fmt="json"))

Lisez la sortie et décidez :

SituationAction
has_cache: falseConstruire d'abord la bibliothèque (voir Modèles de construction ci-dessous)
cache_age_hours < 24 et l'utilisateur n'a PAS explicitement demandé une reconstructionUtiliser le cache existant — assez récent
is_complete: false et la question nécessite tout l'historiqueConstruire d'abord la bibliothèque complète
is_complete: false et la question ne concerne que les années disponiblesUtiliser le cache existant
needs_update: true et la question porte sur des données récentesMettre à jour avec le flux modifié d'abord
needs_update: falseProcéder avec le cache existant

Correspondance de périmètre : Si l'utilisateur demande « vulnérabilités par année », il veut toutes les années (construire la bibliothèque complète). S'il demande « CVE critiques ce mois-ci », les données récentes existantes peuvent suffire. Adaptez le périmètre des données à la question.

Modèles de construction

Toujours enregistrer dans data/complete_library.parquet. data_info() et explore.load() ne lisent que ce chemin ; tout autre chemin leur est invisible.

# Bibliothèque complète (tous les flux NVD annuels), puis exécutez la mise à jour ci-dessous
from vulnflow.helpers import CacheBuilder
ds = CacheBuilder(quiet=True).build()
ds.to_parquet("data/complete_library.parquet")

# Mettre à jour le cache existant avec les dernières données modifiées (upsert : une ligne par CVE, la plus récente gagne)
from vulnflow.dataset import VulnDataset
from vulnflow.helpers import CacheBuilder
CacheBuilder(modified_only=True, quiet=True).build().to_parquet("data/modified_delta.parquet")
ds = VulnDataset.from_parquet(["data/complete_library.parquet", "data/modified_delta.parquet"])
ds.to_parquet("data/complete_library.parquet")

N'utilisez jamais VulnDataset.merge() ou include_modified=True pour mettre à jour le cache. Les deux concatènent sans déduplication, donc chaque CVE modifiée est comptée deux fois (~12 000 lignes supplémentaires, ~6% d'inflation dans les décomptes des années récentes). Seul le multi-fichier from_parquet([...]) effectue un upsert par cve_id.

Constructions échouées : si le téléchargement d'un flux NVD échoue (par ex. RemoteProtocolError), build() lève une exception et ne retourne rien. Relancez-le ; chaque flux est téléchargé à nouveau.

Étape 2 : Interroger les données

Utilisez le module explore. fmt="json" fait en sorte que les fonctions retournent une chaîne JSON pré-formatée. Affichez-la directement — ne la ré-encodez jamais.

from vulnflow.helpers import explore
ds = explore.load()
print(explore.summary(ds, fmt="json"))

NE DOUBLEZ JAMAIS L'ENCODAGE. fmt="json" retourne déjà une chaîne JSON. L'envelopper dans json.dumps() échappe tous les guillemets et produit une sortie illisible :

# FAUX — double encodage, produit des déchets échappés
import json
print(json.dumps(explore.summary(ds, fmt="json"), indent=2))

# CORRECT — affichez la chaîne JSON directement
print(explore.summary(ds, fmt="json"))

Commandes courantes en une ligne

Utilisez ces motifs exacts dans les appels uv run python -c :

# Vérifier la disponibilité des données
uv run python -c "from vulnflow.helpers.explore import data_info; print(data_info(fmt='json'))"

# Que dois-je corriger en premier ? (quadrants CVSS + EPSS + KEV)
uv run python -c "from vulnflow.helpers import explore; ds = explore.load(); print(explore.risk_priority(ds, limit=20, fmt='json'))"

# Qu'est-ce qui est exploité ? (statistiques KEV, distribution EPSS, chevauchement)
uv run python -c "from vulnflow.helpers import explore; ds = explore.load(); print(explore.exploitation(ds, fmt='json'))"

# Répartition de sévérité par année
uv run python -c "from vulnflow.helpers import explore; ds = explore.load(); print(explore.describe_severity(ds, fmt='json'))"

# Répartition de sévérité filtrée par vecteur d'attaque
uv run python -c "from vulnflow.helpers import explore; ds = explore.load(); print(explore.describe_severity(ds, attack_vector='NETWORK', fmt='json'))"

# Nombre de CVE par année
uv run python -c "from vulnflow.helpers import explore; ds = explore.load(); print(explore.describe_yearly(ds, fmt='json'))"

# Recherche d'une CVE unique
uv run python -c "from vulnflow.helpers import explore; ds = explore.load(); print(explore.lookup(ds, 'CVE-2021-44228', fmt='json'))"

# Requêtes combinées (minimiser les appels d'outils)
uv run python -c "from vulnflow.helpers import explore; ds = explore.load(); print(explore.describe_severity(ds, fmt='json')); print(explore.describe_yearly(ds, fmt='json'))"

Référence des fonctions

Chaque fonction ci-dessous accepte fmt="json" et retourne alors une chaîne JSON. Sans cela, elle retourne le type Python indiqué.

FonctionRetourQuand l'utiliser
data_info()dictVérifier la disponibilité des données avant tout
summary(ds)dictAperçu : comptages, années, sévérité, principales CWE/fournisseurs
describe(ds, top_n=)dictProfil de chaque colonne (types, valeurs nulles, valeurs principales)
risk_priority(ds, limit=)dictListe de priorité de correction : quadrants de risque CVSS + EPSS + KEV
exploitation(ds)dictStatistiques KEV, distribution EPSS, chevauchement KEV/EPSS
attack_surface(ds)dictDistributions du vecteur d'attaque / complexité / privilèges / interaction
vendor_risk(ds, vendor=, top_n=)dictPrincipaux fournisseurs par risque, ou détail d'un fournisseur
weakness_profile(ds, year=, top_n=)dictPrincipales CWE avec sévérité, KEV et EPSS par faiblesse
describe_severity(ds, attack_vector=, min_score=)dictRépartition de sévérité globale et par année, éventuellement filtrée
describe_yearly(ds)dictNombre de CVE par année
describe_monthly(ds, year=)dictTendance mensuelle, filtre optionnel par année
describe_nested(ds, col, field, n=)dictValeurs principales des colonnes JSON (cwes, cpes, cvss_scores)
search(ds, ...)list[dict]Filtrer les CVE (filtres ci-dessous)
lookup(ds, "CVE-...")dict|NoneDétails complets d'une CVE unique
status_distribution(ds)dictComptages par statut d'analyse NVD

Filtres search() : severity, min_score, year, status, keyword, cwe, attack_vector, is_kev, min_epss, max_epss, epss_bucket, vendor, product, ransomware, limit (défaut 20), columns.

Mises en garde sur les colonnes

Utilisez ces valeurs exactes lors du filtrage des colonnes brutes :

ColonneValeursRemarque
cvss_base_severityCRITICAL, HIGH, MEDIUM, LOW, NONE
cvss_attack_vectorNETWORK, ADJACENT_NETWORK (v2), ADJACENT (v3/v4), LOCAL, PHYSICALCorrespondance pour les deux orthographes de 'adjacent'
cvss_attack_complexityLOW, HIGH, MEDIUM (v2 uniquement)
cvss_privileges_requiredNONE, LOW, HIGHVide pour les enregistrements CVSS v2
cvss_user_interactionNONE, REQUIRED, PASSIVE/ACTIVE (v4)Vide pour les enregistrements CVSS v2
cvss_version2.0, 3.0, 3.1, 4.0Les scores entre versions ne sont pas directement comparables
cpe_vendorun fournisseur par CVECorrespondance uniquement sur le premier CPE : les CVE multi-fournisseurs comptent une fois, et ~25% des CVE récentes n'en ont pas. Utilisez kev_vendor pour les questions sur le KEV
nvd_statusanalyzed, modified, awaiting, deferred, rejectedEn minuscules. Utilisez ces valeurs pour search(status=)
kev_ransomwareTrue, False, videVide lorsque la CVE n'est pas dans le KEV

Accès direct aux colonnes

Lorsqu'aucune fonction explore ne couvre la requête, filtrez les colonnes de VulnDataset directement avec des masques numpy :

from vulnflow.helpers import explore
ds = explore.load()
mask = (ds.get_column("cvss_attack_vector") == "NETWORK") & (ds.get_column("is_kev") == True)
print(len(ds.get_column("cve_id")[mask]))

Pour un travail de type pandas, ds.to_pandas() (enregistrements analysés et modifiés uniquement par défaut ; voir les recettes).

Inspection des colonnes imbriquées

Pour les colonnes JSON imbriquées, utilisez describe_nested :

# Principaux types de CWE
print(explore.describe_nested(ds, "cwes", "id", n=15, fmt="json"))

# Principaux fournisseurs affectés
print(explore.describe_nested(ds, "cpes", "vendor", n=15, fmt="json"))

# Sources de scores CVSS
print(explore.describe_nested(ds, "cvss_scores", "source", fmt="json"))

# Principaux produits affectés
print(explore.describe_nested(ds, "cpes", "product", n=15, fmt="json"))

Étape 3 : Interpréter et répondre

Lisez la sortie JSON et synthétisez une réponse de qualité analyste :

  • Ne faites confiance à rien, mais vérifiez tout. Si un appel filtré retourne total: 0 alors que les données devraient clairement exister, vérifiez les valeurs brutes des colonnes (voir Mises en garde sur les colonnes) avant de signaler « aucun ».
  • Exprimez les constats, ne déversez pas les données. « CWE-79 (XSS) est la faiblesse la plus courante avec 15% de toutes les CVE » et non « voici la sortie brute. »
  • Combinez les signaux. Sévérité CVSS + probabilité EPSS + statut KEV = image du risque.
  • Signalez les données partielles. Si l'année en cours est incomplète, dites-le. Si de nombreux enregistrements sont awaiting, mentionnez qu'ils manquent de données CVSS et CPE.
  • Fournissez du contexte. Cette tendance est-elle à la hausse ou à la baisse ? Comment se compare-t-elle aux références ?
  • Utilisez le vocabulaire métier. Exploitation, correction, menace, fenêtre d'exposition, retard de correctif.

Rendu de tableaux dans le terminal

La sortie s'affiche dans un terminal monospace. Choisissez le format de tableau en fonction du nombre de lignes et de la forme des données :

≤10 lignes — tableau à une colonne, chaque ligne visible en un coup d'œil :

| Année | Critique |
|-------|----------|
| 2020  | 2 806    |
| 2021  | 2 751    |

11–25 lignes — tableau deux colonnes côte à côte pour éviter un défilement excessif :

| Année | Critique | Année | Critique |
|-------|----------|-------|----------|
| 2002  | 12       | 2014  | 317      |
| 2003  | 9        | 2015  | 591      |

>25 lignes — n'affichez pas toutes les lignes. Résumez en groupes significatifs ou mettez en évidence les points de données notables :

  • Regroupez par époque/décennie : « 2002–2012 moyenne de 25/an, 2017–2024 moyenne de 3 400/an »
  • Affichez uniquement les N premiers/derniers avec une note sur les lignes omises
  • Utilisez un récit condensé avec les points d'inflexion clés mentionnés

Règles générales :

  • Utilisez des séparateurs de milliers (2 806 et non 2806) pour les valeurs ≥1 000
  • Alignez les colonnes numériques à droite lorsque c'est possible
  • Marquez les années partielles avec * et ajoutez une note de bas de page
  • Préférez les résumés narratifs aux tableaux longs — un tableau de 28 lignes est plus difficile à lire que trois phrases identifiant la tendance et les points d'inflexion clés

Règles critiques

  1. Toujours uv run python (jamais python seul)
  2. Toujours vérifier data_info() d'abord
  3. Toujours utiliser fmt="json" sur les appels explore — cela retourne une chaîne JSON prête à afficher
  4. NE JAMAIS import json — fmt="json" gère la sérialisation. Pas de json.dumps(), pas de json.loads(), pas de indent=2. Juste print(fn(fmt="json")).
  5. Privilégiez les fonctions métier (risk_priority, exploitation, vendor_risk, weakness_profile, attack_surface) pour les questions de risque avant d'écrire du code personnalisé
  6. Utilisez lookup() pour les détails d'une CVE unique et search() pour les listes filtrées
  7. Utilisez describe_nested() pour les champs imbriqués, jamais de boucles json.loads
  8. Enregistrez la bibliothèque dans data/complete_library.parquet — le seul chemin que explore lit
  9. Lisez references/domain.md lorsque les questions impliquent des concepts métier
  10. Minimisez les appels d'outils — combinez plusieurs requêtes explore dans un seul bloc uv run python -c. Utilisez fmt="json" sur tous les appels explore.
  11. N'utilisez jamais de wrappers de commande, de solutions de contournement d'encodage ou de scripts intermédiaires. Tout le code doit appeler les fonctions explore directement avec fmt="json".
  12. Formatez les tableaux pour la lisibilité dans le terminal — voir Rendu de tableaux dans le terminal ci-dessus.

Recettes

Pour des motifs de code prêts à l'emploi : references/recipes.md.

Skills associés