
Estrutura de avaliação para agentes de teste de penetração de IA que mede a descoberta validada de vulnerabilidades usando correspondência semântica baseada em LLM, resolução bipartida e análise cumulativa em alvos do mundo real.
Os agentes de pentest baseados em IA são cada vez mais credíveis como sistemas de segurança ofensiva, mas os benchmarks atuais ainda oferecem orientação limitada sobre quais sistemas terão melhor desempenho em alvos do mundo real. A maioria das avaliações existentes avalia e otimiza para objetivos predefinidos, como captura de bandeira, execução remota de código, reprodução de exploração ou similaridade de trajetória, em ambientes simplificados ou restritos. Esses benchmarks são valiosos para medir capacidades limitadas, mas não capturam adequadamente a complexidade, a exploração aberta e a tomada de decisão estratégica exigidas num pentest realista. Apresentamos um framework prático de avaliação que desloca a avaliação da conclusão de tarefas para a descoberta validada de vulnerabilidades, permitindo a avaliação em alvos suficientemente complexos que abrangem múltiplas superfícies de ataque e classes de vulnerabilidade. O framework combina ground-truth estruturado com correspondência semântica baseada em LLM para identificar vulnerabilidades, resolução bipartida para pontuar descobertas sob ambiguidade realista, manutenção contínua do ground-truth, avaliação repetida e cumulativa de agentes estocásticos, métricas de eficiência e seleção reduzida de conjuntos para experimentação sustentável. Esta metodologia amplia o estado da arte, permitindo uma comparação mais realista e operacionalmente informativa de agentes de pentest baseados em IA. Para permitir reprodutibilidade, também disponibilizamos ground-truth anotado por especialistas e código para o protocolo de avaliação proposto.
Pipeline de avaliação para ferramentas de teste de segurança. Compara as descobertas das ferramentas com conjuntos de dados ground-truth usando correspondência baseada em LLM e produz métricas de precisão, recall, F1 e F0.5.
poetry install
Requer Python 3.11+ e Poetry instalado.
# 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 evaluateExecuta o pipeline completo de avaliação num diretório 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) Diretório contendo os subdiretórios de alvo (ou subdiretórios run_* cada um com subdiretórios de alvo). Pode ser omitido ao usar --parent-dir.Opções:
--dataset, -d — (obrigatório) Caminho para o ficheiro YAML do conjunto de dados.--gt-dir, -g — Diretório do ground-truth. O padrão é gt/ junto ao YAML do conjunto de dados.--output-dir, -o — Diretório de saída. O padrão é evaluation_outputs/ dentro do diretório do experimento. Ignorado no modo batch.--replicates, -n — Número de réplicas de correspondência LLM (padrão: 1).--force, -f — Reexecuta todas as etapas, ignorando artefactos em cache. Por defeito, os resultados intermédios existentes (correspondências brutas, correspondências bipartidas, métricas) são reutilizados.--parent-dir, -p — Pasta principal contendo múltiplos diretórios de experimento a avaliar em lote. Todos os subdiretórios imediatos são tratados como experimentos.O que faz:
target_id), carrega cada findings.jsonl, atribui subset_name a partir do YAML do conjunto de dados.metrics.json por alvo se presente, agrega custo/token/duração.evaluation_outputs/plots/.evaluation_outputs/summary.md.ethibench analyzeExecuta ferramentas de análise sobre saídas de avaliação 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) Diretório do experimento a analisar. Pode ser omitido ao usar --parent-dir.Opções:
--dataset, -d — (obrigatório) Caminho para o ficheiro YAML do conjunto de dados.--gt-dir, -g — Diretório do ground-truth. O padrão é gt/ junto ao YAML do conjunto de dados.--output-dir, -o — Diretório das saídas de avaliação. O padrão é evaluation_outputs/ dentro do diretório do experimento.--parent-dir, -p — Pasta principal contendo múltiplos diretórios de experimento a analisar em lote. Produz análise por experimento mais resultados agregados.Saídas por experimento (evaluation_outputs/analysis/):
duplicates.json — descobertas correspondidas na correspondência bruta mas removidas pela otimização bipartida.unmatched.json — descobertas sem correspondência no ground-truth (falsos positivos).statistics.json — estatísticas de cobertura do GT, distribuição de descobertas por GT.Saídas agregadas (apenas com --parent-dir, em <parent-dir>/aggregated_analysis/):
all_duplicates.jsonl — todas as descobertas duplicadas de todos os experimentos (JSONL, objetos completos de descoberta com campo experiment).all_false_positives.jsonl — todas as descobertas não correspondidas/falsos positivos de todos os experimentos (formato JSONL).gt_statistics_avg.json — cobertura média do GT por subconjunto, mais resumo de cobertura por experimento.ethibench compareCompara resultados de avaliação entre múltiplos experimentos, produzindo gráficos lado a lado e um relatório resumido. Cada experimento deve já ter saídas de avaliação (execute ethibench evaluate primeiro). Os rótulos são sempre os nomes dos diretórios.
# 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) Um ou mais diretórios de experimento a incluir explicitamente.Opções:
--output-dir, -o — (obrigatório) Diretório de saída para os resultados da comparação.--parent-dir, -p — Pasta principal para descobrir automaticamente experimentos. Qualquer subdiretório imediato que contenha uma pasta evaluation_outputs/ é incluído, ordenado alfabeticamente. Pode ser combinado com experiment_dirs explícitos.Saídas (em --output-dir):
comparison.json — dados brutos da comparação para todos os experimentos.plots/ — gráficos PNG lado a lado.comparison.md — resumo em Markdown.pairwise_comparison.md — comparação estatística A/B par a par (top 4 experimentos por F1).pairwise_comparison.tex — versão LaTeX da tabela par a par.cumulative-analysis/ — (se existirem dados cumulativos) análise delta comparando F1 médio vs cumulativo, mais gráficos de comparação cumulativa.findings.jsonl)Um objeto JSON por linha. Campo obrigatório: title, description. Opcionais: url, cwe, severity, score, steps, evidence, metadata, etc.
Cada findings.jsonl reside dentro de um diretório de alvo — o nome do diretório determina a que alvo pertencem as descobertas.
{"title": "SQL Injection in Login", "description": "User input not sanitized", "cwe": "89"}
*_gt.jsonl)Um objeto JSON por linha.
{"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"
O target_id deve corresponder ao nome do diretório dentro de cada pasta de execução. Ao avaliar, o ethibench examina o diretório de execução em busca de subdiretórios que correspondam a valores conhecidos de target_id, carrega o findings.jsonl de cada um e atribui-os ao subconjunto correspondente. O campo opcional gt_file especifica um caminho personalizado para o ficheiro GT.
Toda a configuração é feita através de variáveis de ambiente:
Execução única:
my_experiment/
├── app.example.com/ # target_id como nome do diretório
│ ├── findings.jsonl # descobertas para este alvo
│ └── metrics.json # opcional: informação de custo/token
├── api.example.com/
│ ├── findings.jsonl
│ └── metrics.json
Múltiplas execuções:
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 # descobertas unificadas com target_id/subset_name
├── raw_matchings/ # Passo 1: resultados da comparação LLM
│ └── matchings_MyApp.json
├── matchings/ # Passo 2: atribuições 1-para-1 ótimas
│ └── matchings_MyApp.json
├── results/ # Passo 3: métricas por subconjunto
│ └── evaluation_results_MyApp.json
├── results_avg/ # média entre réplicas
├── results_avg_all/ # média entre execuções (apenas multi-execução)
├── metrics_summary.json # métricas agregadas de custo/token
├── plots/ # gráficos PNG
│ ├── metrics_per_subset.png
│ ├── counts_per_subset.png
│ ├── overall_unweighted.png
│ ├── per_target_costs.png
│ └── per_target_duration.png
├── cumulative-analysis/ # apenas multi-execução: descobertas fundidas + sobreposição
│ ├── findings_parsed.jsonl
│ ├── raw_matchings/
│ ├── matchings/
│ ├── results/
│ ├── results_avg/
│ ├── run_overlap.json # sobreposição a nível de GT entre execuções
│ └── plots/
│ ├── metrics_per_subset.png
│ ├── counts_per_subset.png
│ ├── overall_unweighted.png
│ ├── jaccard_similarity.png
│ └── vulnerability_frequency.png
├── analysis/ # a partir de `ethibench analyze`
│ ├── duplicates.json
│ ├── unmatched.json
│ └── statistics.json
└── summary.md
Saída da análise em lote (com --parent-dir):
parent_dir/
├── experiment_a/
│ └── evaluation_outputs/analysis/ # análise por experimento
├── experiment_b/
│ └── evaluation_outputs/analysis/
└── aggregated_analysis/ # agregação entre experimentos
├── all_duplicates.jsonl # todos os duplicados como JSONL
├── all_false_positives.jsonl # todos os falsos positivos como JSONL
└── gt_statistics_avg.json # estatísticas médias de cobertura GT
Para experimentos com múltiplas execuções (subdiretórios run_*), o ethibench produz automaticamente uma análise cumulativa que funde todas as execuções num único conjunto de dados combinado e recalcula a correspondência bipartida e as métricas sobre os dados fundidos. Esta é a última etapa de ethibench evaluate para experimentos multi-execução.
findings_parsed.jsonl de todas as execuções (sem desduplicação).A análise de sobreposição (run_overlap.json) responde à pergunta: execuções diferentes estão a descobrir as mesmas vulnerabilidades? Utiliza as correspondências bipartidas (atribuições TP autoritativas) de cada execução individual.
Para cada subconjunto e globalmente:
found_by_all, found_by_some, found_by_one, found_by_none.jaccard_similarity.png — gráfico de barras da similaridade de Jaccard par a par entre execuções com uma linha tracejada a mostrar a média.vulnerability_frequency.png — gráfico de barras a mostrar quantas vulnerabilidades GT foram encontradas por exatamente N execuções (codificado por cores: vermelho=0, amarelo, verde=todas).evaluation_outputs/cumulative-analysis/
├── findings_parsed.jsonl # fundido de todas as execuções
├── raw_matchings/ # fundido de todas as execuções
├── matchings/ # correspondência bipartida sobre dados fundidos
├── results/ # métricas por subconjunto
├── results_avg/ # total médio + ponderado/não ponderado
├── run_overlap.json # sobreposição a nível de GT entre execuções
└── plots/
├── metrics_per_subset.png
├── counts_per_subset.png
├── overall_unweighted.png
├── jaccard_similarity.png
└── vulnerability_frequency.png
Correspondência bruta: Cada descoberta é comparada com cada entrada do ground-truth por um LLM. O LLM decide SIM/NÃO para cada par. Isto produz um mapeamento muitos-para-muitos.
Correspondência bipartida: O algoritmo Húngaro (scipy.optimize.linear_sum_assignment) encontra a atribuição um-para-um ótima que maximiza o número de pares correspondidos.
Classificação:
Métricas:
src/ethibench/
├── cli.py # Pontos de entrada CLI Click (evaluate, convert-report, analyze, compare)
├── config.py # Configuração de variáveis de ambiente
├── models.py # Modelos de dados Pydantic
├── datasets.py # Gestão de conjuntos de dados/alvos YAML
├── llm.py # Fábrica de provedores LLM
├── evaluate.py # Pipeline de avaliação principal em 3 etapas
├── results.py # Agregação e média de resultados
├── metrics.py # Métricas de custo/token/duração por alvo
├── convert_report.py # Conversão de relatório → descobertas
├── cumulative_analysis.py # Análise cumulativa entre execuções + sobreposição
├── pairwise.py # Comparação estatística A/B par a par (teste t, d de Cohen)
├── plots.py # Geração de gráficos PNG (eval, cumulativo, comparação)
├── report.py # Geração de resumo Markdown
└── analysis/
├── duplicates.py # Deteção de descobertas duplicadas
├── unmatched.py # Extração de descobertas não correspondidas
└── statistics.py # Estatísticas de cobertura GT
| Variável | Padrão | Descrição |
|---|
ETHIBENCH_LLM_PROVIDER | openai | Provedor LLM: openai, anthropic, ollama, gemini |
ETHIBENCH_LLM_MODEL | gpt-5.4-mini | Nome do modelo |
ETHIBENCH_TEMPERATURE | 0.3 | Temperatura de amostragem |
ETHIBENCH_API_URL | — | Endpoint de API personalizado (para Ollama ou APIs compatíveis) |
ETHIBENCH_CONCURRENCY | 50 | Máximo de chamadas LLM concorrentes |
ETHIBENCH_MAX_RETRIES | 5 | Máximo de tentativas por chamada LLM |
ETHIBENCH_MAX_PARALLEL_RUNS | 3 | Máximo de execuções avaliadas em paralelo dentro de um experimento |
ANTHROPIC_API_KEY | — | Chave da API Anthropic |
OPENAI_API_KEY | — | Chave da API OpenAI |
GEMINI_API_KEY | — | Chave da API Google Gemini |