
Cadre d'évaluation pour les agents de test d'intrusion IA qui mesure la découverte validée de vulnérabilités à l'aide de la correspondance sémantique basée sur LLM, de la résolution bipartite et de l'analyse cumulative sur des cibles réelles.
Les agents de pentest basés sur l'IA sont de plus en plus crédibles en tant que systèmes de sécurité offensive, mais les benchmarks actuels n’offrent encore qu’un guidage limité sur les systèmes qui obtiendront les meilleurs résultats sur des cibles réelles. La plupart des évaluations existantes évaluent et optimisent des objectifs prédéfinis tels que la capture de flag, l’exécution de code à distance, la reproduction d’exploits ou la similarité des trajectoires, dans des environnements simplifiés ou restreints. Ces benchmarks sont utiles pour mesurer des capacités délimitées, mais ils ne saisissent pas correctement la complexité, l’exploration libre et la prise de décision stratégique requises dans un pentest réaliste. Nous présentons un cadre d’évaluation pratique qui déplace l’évaluation de l’accomplissement de tâches vers la découverte validée de vulnérabilités, permettant une évaluation sur des cibles suffisamment complexes couvrant plusieurs surfaces d’attaque et classes de vulnérabilités. Le cadre combine une vérité terrain structurée à une correspondance sémantique basée sur LLM pour identifier les vulnérabilités, une résolution bipartie pour noter les constatations dans une ambiguïté réaliste, une maintenance continue de la vérité terrain, une évaluation répétée et cumulative d’agents stochastiques, des métriques d’efficacité, ainsi qu’une sélection de suite réduite pour une expérimentation durable. Cette méthodologie fait évoluer l’état de l’art en permettant une comparaison plus réaliste et plus instructive sur le plan opérationnel des agents de pentest IA. Pour garantir la reproductibilité, nous publions également une vérité terrain annotée par des experts ainsi que le code du protocole d’évaluation proposé.
Pipeline d’évaluation pour les outils de test de sécurité. Compare les constatations de l’outil aux jeux de données de vérité terrain à l’aide d’une correspondance basée sur LLM et produit les métriques précision, rappel, F1 et F0.5.
poetry install
Nécessite Python 3.11+ et Poetry installés.
# 1. Set your LLM API key
export OPENAI_API_KEY="..."
# 2. Run evaluation
ethibench evaluate ./my_experiment --dataset path/to/dataset.yaml
# 3. View results
cat ./my_experiment/evaluation_outputs/summary.md
ethibench evaluateExécute le pipeline d’évaluation complet sur un répertoire d’expérience.
ethibench evaluate <experiment_dir> --dataset <dataset.yaml> [options]
# Batch: evaluate all experiments in a folder
ethibench evaluate --parent-dir final_experiments/ --dataset <dataset.yaml>
# Force re-evaluation (ignore cached artifacts)
ethibench evaluate <experiment_dir> --dataset <dataset.yaml> --force
Arguments :
experiment_dir — (facultatif) Répertoire contenant les sous-répertoires de cibles (ou des sous-répertoires run_* contenant chacun des sous-répertoires de cibles). Peut être omis lors de l’utilisation de --parent-dir.Options :
--dataset, -d — (obligatoire) Chemin vers le fichier YAML du jeu de données.--gt-dir, -g — Répertoire de vérité terrain. Par défaut gt/ à côté du fichier YAML du jeu de données.--output-dir, -o — Répertoire de sortie. Par défaut evaluation_outputs/ dans le répertoire d’expérience. Ignoré en mode batch.--replicates, -n — Nombre de répliques de correspondance LLM (défaut : 1).--force, -f — Réexécute toutes les étapes, en ignorant les artefacts en cache. Par défaut, les résultats intermédiaires existants (correspondances brutes, correspondances biparties, métriques) sont réutilisés.--parent-dir, -p — Dossier parent contenant plusieurs répertoires d’expériences à évaluer en batch. Tous les sous-répertoires immédiats sont traités comme des expériences.Ce qu’il fait :
target_id), charge chaque findings.jsonl, attribue subset_name depuis le YAML du jeu de données.metrics.json par cible s’il existe, agrège coût/tokens/durée.evaluation_outputs/plots/.evaluation_outputs/summary.md.ethibench analyzeExécute les outils d’analyse sur des sorties d’évaluation existantes.
ethibench analyze <experiment_dir> --dataset <dataset.yaml> [options]
# Batch: analyze all experiments and produce aggregated results
ethibench analyze --parent-dir final_experiments/ --dataset <dataset.yaml>
Arguments :
experiment_dir — (facultatif) Répertoire d’expérience à analyser. Peut être omis lors de l’utilisation de --parent-dir.Options :
--dataset, -d — (obligatoire) Chemin vers le fichier YAML du jeu de données.--gt-dir, -g — Répertoire de vérité terrain. Par défaut gt/ à côté du fichier YAML du jeu de données.--output-dir, -o — Répertoire des sorties d’évaluation. Par défaut evaluation_outputs/ dans le répertoire d’expérience.--parent-dir, -p — Dossier parent contenant plusieurs répertoires d’expériences à analyser en batch. Produit une analyse par expérience ainsi que des résultats agrégés.Sorties par expérience (evaluation_outputs/analysis/) :
duplicates.json — constatations qui correspondaient lors de la correspondance brute mais qui ont été supprimées par l’optimisation bipartie.unmatched.json — constatations sans correspondance avec la vérité terrain (faux positifs).statistics.json — statistiques de couverture GT, distribution des constatations par GT.Sorties agrégées (uniquement avec --parent-dir, dans <parent-dir>/aggregated_analysis/) :
all_duplicates.jsonl — toutes les constatations en double dans toutes les expériences (JSONL, objets de constatation complets avec le champ experiment).all_false_positives.jsonl — toutes les constatations sans correspondance / faux positifs dans toutes les expériences (format JSONL).gt_statistics_avg.json — couverture GT moyenne par sous-ensemble, plus résumé de couverture par expérience.ethibench compareCompare les résultats d’évaluation de plusieurs expériences, en produisant des graphiques côte à côte et un rapport récapitulatif. Chaque expérience doit déjà avoir des sorties d’évaluation (exécutez ethibench evaluate d’abord). Les libellés correspondent toujours aux noms de répertoires.
# Explicit experiment directories
ethibench compare exp-gpt4o/ exp-claude/ --output-dir comparison/
# Auto-discover all experiments under a parent folder
ethibench compare --parent-dir all-experiments/ --output-dir comparison/
# Mix: explicit dirs + auto-discovery
ethibench compare exp-extra/ --parent-dir all-experiments/ --output-dir comparison/
Arguments :
experiment_dirs — (facultatif) Un ou plusieurs répertoires d’expériences à inclure explicitement.Options :
--output-dir, -o — (obligatoire) Répertoire de sortie pour les résultats de comparaison.--parent-dir, -p — Dossier parent pour la découverte automatique des expériences. Tout sous-répertoire immédiat contenant un dossier evaluation_outputs/ est inclus, trié par ordre alphabétique. Peut être combiné avec des experiment_dirs explicites.Sorties (dans --output-dir) :
comparison.json — données de comparaison brutes pour toutes les expériences.plots/ — graphiques PNG côte à côte.comparison.md — résumé Markdown.pairwise_comparison.md — comparaison statistique par paires A/B (top 4 des expériences selon le F1).pairwise_comparison.tex — version LaTeX du tableau de comparaison par paires.cumulative-analysis/ — (si des données cumulatives existent) analyse delta comparant le F1 moyen au F1 cumulatif, plus des graphiques de comparaison cumulative.findings.jsonl)Un objet JSON par ligne. Champ obligatoire : title, description. Facultatifs : url, cwe, severity, score, steps, evidence, metadata, etc.
Chaque findings.jsonl se trouve dans un répertoire de cible — le nom du répertoire détermine la cible à laquelle appartiennent les constatations.
{"title": "SQL Injection in Login", "description": "User input not sanitized", "cwe": "89"}
*_gt.jsonl)Un objet JSON par ligne.
{"id": "gt-001", "name": "SQL Injection", "subset_name": "MyApp", "target_id": "app", "category": "CWE-89", "description": "Database query vulnerability", "cvss": 9.8}
- subset: "MyApp"
weight: 1.0
targets:
- target_id: "app"
Le target_id doit correspondre au nom du répertoire sous chaque dossier d’exécution. Lors de l’évaluation, ethibench parcourt le répertoire d’exécution à la recherche de sous-répertoires correspondant aux valeurs target_id connues, charge leurs findings.jsonl et les attribue au sous-ensemble correspondant. Le champ facultatif gt_file spécifie un chemin personnalisé vers un fichier GT.
Toute la configuration s’effectue via des variables d’environnement :
| Variable | Default | Description |
|---|---|---|
ETHIBENCH_LLM_PROVIDER | openai | Fournisseur LLM : openai, anthropic, ollama, gemini |
ETHIBENCH_LLM_MODEL | gpt-5.4-mini | Nom du modèle |
ETHIBENCH_TEMPERATURE | 0.3 | Température d’échantillonnage |
ETHIBENCH_API_URL | — | Point de terminaison API personnalisé (pour Ollama ou les API compatibles) |
ETHIBENCH_CONCURRENCY | 50 | Nombre maximal d’appels LLM simultanés |
ETHIBENCH_MAX_RETRIES | 5 | Nombre maximal de nouvelles tentatives par appel LLM |
ETHIBENCH_MAX_PARALLEL_RUNS | 3 | Nombre maximal d’exécutions évaluées en parallèle au sein d’une expérience |
ANTHROPIC_API_KEY | — | Clé API Anthropic |
OPENAI_API_KEY | — | Clé API OpenAI |
GEMINI_API_KEY | — | Clé API Google Gemini |
Exécution unique :
my_experiment/
├── app.example.com/ # target_id as directory name
│ ├── findings.jsonl # findings for this target
│ └── metrics.json # optional: cost/token info
├── api.example.com/
│ ├── findings.jsonl
│ └── metrics.json
Exécutions multiples :
my_experiment/
├── run_001/
│ ├── app.example.com/
│ │ ├── findings.jsonl
│ │ └── metrics.json
│ └── api.example.com/
│ └── findings.jsonl
├── run_002/
│ ├── app.example.com/
│ │ └── findings.jsonl
│ └── api.example.com/
│ └── findings.jsonl
my_experiment/
└── evaluation_outputs/
├── findings_parsed.jsonl # unified findings with target_id/subset_name
├── raw_matchings/ # Step 1: LLM comparison results
│ └── matchings_MyApp.json
├── matchings/ # Step 2: optimal 1-to-1 assignments
│ └── matchings_MyApp.json
├── results/ # Step 3: per-subset metrics
│ └── evaluation_results_MyApp.json
├── results_avg/ # averaged across replicates
├── results_avg_all/ # averaged across runs (multi-run only)
├── metrics_summary.json # aggregated cost/token metrics
├── plots/ # PNG charts
│ ├── metrics_per_subset.png
│ ├── counts_per_subset.png
│ ├── overall_unweighted.png
│ ├── per_target_costs.png
│ └── per_target_duration.png
├── cumulative-analysis/ # multi-run only: merged findings + overlap
│ ├── findings_parsed.jsonl
│ ├── raw_matchings/
│ ├── matchings/
│ ├── results/
│ ├── results_avg/
│ ├── run_overlap.json # GT-level overlap between runs
│ └── plots/
│ ├── metrics_per_subset.png
│ ├── counts_per_subset.png
│ ├── overall_unweighted.png
│ ├── jaccard_similarity.png
│ └── vulnerability_frequency.png
├── analysis/ # from `ethibench analyze`
│ ├── duplicates.json
│ ├── unmatched.json
│ └── statistics.json
└── summary.md
Sortie de l’analyse par lot (avec --parent-dir) :
parent_dir/
├── experiment_a/
│ └── evaluation_outputs/analysis/ # per-experiment analysis
├── experiment_b/
│ └── evaluation_outputs/analysis/
└── aggregated_analysis/ # cross-experiment aggregation
├── all_duplicates.jsonl # all duplicates as JSONL
├── all_false_positives.jsonl # all false positives as JSONL
└── gt_statistics_avg.json # averaged GT coverage stats
Pour les expériences comportant plusieurs exécutions (sous-répertoires run_*), ethibench produit automatiquement une analyse cumulative qui fusionne toutes les exécutions en un seul jeu de données combiné et recalcule la correspondance bipartie et les métriques sur les données fusionnées. Cette étape s’exécute en dernier dans ethibench evaluate pour les expériences multi-exécutions.
findings_parsed.jsonl de toutes les exécutions (sans déduplication).L’analyse du chevauchement (run_overlap.json) répond à la question : les différentes exécutions découvrent-elles les mêmes vulnérabilités ? Elle utilise les correspondances biparties (affectations TP de référence) de chaque exécution individuelle.
Pour chaque sous-ensemble et globalement :
found_by_all, found_by_some, found_by_one, found_by_none.jaccard_similarity.png — diagramme en barres de la similarité de Jaccard par paire entre les exécutions, avec une ligne pointillée indiquant la moyenne.vulnerability_frequency.png — diagramme en barres montrant combien de vulnérabilités GT ont été trouvées par exactement N exécutions (code couleur allant du rouge=0 au jaune, jusqu’au vert=toutes).evaluation_outputs/cumulative-analysis/
├── findings_parsed.jsonl # merged from all runs
├── raw_matchings/ # merged from all runs
├── matchings/ # bipartite matching on merged data
├── results/ # per-subset metrics
├── results_avg/ # averaged + weighted/unweighted overall
├── run_overlap.json # GT-level overlap between runs
└── plots/
├── metrics_per_subset.png
├── counts_per_subset.png
├── overall_unweighted.png
├── jaccard_similarity.png
└── vulnerability_frequency.png
Correspondance brute : chaque constatation est comparée à chaque entrée de vérité terrain par un LLM. Le LLM décide YES/NO pour chaque paire. Cela produit une correspondance plusieurs-à-plusieurs.
Correspondance bipartie : l’algorithme hongrois (scipy.optimize.linear_sum_assignment) trouve l’affectation optimale un-à-un qui maximise le nombre de paires correspondantes.
Classification :
Métriques :
src/ethibench/
├── cli.py # Click CLI entry points (evaluate, convert-report, analyze, compare)
├── config.py # Environment variable configuration
├── models.py # Pydantic data models
├── datasets.py # Dataset/target YAML management
├── llm.py # LLM provider factory
├── evaluate.py # Core 3-step evaluation pipeline
├── results.py # Results aggregation and averaging
├── metrics.py # Per-target cost/token/duration metrics
├── convert_report.py # Report → findings conversion
├── cumulative_analysis.py # Cross-run cumulative analysis + overlap
├── pairwise.py # Pairwise A/B statistical comparison (t-test, Cohen's d)
├── plots.py # PNG chart generation (eval, cumulative, comparison)
├── report.py # Markdown summary generation
└── analysis/
├── duplicates.py # Duplicate finding detection
├── unmatched.py # Unmatched finding extraction
└── statistics.py # GT coverage statistics