Benchmarks
BreachPilot's benchmark suite turns evaluation into a first-class, reproducible feature: benchmark targets run under the existing sandboxed execution architecture, outcomes are verified by an independent oracle, and every run records enough metadata to reproduce and defend the numbers.
Warning — authorized lab environments only. Benchmark targets are deliberately vulnerable images. Operate them exclusively against infrastructure you own or are explicitly authorized to test, in an isolated lab. The same authorization rules as the rest of BreachPilot apply.
Architecture
The benchmark subsystem lives in tools/benchmark/ and separates concerns so
no god-file owns everything:
benchmark provider (XBEN is one; suites plug in via tools/benchmark/registry.py)
-> scenarios (tools/benchmark/models.py::BenchmarkScenario)
-> provision/reset target (tools/benchmark/targets.py)
-> create sandbox (existing tools/sandbox; no host fallback)
-> run BreachPilot mission (tools/benchmark/agent_runner.py
wrapping tools/exploit_session.run_exploit_session)
-> independently verify (tools/benchmark/verifier.py reusing
tools/eval_checks executors + eval_harness flag semantics)
-> collect metrics (tools/benchmark/metrics.py)
-> persist results (tools/benchmark/storage.py)
-> destroy/reset target
models.py— typed dataclasses: scenarios, trials, run config/environment, summaries.TrialStatusdistinguishesVERIFIED/FAILED/FALSE_POSITIVE/TIMEOUT/INFRASTRUCTURE_ERROR.registry.py— provider registry. XBEN (tools/benchmark/xben/) is one provider; future suites register their own provider and the runner never changes.targets.py— target lifecycle (docker run/restart/teardown) with a monkeypatchable subprocess seam. Provision failures raiseTargetProvisionError→ the trial isINFRASTRUCTURE_ERROR, never a fake exploitation failure.agent_runner.py— one attack mission per trial. Detects the agent's claimed success from structured tool-outcome counters, captures token / model-call telemetry from the sharedllm_usage.jsonldelta, converts the audit trail into structured events, and extracts sandbox facts.verifier.py— the ONLY source oforacle_verified_success. Reuses the graded eval's declarative check executors (tools/eval_checks.py): HTTP login/request probes,file_contains(incl.loot://), andshell_commandthrough a dedicated soft-fail MCP session. A missing session degrades shell checks to UNVERIFIED (fail-closed).runner.py— orchestrates trials: provision → mission → verify → classify → persist → teardown. Async-safe, cancellable, and one failing trial never aborts the suite.service.py— API-facing owner of the active run (lifecycle plumbing only).metrics.py/regression.py/report.py/storage.py/replay.py— aggregation + statistics, baselines and regression detection, public report rendering, persistence, and reproduction manifests.
Verified success vs claimed success
This is the core contract:
agent_claimed_success— what the agent thought (from structured tool-outcome counts, not LLM prose, exit codes, or tool output text).oracle_verified_success— what the independent verifier confirmed on the target.
A trial is solved only when the oracle confirms. When the agent claims
success and the oracle disagrees, the trial is a FALSE_POSITIVE — its own
status and failure category, surfaced prominently in reports and the WebUI.
The inverse (oracle verified, agent undersold) is recorded as
false_negative where determinable.
Running benchmarks
# WebUI (default no-args launch), then click "Benchmarks"
python main.py
# CLI: run a suite
python main.py --benchmark xben
# Filters and repetition
python main.py --benchmark xben --scenario xben-dvwa --trials 5
python main.py --benchmark xben --tag web
# List registered suites and their scenarios
python main.py --benchmark-list
# Baselines / regression gates (exit 1 on hard regressions)
python main.py --benchmark xben --save-baseline
python main.py --benchmark xben --check-regression
The existing --eval / --eval-list commands are unchanged; the benchmark
CLI reuses the same config validation and baseline workflow.
Target setup
Scenario definitions live in benchmarks/<suite>/*.json (XBEN-style
manifests: benchmark_id, name, target image/host/ports, goal, tags,
difficulty, reset strategy, timeout, and the oracle). The shipped xben
manifests target the repo's eval_targets/docker-compose.yml lab suite:
docker compose -f eval_targets/docker-compose.yml up -d # loopback-only
python main.py --benchmark xben
Loopback-lab + sandbox prerequisite. The shipped
xbenmanifests target127.0.0.1, but a sandboxed worker's loopback is container-local (sandbox.network.map_host_loopback:false), so sandboxed exploit execution cannot reach the lab by construction. Loopback trials fail fast asINFRASTRUCTURE_ERROR/SANDBOX_FAILEDinstead of burning the mission budget. For the loopback lab, rerun with the explicit lab opt-out (sandbox.enabled:false+benchmark.sandbox_required:false), or setsandbox.network.map_host_loopback:true(dev-lab localhost only, never for production runs).
A manifest can also declare target_type: "docker" + target_image, in which
case the benchmark provisions one container per trial itself (reset strategy
recreate or restart).
Reproducibility
Every run records a reproduction manifest inside run.json: git SHA + dirty
status, model provider/alias/id/version, reasoning config, temperature,
config hash, benchmark config hash, sandbox image + digest, per-scenario
target images, and timestamps. Missing metadata is recorded as unknown —
never silently substituted — so reproducibility claims stay honest.
tools/benchmark/replay.py::check_reproducibility compares a stored run
against the current environment field-by-field.
Metrics
Aggregate summaries include verified success rate, false-positive rate,
median/mean solve time and tool actions, token totals, estimated cost,
time-to-first-verified-success, sandbox-blocked action counts, and failure
categories. With repeated trials (--trials N) each scenario gets a success
probability, variance/standard deviation, and a Wilson 95% confidence
interval. A single lucky trial never reads as reliable — with one trial the
CI spans most of the range.
Failure classification
Unsuccessful trials are classified to answer "why does BreachPilot fail
here?": TARGET_PROVISION_FAILED, SANDBOX_FAILED, MODEL_FAILED,
TIMEOUT, PLANNER_FAILURE, TOOL_FAILURE, VERIFICATION_FAILURE,
FALSE_POSITIVE, NO_EXPLOIT_PATH, AGENT_ABORTED, TARGET_RESET_FAILED,
UNKNOWN. Infrastructure failures (provision, sandbox) are reported as
INFRASTRUCTURE_ERROR and excluded from success-rate denominators — they are
not exploitation failures.
Storage layout
reports/benchmarks/<suite>/<run_id>/
run.json config + environment + replay manifest + trial list
summary.json aggregated metrics
events.jsonl structured mission events (whole run)
report.md/.html public report rendered FROM the JSON (JSON is canonical)
scenarios/<id>/trial_<n>.json per-trial result
scenarios/<id>/trial_<n>_workspace/ the mission's exploit workspace
Writes are atomic; a killed run never leaves a half-written JSON.
Sandbox behavior
All benchmark attack execution funnels through the existing sandbox
(tools/sandbox/). With benchmark.sandbox_required: true (the default), a
run without sandbox.enabled marks every trial
INFRASTRUCTURE_ERROR/SANDBOX_FAILED — there is no host-execution
fallback. Runs record sandbox enabled state, image + digest, container id,
network-policy fingerprint, authorized destinations, and blocked/failure
counts. tools/sandbox/family_audit.py is the enforceable registry of every
MCP tool family's containment status (sandboxed vs documented host exception);
tests/test_sandbox_family_audit.py fails when a new subprocess-using family
appears without a registry entry.
WebUI
The Benchmarks nav item opens the dashboard: verified success rate, solved, false-positive rate, median solve time, average cost and sandbox-violation cards; the run panel (suite, scenarios, tags, trials, model, sandbox requirement, baseline options); run history with charts; the comparison view (two arbitrary runs, per-scenario newly-solved/regressed/still-solved/ still-failing); and run detail pages with a live progress view, structured timeline, configuration/environment pins, scenario results table and evidence links. Historical runs survive restarts (everything is on disk).
CI usage
- PR CI runs the fully mocked benchmark test suite (fake suite, fake
mission, fake verifier, fake docker seams) — no model API keys, no live
targets, plus the deterministic
fakesuite smoke path (.github/workflows/benchmark.yml). - Live benchmarks run only via explicit/manual or scheduled jobs with
secrets and the lab target suite up.
--check-regressionexits non-zero on hard regressions so it can gate CI.