AI 渗透测试代理作为进攻性安全系统正日益可信,但当前的基准测试在哪些系统能在真实世界目标上表现最佳方面,提供的指导仍然有限。大多数现有评估都在简化或狭窄的环境中,针对预设目标(如获取 flag、远程代码执行、漏洞利用复现或轨迹相似度)进行评估和优化。这些基准测试对于衡量受限能力很有价值,但无法充分捕捉真实渗透测试所需的复杂性、开放式探索和战略决策能力。我们提出了一种实用的评估框架,将评估从任务完成转向经验证的漏洞发现,从而允许在涵盖多个攻击面和漏洞类别的足够复杂目标上进行评估。该框架结合结构化真值与基于 LLM 的语义匹配来识别漏洞,通过二部图求解在现实歧义下对发现结果进行评分,并支持持续的真值维护、对随机代理的重复与累积评估、效率指标,以及精简套件选择以实现可持续实验。该方法使 AI 渗透测试代理的比较更加真实、更具操作参考价值,从而推进了现有技术水平。为实现可复现性,我们还发布了专家标注的真值数据以及所提出评估协议的代码。
安全测试工具的评估流水线。使用基于 LLM 的匹配将工具发现结果与真值数据集进行比较,并生成精确率、召回率、F1 和 F0.5 指标。
poetry install
需要安装 Python 3.11+ 和 Poetry。
# 1. 设置你的 LLM API 密钥
export OPENAI_API_KEY="..."
# 2. 运行评估
ethibench evaluate ./my_experiment --dataset path/to/dataset.yaml
# 3. 查看结果
cat ./my_experiment/evaluation_outputs/summary.md
ethibench evaluate对实验目录运行完整的评估流水线。
ethibench evaluate <experiment_dir> --dataset <dataset.yaml> [options]
# 批处理:评估文件夹中的所有实验
ethibench evaluate --parent-dir final_experiments/ --dataset <dataset.yaml>
# 强制重新评估(忽略缓存的产物)
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 — 重新运行所有步骤,忽略缓存的产物。默认情况下,会复用现有的中间结果(原始匹配、二部图匹配、指标)。--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]
# 批处理:分析所有实验并生成聚合结果
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 — 在原始匹配中匹配到但被二部图优化移除的发现结果。unmatched.json — 没有真值匹配的发现结果(误报)。statistics.json — GT 覆盖率统计、每个 GT 的发现结果分布。聚合输出(仅使用 --parent-dir 时,位于 <parent-dir>/aggregated_analysis/):
all_duplicates.jsonl — 所有实验中所有重复的发现结果(JSONL 格式,包含 experiment 字段的完整发现对象)。all_false_positives.jsonl — 所有实验中所有未匹配/误报的发现结果(JSONL 格式)。gt_statistics_avg.json — 每个子集的平均 GT 覆盖率,以及每个实验的覆盖率摘要。ethibench compare比较多个实验的评估结果,生成并排图表和摘要报告。每个实验必须已有评估输出(先运行 ethibench evaluate)。标签始终使用目录名称。
# 显式指定实验目录
ethibench compare exp-gpt4o/ exp-claude/ --output-dir comparison/
# 自动发现父文件夹下的所有实验
ethibench compare --parent-dir all-experiments/ --output-dir comparison/
# 混合:显式目录 + 自动发现
ethibench compare exp-extra/ --parent-dir all-experiments/ --output-dir comparison/
参数:
experiment_dirs —(可选)要显式包含的一个或多个实验目录。选项:
--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 的差异分析,以及累积比较图表。findings.jsonl)每行一个 JSON 对象。必填字段:title、description。可选字段:url、cwe、severity、score、steps、evidence、metadata 等。
每个 findings.jsonl 位于目标目录内 — 目录名称决定了这些发现结果属于哪个目标。
{"title": "SQL Injection in Login", "description": "User input not sanitized", "cwe": "89"}
*_gt.jsonl)每行一个 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 文件路径。
所有配置均通过环境变量进行:
| Variable | Default | Description |
|---|---|---|
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 密钥 |
单次运行:
my_experiment/
├── app.example.com/ # 目录名称作为 target_id
│ ├── findings.jsonl # 该目标的发现结果
│ └── metrics.json # 可选:成本/令牌信息
├── 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 # 统一的发现结果,包含 target_id/subset_name
├── raw_matchings/ # 步骤 1:LLM 比较结果
│ └── matchings_MyApp.json
├── matchings/ # 步骤 2:最优一对一分配
│ └── matchings_MyApp.json
├── results/ # 步骤 3:每个子集的指标
│ └── evaluation_results_MyApp.json
├── results_avg/ # 对多次重复取平均
├── results_avg_all/ # 对多次运行取平均(仅多次运行)
├── metrics_summary.json # 聚合的成本/令牌指标
├── plots/ # PNG 图表
│ ├── metrics_per_subset.png
│ ├── counts_per_subset.png
│ ├── overall_unweighted.png
│ ├── per_target_costs.png
│ └── per_target_duration.png
├── cumulative-analysis/ # 仅多次运行:合并的发现结果 + 重叠
│ ├── findings_parsed.jsonl
│ ├── raw_matchings/
│ ├── matchings/
│ ├── results/
│ ├── results_avg/
│ ├── run_overlap.json # 运行之间的 GT 级重叠
│ └── plots/
│ ├── metrics_per_subset.png
│ ├── counts_per_subset.png
│ ├── overall_unweighted.png
│ ├── jaccard_similarity.png
│ └── vulnerability_frequency.png
├── analysis/ # 来自 `ethibench analyze`
│ ├── duplicates.json
│ ├── unmatched.json
│ └── statistics.json
└── summary.md
批处理分析输出(使用 --parent-dir):
parent_dir/
├── experiment_a/
│ └── evaluation_outputs/analysis/ # 每个实验的分析
├── experiment_b/
│ └── evaluation_outputs/analysis/
└── aggregated_analysis/ # 跨实验聚合
├── all_duplicates.jsonl # 所有重复项,JSONL 格式
├── all_false_positives.jsonl # 所有误报,JSONL 格式
└── gt_statistics_avg.json # 平均 GT 覆盖率统计
对于具有多次运行(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 # 从所有运行中合并
├── raw_matchings/ # 从所有运行中合并
├── matchings/ # 在合并数据上的二部图匹配
├── results/ # 每个子集的指标
├── results_avg/ # 平均值 + 加权/未加权总体
├── run_overlap.json # 运行之间的 GT 级重叠
└── plots/
├── metrics_per_subset.png
├── counts_per_subset.png
├── overall_unweighted.png
├── jaccard_similarity.png
└── vulnerability_frequency.png
原始匹配:每条发现结果由 LLM 与每条真值条目进行比较。LLM 对每一对判断是/否(YES/NO)。这会产生一个多对多的映射。
二部图匹配:匈牙利算法(scipy.optimize.linear_sum_assignment)寻找最大化匹配对数量的一对一最优分配。
分类:
指标:
src/ethibench/
├── cli.py # Click CLI 入口点(evaluate、convert-report、analyze、compare)
├── config.py # 环境变量配置
├── models.py # Pydantic 数据模型
├── datasets.py # 数据集/目标 YAML 管理
├── llm.py # LLM 提供商工厂
├── evaluate.py # 核心三步评估流水线
├── results.py # 结果聚合与取平均
├── metrics.py # 每个目标的成本/令牌/耗时指标
├── convert_report.py # 报告 → 发现结果转换
├── cumulative_analysis.py # 跨运行累积分析 + 重叠
├── pairwise.py # 两两 A/B 统计比较(t 检验、Cohen's d)
├── plots.py # PNG 图表生成(评估、累积、比较)
├── report.py # Markdown 摘要生成
└── analysis/
├── duplicates.py # 重复发现结果检测
├── unmatched.py # 未匹配发现结果提取
└── statistics.py # GT 覆盖率统计