
AI ペンテストエージェントは、攻撃的セキュリティシステムとして信頼性を高めつつありますが、現在のベンチマークは、実世界のターゲットに対してどのシステムが最も優れた性能を発揮するかについて、依然として限定的な指針しか提供していません。既存の評価の大半は、フラグ獲得、リモートコード実行、エクスプロイトの再現、軌跡の類似性といった事前定義された目標を、簡素化された狭い環境で評価・最適化しています。これらのベンチマークは限定された能力を測定する上では有用ですが、実際のペンテストに求められる複雑さ、自由な探索、戦略的な意思決定を十分に捉えてはいません。本稿では、評価をタスク完了から検証済み脆弱性の発見へと転換する実践的な評価フレームワークを提示し、複数の攻撃面と脆弱性クラスにまたがる十分に複雑なターゲットでの評価を可能にします。このフレームワークは、構造化されたグラウンドトゥルースと LLM ベースのセマンティックマッチングを組み合わせて脆弱性を特定し、現実的な曖昧性の下で findings をスコアリングする二部マッチングによる解決、グラウンドトゥルースの継続的なメンテナンス、確率的エージェントの反復的かつ累積的な評価、効率メトリクス、持続可能な実験のための削減スイートの選択を統合します。この方法論は、AI ペンテストエージェントのより現実的で運用上有益な比較を可能にし、最先端を拡張します。再現性を確保するため、提案する評価プロトコルの専門家注釈付きグラウンドトゥルースとコードも併せて公開します。
セキュリティテストツールの評価パイプライン。LLM ベースのマッチングを使用してツールの findings をグラウンドトゥルースデータセットと比較し、precision、recall、F1、F0.5 のメトリクスを生成します。
poetry install
Python 3.11+ と Poetry のインストールが必要です。
# 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 evaluate実験ディレクトリに対して完全な評価パイプラインを実行します。
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
引数:
experiment_dir —(任意)ターゲットのサブディレクトリ(または、それぞれがターゲットサブディレクトリを持つ run_* サブディレクトリ)を含むディレクトリ。--parent-dir 使用時は省略可能。オプション:
--dataset, -d —(必須)データセット YAML ファイルへのパス。--gt-dir, -g — グラウンドトゥルースディレクトリ。デフォルトはデータセット YAML の隣の gt/。--output-dir, -o — 出力ディレクトリ。デフォルトは実験ディレクトリ内の evaluation_outputs/。バッチモードでは無視されます。--replicates, -n — LLM マッチングの反復回数(デフォルト: 1)。--force, -f — キャッシュされた成果物を無視してすべてのステップを再実行します。デフォルトでは、既存の中間結果(raw matchings、bipartite matchings、metrics)が再利用されます。--parent-dir, -p — バッチ評価する複数の実験ディレクトリを含む親フォルダ。直下のすべてのサブディレクトリが実験として扱われます。処理内容:
target_id)をスキャンし、各 findings.jsonl を読み込んで、データセット YAML から subset_name を割り当てます。metrics.json を読み込み、コスト/トークン/所要時間を集約します。evaluation_outputs/plots/ に PNG チャートを生成します。evaluation_outputs/summary.md を書き出します。ethibench analyze既存の評価出力に対して分析ツールを実行します。
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>
引数:
experiment_dir —(任意)分析対象の実験ディレクトリ。--parent-dir 使用時は省略可能。オプション:
--dataset, -d —(必須)データセット YAML ファイルへのパス。--gt-dir, -g — グラウンドトゥルースディレクトリ。デフォルトはデータセット YAML の隣の gt/。--output-dir, -o — 評価出力ディレクトリ。デフォルトは実験ディレクトリ内の evaluation_outputs/。--parent-dir, -p — バッチ分析する複数の実験ディレクトリを含む親フォルダ。実験ごとの分析に加え、集約結果を生成します。実験ごとの出力(evaluation_outputs/analysis/):
duplicates.json — raw マッチングでは一致したが、二部最適化によって除外された findings。unmatched.json — グラウンドトゥルースに一致しない findings(false positives)。statistics.json — GT カバレッジ統計、GT ごとの findings 数の分布。集約出力(--parent-dir 指定時のみ、<parent-dir>/aggregated_analysis/ 内):
all_duplicates.jsonl — 全実験にわたるすべての重複 findings(JSONL。experiment フィールド付きの完全な finding オブジェクト)。all_false_positives.jsonl — 全実験にわたるすべての未マッチ/false positive findings(JSONL 形式)。gt_statistics_avg.json — サブセットごとの平均 GT カバレッジと、実験ごとのカバレッジサマリー。ethibench compare複数の実験にわたる評価結果を比較し、並べて表示するプロットとサマリーレポートを生成します。各実験には既に評価出力が存在している必要があります(先に ethibench evaluate を実行してください)。ラベルは常にディレクトリ名です。
# 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/
引数:
experiment_dirs —(任意)明示的に含める 1 つ以上の実験ディレクトリ。オプション:
--output-dir, -o —(必須)比較結果の出力ディレクトリ。--parent-dir, -p — 実験を自動検出する親フォルダ。evaluation_outputs/ フォルダを含む直下のサブディレクトリがすべてアルファベット順に含まれます。明示的な experiment_dirs と組み合わせることができます。出力(--output-dir 内):
comparison.json — 全実験の生の比較データ。plots/ — 並べて表示する PNG チャート。comparison.md — Markdown サマリー。pairwise_comparison.md — ペアワイズ A/B 統計比較(F1 上位 4 件の実験)。pairwise_comparison.tex — ペアワイズ表の LaTeX 版。cumulative-analysis/ —(累積データが存在する場合)平均 F1 と累積 F1 を比較するデルタ分析と、累積比較プロット。findings.jsonl)1 行に 1 つの JSON オブジェクト。必須フィールド: title、description。任意: url、cwe、severity、score、steps、evidence、metadata など。
各 findings.jsonl はターゲットディレクトリ内に置かれます。ディレクトリ名が、findings がどのターゲットに属するかを決定します。
{"title": "SQL Injection in Login", "description": "User input not sanitized", "cwe": "89"}
*_gt.jsonl)1 行に 1 つの JSON オブジェクト。
{"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"
target_id は各ラン フォルダ配下のディレクトリ名と一致している必要があります。評価時、ethibench はラン ディレクトリをスキャンして既知の target_id 値に一致するサブディレクトリを探し、その findings.jsonl を読み込んで対応するサブセットに割り当てます。オプションの gt_file フィールドでカスタム GT ファイルパスを指定できます。
すべての設定は環境変数を介して行います:
単一ラン:
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
複数ラン:
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
バッチ分析出力(--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
複数ラン(run_* サブディレクトリ)を持つ実験の場合、ethibench はすべてのランを単一の結合データセットにマージし、マージされたデータ上で二部マッチングとメトリクスを再計算する累積分析を自動的に生成します。これは複数ラン実験の ethibench evaluate の最後のステップとして実行されます。
findings_parsed.jsonl を連結します(重複除去なし)。重複分析(run_overlap.json)は、異なるランが同じ脆弱性を発見しているか? という問いに答えます。各ランの二部マッチング(信頼できる TP 割り当て)を使用します。
各サブセットおよび全体について:
found_by_all、found_by_some、found_by_one、found_by_none。jaccard_similarity.png — ラン間のペアワイズ Jaccard 類似度の棒グラフ。平均を示す破線付き。vulnerability_frequency.png — ちょうど N 個のランによって発見された GT 脆弱性の数を示す棒グラフ(赤=0 から黄を経て緑=すべてまで色分け)。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
raw マッチング: 各 finding は LLM によって各グラウンドトゥルースエントリと比較されます。LLM は各ペアについて YES/NO を判定します。これにより多対多のマッピングが生成されます。
二部マッチング: ハンガリアンアルゴリズム(scipy.optimize.linear_sum_assignment)が、一致したペア数を最大化する最適な 1 対 1 の割り当てを求めます。
分類:
メトリクス:
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
| 変数 | デフォルト | 説明 |
|---|
ETHIBENCH_LLM_PROVIDER | openai | LLM プロバイダー: openai、anthropic、ollama、gemini |
ETHIBENCH_LLM_MODEL | gpt-5.4-mini | モデル名 |
ETHIBENCH_TEMPERATURE | 0.3 | サンプリング温度 |
ETHIBENCH_API_URL | — | カスタム API エンドポイント(Ollama または互換 API 用) |
ETHIBENCH_CONCURRENCY | 50 | LLM 呼び出しの最大同時実行数 |
ETHIBENCH_MAX_RETRIES | 5 | LLM 呼び出しあたりの最大リトライ回数 |
ETHIBENCH_MAX_PARALLEL_RUNS | 3 | 実験内で並列評価される最大ラン数 |
ANTHROPIC_API_KEY | — | Anthropic API キー |
OPENAI_API_KEY | — | OpenAI API キー |
GEMINI_API_KEY | — | Google Gemini API キー |