
Los agentes de pentesting con IA son cada vez más creíbles como sistemas ofensivos de seguridad, pero los benchmarks actuales aún proporcionan una guía limitada sobre qué sistemas tendrán un mejor rendimiento en objetivos del mundo real. La mayoría de las evaluaciones existentes valoran y optimizan metas predefinidas, como la captura de banderas, la ejecución remota de código, la reproducción de exploits o la similitud de trayectorias, en entornos simplificados o reducidos. Estos benchmarks son valiosos para medir capacidades acotadas, pero no capturan adecuadamente la complejidad, la exploración abierta y la toma de decisiones estratégicas que exige el pentesting realista. Presentamos un marco de evaluación práctico que traslada la evaluación desde la finalización de tareas hacia el descubrimiento validado de vulnerabilidades, lo que permite evaluar objetivos suficientemente complejos que abarcan múltiples superficies de ataque y clases de vulnerabilidad. El marco combina ground truth estructurado con emparejamiento semántico basado en LLM para identificar vulnerabilidades, resolución bipartita para puntuar los hallazgos bajo una ambigüedad realista, mantenimiento continuo del ground truth, evaluación repetida y acumulativa de agentes estocásticos, métricas de eficiencia y selección de conjuntos reducidos para una experimentación sostenible. Esta metodología amplía el estado del arte al permitir una comparación más realista y operativamente informativa de los agentes de pentesting con IA. Para permitir la reproducibilidad, además publicamos el ground truth anotado por expertos y el código del protocolo de evaluación propuesto.
Canal de evaluación para herramientas de pruebas de seguridad. Compara los hallazgos de la herramienta con conjuntos de datos de ground truth mediante emparejamiento basado en LLM y produce métricas de precisión, recall, F1 y F0.5.
poetry install
Requiere Python 3.11+ y Poetry instalados.
# 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 evaluateEjecuta el pipeline completo de evaluación en un directorio de experimento.
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
Argumentos:
experiment_dir — (opcional) Directorio que contiene subdirectorios de objetivos (o subdirectorios run_*, cada uno con subdirectorios de objetivos). Puede omitirse cuando se usa --parent-dir.Opciones:
--dataset, -d — (obligatorio) Ruta al archivo YAML del dataset.--gt-dir, -g — Directorio de ground truth. Por defecto, gt/ junto al YAML del dataset.--output-dir, -o — Directorio de salida. Por defecto, evaluation_outputs/ dentro del directorio del experimento. Se ignora en modo por lotes.--replicates, -n — Número de réplicas de emparejamiento LLM (predeterminado: 1).--force, -f — Vuelve a ejecutar todos los pasos, ignorando los artefactos en caché. De forma predeterminada, se reutilizan los resultados intermedios existentes (emparejamientos crudos, emparejamientos bipartitos, métricas).--parent-dir, -p — Carpeta principal que contiene múltiples directorios de experimento para evaluar en lote. Todos los subdirectorios inmediatos se tratan como experimentos.Qué hace:
target_id), carga cada findings.jsonl y asigna subset_name desde el YAML del dataset.metrics.json por objetivo si existe, agrega coste/tokens/duración.evaluation_outputs/plots/.evaluation_outputs/summary.md.ethibench analyzeEjecuta herramientas de análisis sobre salidas de evaluación existentes.
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>
Argumentos:
experiment_dir — (opcional) Directorio de experimento a analizar. Puede omitirse cuando se usa --parent-dir.Opciones:
--dataset, -d — (obligatorio) Ruta al archivo YAML del dataset.--gt-dir, -g — Directorio de ground truth. Por defecto, gt/ junto al YAML del dataset.--output-dir, -o — Directorio de salidas de evaluación. Por defecto, evaluation_outputs/ dentro del directorio del experimento.--parent-dir, -p — Carpeta principal que contiene múltiples directorios de experimento para analizar en lote. Produce análisis por experimento además de resultados agregados.Salidas por experimento (evaluation_outputs/analysis/):
duplicates.json — hallazgos que coincidieron en el emparejamiento crudo pero fueron eliminados por la optimización bipartita.unmatched.json — hallazgos sin coincidencia con ground truth (falsos positivos).statistics.json — estadísticas de cobertura de GT, distribución de hallazgos por GT.Salidas agregadas (solo con --parent-dir, en <parent-dir>/aggregated_analysis/):
all_duplicates.jsonl — todos los hallazgos duplicados de todos los experimentos (JSONL, objetos de hallazgo completos con campo experiment).all_false_positives.jsonl — todos los hallazgos no emparejados/falsos positivos de todos los experimentos (formato JSONL).gt_statistics_avg.json — cobertura de GT promediada por subconjunto, más un resumen de cobertura por experimento.ethibench compareCompara los resultados de evaluación de múltiples experimentos, generando gráficos lado a lado y un informe resumen. Cada experimento debe tener ya salidas de evaluación (ejecuta primero ethibench evaluate). Las etiquetas son siempre los nombres de los directorios.
# 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/
Argumentos:
experiment_dirs — (opcional) Uno o más directorios de experimento para incluir explícitamente.Opciones:
--output-dir, -o — (obligatorio) Directorio de salida para los resultados de la comparación.--parent-dir, -p — Carpeta principal desde la que descubrir experimentos automáticamente. Se incluye cualquier subdirectorio inmediato que contenga una carpeta evaluation_outputs/, ordenado alfabéticamente. Puede combinarse con experiment_dirs explícitos.Salidas (en --output-dir):
comparison.json — datos crudos de comparación de todos los experimentos.plots/ — gráficos PNG lado a lado.comparison.md — resumen en Markdown.pairwise_comparison.md — comparación estadística por pares A/B (los 4 mejores experimentos por F1).pairwise_comparison.tex — versión en LaTeX de la tabla por pares.cumulative-analysis/ — (si existen datos acumulativos) análisis delta que compara el F1 promedio frente al acumulativo, además de gráficos de comparación acumulativa.findings.jsonl)Un objeto JSON por línea. Campo obligatorio: title, description. Opcionales: url, cwe, severity, score, steps, evidence, metadata, etc.
Cada findings.jsonl se encuentra dentro de un directorio de objetivo; el nombre del directorio determina a qué objetivo pertenecen los hallazgos.
{"title": "SQL Injection in Login", "description": "User input not sanitized", "cwe": "89"}
*_gt.jsonl)Un objeto JSON por línea.
{"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"
El target_id debe coincidir con el nombre del directorio dentro de cada carpeta de ejecución. Al evaluar, ethibench escanea el directorio de ejecución en busca de subdirectorios que coincidan con los valores target_id conocidos, carga sus findings.jsonl y los asigna al subconjunto correspondiente. El campo opcional gt_file especifica una ruta personalizada de archivo GT.
Toda la configuración se realiza mediante variables de entorno:
Ejecución única:
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
Múltiples ejecuciones:
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
Salida del análisis por lotes (con --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
Para experimentos con múltiples ejecuciones (subdirectorios run_*), ethibench produce automáticamente un análisis acumulativo que fusiona todas las ejecuciones en un único conjunto de datos combinado y recalcula el emparejamiento bipartito y las métricas sobre los datos fusionados. Esto se ejecuta como último paso de ethibench evaluate para experimentos con múltiples ejecuciones.
findings_parsed.jsonl de todas las ejecuciones (sin deduplicación).El análisis de solapamiento (run_overlap.json) responde a la pregunta: ¿están las distintas ejecuciones descubriendo las mismas vulnerabilidades? Utiliza los emparejamientos bipartitos (asignaciones TP autoritativas) de cada ejecución individual.
Para cada subconjunto y globalmente:
found_by_all, found_by_some, found_by_one, found_by_none.jaccard_similarity.png — gráfico de barras de la similitud de Jaccard por pares entre ejecuciones, con una línea discontinua que muestra la media.vulnerability_frequency.png — gráfico de barras que muestra cuántas vulnerabilidades GT fueron encontradas por exactamente N ejecuciones (codificado por color desde rojo=0, pasando por amarillo, hasta verde=todas).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
Emparejamiento crudo: cada hallazgo se compara con cada entrada de ground truth mediante un LLM. El LLM decide SÍ/NO para cada par. Esto produce una asignación de muchos a muchos.
Emparejamiento bipartito: el algoritmo húngaro (scipy.optimize.linear_sum_assignment) encuentra la asignación óptima uno a uno que maximiza el número de pares emparejados.
Clasificación:
Métricas:
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
| Variable | Predeterminado | Descripción |
|---|
ETHIBENCH_LLM_PROVIDER | openai | Proveedor de LLM: openai, anthropic, ollama, gemini |
ETHIBENCH_LLM_MODEL | gpt-5.4-mini | Nombre del modelo |
ETHIBENCH_TEMPERATURE | 0.3 | Temperatura de muestreo |
ETHIBENCH_API_URL | — | Endpoint de API personalizado (para Ollama o APIs compatibles) |
ETHIBENCH_CONCURRENCY | 50 | Máximo de llamadas LLM concurrentes |
ETHIBENCH_MAX_RETRIES | 5 | Máximo de reintentos por llamada LLM |
ETHIBENCH_MAX_PARALLEL_RUNS | 3 | Máximo de ejecuciones evaluadas en paralelo dentro de un experimento |
ANTHROPIC_API_KEY | — | Clave de API de Anthropic |
OPENAI_API_KEY | — | Clave de API de OpenAI |
GEMINI_API_KEY | — | Clave de API de Google Gemini |