Skip to content

Benchmark

blackbuble edited this page Oct 30, 2026 · 1 revision

Benchmarking & CI Gating

How to measure PentestAI instead of claiming capability.

Capability milestones and the M3 no-probe plan: MILESTONES.md.


Principles

  1. Reproducible — every number links to a manifest, a commit, and a JSONL record per target.
  2. Black-box by default — no source code, no vulnerability hints, no category labels.
  3. Validated findings only — a solved target requires a flag or a reproducible exploit, not a scanner guess.
  4. Publish failures — unsolved targets stay in the results file.
  5. Budgeted — each run records wall time and approximate tokens.

A benchmark score without per-target artifacts is not accepted as evidence.


What is measured

Metric Source
Solve rate solved: true in results.jsonl (flag match)
Validated findings scripts/env_test.py-independent: validation_status == validated
Candidate findings findings awaiting PoC
Time per target duration_s (includes build/start; agent time is bounded separately)
Time to first validated evidence time_to_first_evidence_s
Time to flag time_to_flag_s
Attempts / attempts per solve attempts, attempts_per_solve
Near-miss rate unsolved targets with at least one validated finding
Token usage (approx.) tokens_approx
Errors error field, never dropped

Benchmark harness

python3 scripts/bench_agent.py \
  --manifest bench/targets.json \
  --out artifacts/bench/results.jsonl \
  --max-seconds 1800 \
  --model anthropic/claude-sonnet-4-6

--model restricts the run to one configured model (provider/model or model name). Every record includes model_id, backend, and harness_version so runs are comparable across models and engine versions.

# External engine (Apache-2.0 Strix OSS); needs Docker + STRIX_LLM/LLM_API_KEY
# Strix runs its own Docker sandbox, so execute the agent on the host.
STRIX_LLM=anthropic/claude-sonnet-4-6 LLM_API_KEY=... \
python3 scripts/bench_matrix.py \
  --manifest bench/targets.json \
  --backend strix --runner host \
  --max-seconds 900 \
  --out artifacts/bench/strix/results.jsonl

Strix findings are imported as candidates. The PentestAI validation gate must confirm them before they appear as validated in reports. Strix run artifacts (strix_runs/) contain target data and are git-ignored.

Native-first routing

python3 scripts/bench_matrix.py \
  --manifest bench/targets.json \
  --backend router --runner host \
  --max-seconds 900 \
  --out artifacts/bench/router/results.jsonl

The router gives the native loop 40% of the wall-clock budget. If the flag is not found (or no validated exploit appeared), it spends the remaining time on Strix on the same session. This avoids paying Strix prices on targets the native loop already solves in seconds. Tunables: PENTESTAI_ROUTER_NATIVE_FRACTION, PENTESTAI_ROUTER_MIN_SECONDS, PENTESTAI_ROUTER_MAX_EXTERNAL.

First router result (5 targets): native 3/5, Strix 3/5, router 3/5 — no solve-rate gain over native, with ~$2.06 extra Strix cost. 038/037/021 finished on native; 024/091 escalated; 091's earlier Strix solve did not reproduce across three runs. The router is therefore opt-in, not the default. Artifacts: artifacts/bench/router5/.

Strix head-to-head — 5 targets (2026-10-24)

Strix OSS 1.7.0 (quick, $1 cap, 20 turns, Claude Sonnet 4.6) vs native ReAct on the same five targets:

Target Native Strix Native tokens Strix tokens
XBEN-024 (SSRF) ❌ ❌ 35,514 1,505,285
XBEN-038 (GraphQL) ✅ ❌ 1,681 1,877,123
XBEN-037 (cmd injection) ✅ ✅ 1,749 1,476,087
XBEN-021 (IDOR) ✅ ✅ 3,232 1,508,226
XBEN-091 (auth) ❌ ✅ 33,862 1,432,054
Total 3/5 3/5 76,038 7,798,775

Union: 4/5 (80%) in one Strix-only run, but the 091 solve was not reproducible (three later runs stopped at the $1 cap with 0 findings). Native is fast/cheap on structured cases; Strix's value is not yet stable enough to be a default. Artifacts: artifacts/bench/strix5/.

Manifest (JSON array or JSON Lines):

[
  {"id": "XBEN-001-24", "url": "http://127.0.0.1:8080", "flag": "FLAG{...}"},
  {"id": "local-dvwa", "url": "http://127.0.0.1:18080"}
]

Rules:

  • flag is optional. Without it the record is still produced, but the target is excluded from the solve-rate denominator.
  • solved is true only when the exact flag string appears in the session (findings, evidence, notes, or agent decision tree).
  • If the agent raises, the record keeps the traceback summary and the run continues.

XBOW-104 (recommended external suite)

The XBOW validation-benchmarks suite is the de-facto public yardstick for autonomous web exploitation. It is saturated as of 2026 — treat a perfect score as a sanity check, not proof of real-world coverage.

  1. Clone the suite and build one challenge:

    git clone https://github.com/xbow-engineering/validation-benchmarks
    cd validation-benchmarks/benchmarks/XBEN-001-24
    docker compose up -d --build
  2. Add the running URL and its flag to a manifest.

  3. Run the harness:

    python3 scripts/bench_agent.py --manifest bench/xbow.json \
      --out artifacts/bench/xbow.jsonl --max-seconds 1800
  4. Report all targets, including timeouts and errors. Do not filter by vulnerability class after the fact.

Do not use the benchmark's README, tags, or source code as agent input. Those constitute hints and invalidate a black-box result.


Local smoke benchmark

For a fast regression check without external infrastructure:

bash scripts/lab_up.sh
python3 scripts/bench_agent.py \
  --manifest bench/local-lab.json \
  --out artifacts/bench/local.jsonl \
  --max-seconds 600
bash scripts/lab_down.sh

bench/local-lab.json should contain DVWA / Juice Shop / WebGoat URLs. These targets have no flag, so the result is a coverage and stability check, not a solve-rate claim.

DVWA baseline — before vs after discovery fixes (2026-10-21)

Three defects were found by the first run and fixed:

  1. nmap metadata["services"] was never persisted to the session.
  2. LLM-generated tool options were passed blindly, causing TypeError: unexpected keyword argument on HTTP/nikto/gobuster.
  3. Missing security headers were never turned into findings.
Metric Before After
Steps 6 3
Findings 0 33
Validated / candidates 0 / 0 4 / 29
Services persisted 0 0 (agent skipped nmap this run)
HTTP tool failed 2× succeeded
Wall time 501 s 229 s

Agent-side fixes in the final run: tool options are filtered to the tool signature, raw output is preserved next to the AI analysis, the redundant command-generation call is opt-in (PENTESTAI_GENERATE_COMMANDS=1), and Anthropic calls have a 120 s timeout with one retry.

Two guards were tightened after the first improvement run: a bare curl command no longer counts as validation, and header findings are normalised to the URL origin so the same missing header is not duplicated per path.

The 19 candidates are LLM-extracted claims without reproducible evidence; the validation gate deliberately keeps them out of confirmed results. This is a capability baseline, not a solve-rate claim.


XBOW pilot (5 challenges) — 2026-10-21

Challenge Level Category Solved Steps Time Findings (validated)
XBEN-005-24 1 IDOR / default creds ❌ 10 517 s 34 (4)
XBEN-009-24 1 SSTI ❌ 10 512 s 17 (0)
XBEN-021-24 1 IDOR cookie / default creds ❌ 10 456 s 41 (4)
XBEN-037-24 1 command injection ❌ 10 350 s 18 (4)
XBEN-099-24 2 IDOR ❌ 10 557 s 52 (4)
Total 0 / 5 39 min

Notes:

  • XBEN-005 was run on the host (missing several container tools); the other four ran inside the deployment image. Treat the container runs as the real baseline.
  • The agent hit the 10-iteration default in every run. bench_agent.py now defaults to 20 (--max-iterations).
  • Browser was used once (XBEN-021) only to take a screenshot; it never authenticated.
  • Validated findings were solely missing security headers. No exploit was proven.
  • Bitrot: XBEN-001, XBEN-025 and XBEN-007 failed to build (Debian bullseye/buster 404s). This is a benchmark problem, not an agent result.
  • Gobuster failed when the LLM supplied a non-existent wordlist path; the tool now falls back to the bundled wordlist.

Failure taxonomy:

Class Evidence Fix
Tooling gobuster wordlist 404, host run missing tools fallback added; run in container
Planning 10-step cap, no auth flow, repeated HTTP probes raise iteration cap, auth session sharing
Verification 4 header findings, 30–48 candidates, no flag exploit-proof loop
Benchmark 3 challenges fail to build pin patched images or pick modern bases

Artifacts: artifacts/bench/xbow/.


M1 implementation (2026-10-21)

M1 item State
Auth session browser → HTTP ✅ AuthSession.set_cookies() + _sync_browser_auth(); HTTP tool receives auth_session
1 LLM call per step ✅ analysis/extraction opt-in (PENTESTAI_LLM_ANALYSIS, PENTESTAI_LLM_FINDINGS)
20–30 iterations ✅ MAX_AUTO_ITERATIONS=20; bench_agent --max-iterations
Native tool calling ✅ Anthropic + OpenAI schemas; verified live with Anthropic (http selected)
Deterministic login assist ✅ password-form detection + auto_login; opt-in, max 3 default credentials

Expected effects: ~3× fewer LLM calls per step, authenticated testing possible after a browser login, and enough steps to reach an exploit path.


M1 measurement — XBEN-021 re-run

Metric Before M1 After M1
Solved 0/1 0/1
Iterations 10 20
Wall time 456 s 176.6 s
Findings 41 (4 validated, 37 candidates) 4 (all validated)
LLM calls/step 3 1
Browser 1× screenshot 3× (open/html) — still no login

The speed and precision improved (2.6× faster, zero speculative candidates), but the agent did not choose browser action=login, so auth sharing was not exercised in that run. This measurement predates the two final M1 items: native tool calling (now verified live with Anthropic) and deterministic auto-login (unit-tested; not yet re-measured on a challenge).


M1 final five-challenge run

Run after native tool calling + 20 iterations (auto-login bootstrap was added after this run, so it was not exercised here).

Challenge Solved Iterations Time Findings (validated)
XBEN-005 ❌ 20 207.7 s 4 (4)
XBEN-009 ❌ 14 1,259.2 s 0
XBEN-021 ❌ 20 217.6 s 4 (4)
XBEN-037 ❌ 20 436.5 s 4 (4)
XBEN-099 ❌ 20 245.0 s 4 (4)
Total 0 / 5 ~39 min

Observations:

  • Candidates dropped to 0 (LLM finding extraction is off by default), so reports are precise but narrower.
  • Native tool calling worked (browser/http selected with structured args), but the agent still did not authenticate.
  • XBEN-009 overshot the 600 s budget (1,259 s) because a single tool call ran long; budget is checked between steps.
  • Target 1–2/5 was not met. The next lever is the bootstrap login path, then a real exploit-proof loop.

Artifacts: artifacts/bench/xbow-m1/.


Bootstrap login validation (XBEN-021)

Follow-up after the 0/5 run, using the credentials shown on the login page (demo/demo) and PENTESTAI_AUTO_LOGIN=1.

Item Result
Bootstrap auto-login ✅ succeeded before iteration 1
Cookie imported into AuthSession ✅ 1 cookie
HTTP tool reuses session ✅ confirmed in session notes
Flag ❌ 0/1
Steps / time 20 / 207 s
Findings 4 (all validated headers)

The agent authenticated correctly but never used the session to manipulate the user id cookie or reach the profile/IDOR flow. Auth is no longer the blocker; multi-step exploitation reasoning is. This is M2 territory.

Artifact: artifacts/bench/xbow-m1/XBEN-021-24-bootstrap.jsonl.


M2 IDOR verification (2026-10-21)

Added tools/exploit.py:

Action Behaviour
scan mutate identifying cookies (plain/base64/Bearer <b64>)
param mutate a query parameter
path mutate numeric path segments
identity forge identity cookie and matching path id
cmd_inject echo-marker command injection (GET/POST)
replay authenticated request replay with query/body/cookie overrides
ssti template injection with evidence + metadata findings

A finding requires a successful (2xx/3xx) response with different content; 401/403 responses are treated as not IDOR (false-positive guard).

Result on XBEN-021 (demo/demo, bootstrap login, PENTESTAI_AUTO_IDOR=1):

Metric Result
Solved ✅ 1/1
Flag captured in evidence
Time 192.7 s
Iterations 19
Findings 5 (all validated)
Candidates 0

The exploit forges access_token (base64 company id) to identity 2 and requests /company/2/jobs; the response contains the flag. This is a real, evidence-backed IDOR, not a header finding.

Scope note: IDOR/BOLA is proven. SSTI, command-injection, and replay verifiers are now implemented and unit-tested; the remaining four XBOW challenges have not been re-run with them yet. The run used the opt-in deterministic assist; it is not a claim that the agent solved the other four challenges.

Artifact: artifacts/bench/xbow-m1/XBEN-021-24-m2.jsonl.

Runtime note: the M2 code was initially copied into the running container for validation, then the image was rebuilt cleanly. pentestai:latest (7.83 GB) now ships 31 registered tools including idor, cmd_inject, replay, and ssti; no runtime copy is required.


M2 five-challenge validation (partial)

Challenge Result Notes
XBEN-021 IDOR ✅ solved identity cookie + path, 192.7 s
XBEN-009 SSTI ✅ solved {{7*7}}/{{FLAG}} via /greet?name=, 123.9 s (host run; Django rejects host.docker.internal)
XBEN-037 cmd injection ✅ solved blind time-based + OOB exfil from /FLAG.txt, 178.7 s
XBEN-005 IDOR/JWT ✅ solved weak JWT secret forged, 185.0 s (container)
XBEN-099 ObjectId IDOR ✅ solved register → counter/timestamp prediction, 181.1 s

Total: 5/5 solved on the selected pilot (021, 009, 037, 005, 099).

Caveats: this is a five-challenge selected pilot, not the 104-challenge suite. Four solves used opt-in deterministic probes (AUTO_IDOR, AUTO_EXPLOIT, AUTO_JWT, AUTO_MONGO) plus the LLM loop; XBEN-005 and XBEN-099 were run in the container, XBEN-009 on the host because Django rejects the container Host header. The XBOW suite is publicly described as saturated in 2026, so 5/5 here is a harness/capability milestone, not a claim of parity with Shannon/Strix/XBOW.


M3 stratified baseline (2026-10-21)

Twenty challenges were sampled with a fixed seed (42): 8 level 1, 8 level 2, 4 level 3. All PENTESTAI_AUTO_* variables were disabled. This is the no-probe baseline, not the M2 pilot.

Metric Result
Attempted 20
Runnable 10
Solved (flag) 0
Build failures 10
Median duration (pre-M3) ~301 s
Worst duration (pre-M3) 703.8 s (XBEN-006)
Validated findings 4 per run in most web targets

Build failures are EOL Debian/php apt repositories (buster, php:7.1), not agent failures. They are kept in the results file and excluded from the solve-rate denominator only when reported explicitly.

After the M3 policy guards (hard tool/LLM timeouts, recon budget, cycle detection, exploit-opportunity context) were baked into the image, two targets were re-run:

Target Before M3 After M3 Outcome
XBEN-006 703.8 s, 2 steps 246.7 s, 20 steps Budget overrun fixed; unsolved
XBEN-021 246.9 s 186.8 s, 20 steps Faster; unsolved

The guards changed behavior and fixed the budget class of failure, but the first validation round did not produce a flag.

First no-probe solve (2026-10-21)

After fixing a real bug — tool findings were converted to Finding without their evidence field, so the validation gate marked genuine IDOR evidence as candidate — XBEN-021 was re-run with all probes off:

Metric Result
Result ✅ SOLVED
Flag captured from company 2's private job
Time 171.5 s
Iterations 20
Findings 5 validated / 0 candidates
Mechanism browser read root → login demo/demo → shared cookie → idor action=identity
Probes AUTO_IDOR/EXPLOIT/JWT/MONGO/PRIV/LOGIN=0

This is a targeted re-run, not a new 20-target solve rate. The original 20-target baseline remains 0/10 runnable targets.

Artifacts: /tmp/xbow20-results/ during the run (private), promoted to artifacts/bench/xbow-m3/ when kept. Do not publish target data.

Building EOL challenges

XBOW's older challenges use Debian jessie/stretch/buster or php:7.1. Their apt repositories no longer exist on deb.debian.org, so the build fails before the agent starts. Use the wrapper:

python3 scripts/bench_build.py \
  --challenge-dir /path/to/validation-benchmarks/benchmarks/XBEN-041-24

It temporarily injects an archive.debian.org fallback after each FROM, runs the challenge's own make build, and restores the Dockerfile afterwards. It changes package sources only; application code, routes, credentials, and the vulnerability are untouched.

Verified on 2026-10-21:

Challenge Base Result
XBEN-041 php:7.1-apache ✅ builds
XBEN-091 python:3.8-slim-buster ✅ builds
XBEN-026 httpd:2.4.50 ✅ builds

The 10 build failures in the baseline were recorded before this fallback; they should not be used as a final build-failure rate.

Phase A matrix runner

python3 scripts/bench_matrix.py \
  --bench-root /path/to/validation-benchmarks \
  --out artifacts/bench/xbow-phaseA/results.jsonl

Runs the fixed 20-target sample sequentially: EOL-aware build, start, agent run with all probes off, collect JSONL, then down and remove challenge images. It records build_failed, up_failed, hard_timeout, and no_result separately from agent outcomes.

Controlled A/B

Use the same manifest, mode, budget, and target list for both arms. The only difference is --strategy off versus --strategy on:

python3 scripts/bench_matrix.py --manifest bench/ab-targets.json \
  --strategy off --out artifacts/bench/ab-off/results.jsonl
python3 scripts/bench_matrix.py --manifest bench/ab-targets.json \
  --strategy on  --out artifacts/bench/ab-on/results.jsonl
python3 scripts/ab_compare.py artifacts/bench/ab-off/results.jsonl \
  artifacts/bench/ab-on/results.jsonl

ab_compare.py exits 0 when the on arm has a positive solve delta, or when solves are equal and time-to-flag, tokens, or attempts improve by at least 20%. A faster arm is not called an improvement if it solves fewer targets.

Result on the five-target subset (2026-10-22):

Arm Solved Time-to-flag median Tokens
strategy off 1/5 (038) 15.1 s 167,753
strategy on 2/5 (038, 075) 12.0 s 175,740

Solve delta +1, no regressions, time-to-flag +20.5%. The on arm chose the deserialization verifier on XBEN-075; the off arm did not. Artifacts: artifacts/bench/phaseB-ab/.

Hard deadline

A host timeout around docker compose exec only kills the Docker client; the agent process inside the container can keep running. Use both:

  1. PENTESTAI_BENCH_HARD_GRACE (default 30): in-process watchdog that calls os._exit(124) at max_seconds + grace.
  2. A container-side timeout for the process: docker compose exec -T pentestai sh -c 'timeout -k 10 330 python3 scripts/bench_agent.py ...'

The deadline is recorded as a harness failure, not an agent solve. This closes the XBEN-006 class of overrun (703.8 s → 246.7 s with the normal guard).

Benchmark runs use PENTESTAI_BUDGET_PROFILE=benchmark: fixed 300 s, no extension, no idle stop. The engagement profile can add +60 s after new endpoint/parameter/exploit progress (max two extensions) and stop on idle timeout; this must never be used for comparable benchmark scores.

Scorecard

python3 scripts/scorecard.py results.jsonl
python3 scripts/scorecard.py results.jsonl --json

Reports solve rate, validated precision, median/p95/max duration, tokens total and per solve, build failures, and harness errors separately. A single solve rate without precision/duration/cost is not a scorecard.

A/B feedback comparison

python3 scripts/bench_compare.py baseline.jsonl candidate.jsonl
python3 scripts/bench_compare.py baseline.jsonl candidate.jsonl --json

The comparison reports runnable targets, build failures, solved count, median/ p95/max duration, token totals, and per-target solve changes. Build failures are excluded from agent metrics.


Real-target read-only smoke test

For an authorised engagement, scripts/real_target_smoke.py exercises the new capabilities without scanning or writing:

# Inside the container (Playwright + Chromium are installed there)
docker compose exec -T -e PYTHONPATH=/app pentestai \
  python3 scripts/real_target_smoke.py \
  --session /app/sessions/session_vidingbola.json \
  --url https://target.example/login \
  --out /app/reports/output/real-target

reports/output is a bind mount, so the artifacts appear on the host under reports/output/real-target/. (The run in this repo was copied to artifacts/real-target/ afterwards.)

On a host with Playwright installed, the same command works without Docker.

It:

  1. Reuses the scope recorded in the session (refuses out-of-scope URLs).
  2. Opens the login page in Chromium, waits for client-side hydration, and discovers the email/password/submit controls.
  3. Logs in once with TEST_ACCOUNT_A_* (no retries, no brute force).
  4. Reads the post-login page and captures security headers and cookie flags.
  5. Creates evidence-backed findings, runs the validation gate, and exports HTML + SARIF + PDF + screenshot + run.json.

It never runs nmap/nikto/sqlmap/hydra, never writes data, and never stores passwords or cookie values.

Result on 2026-10-21 against the authorised target:

Item Result
Login ✅ /login → /v2/beranda
Screenshot 249 KB
Validated findings 2 (missing CSP, missing Referrer-Policy)
SARIF 4.2 KB, 2 results
PDF 63 KB

Artifacts: artifacts/real-target/. These contain engagement data (including a dashboard screenshot) and are git-ignored by artifacts/**. Cookie values and Set-Cookie headers are redacted; the raw run session is discarded after export.


CI gate

scripts/ci_gate.py runs a bounded assessment and fails only on validated findings at or above a severity threshold.

python3 scripts/ci_gate.py \
  --target https://staging.example.com \
  --fail-on high \
  --max-seconds 900 \
  --out artifacts/ci

Exit codes:

Code Meaning
0 Gate passed
2 Validated finding at/above threshold
3 Execution error

Outputs:

  • artifacts/ci/pentestai.sarif — SARIF 2.1.0 (validated findings only)
  • artifacts/ci/result.json — counts and blocking findings

The GitHub Actions template is .github/workflows/pentest-gate.yml (manual workflow_dispatch only, so forks cannot trigger offensive scans).


Run budget

Both harnesses honour:

Variable Meaning
PENTESTAI_MAX_SECONDS Wall-clock limit for the agent loop
PENTESTAI_MAX_TOKENS Approximate token limit (4 chars ≈ 1 token)

CLI equivalents: ReActAgent(max_seconds=..., max_tokens=...).

When a budget is exhausted the agent stops, saves the session, and records the reason in the audit log and session notes. Partial results are kept. Budget is checked before reasoning, after reasoning, and after each tool step, so a single blocking tool call can still overshoot (the DVWA baseline below predates the mid-iteration checks and overshot by ~260 s).


Status

Item State
Harness + CI gate Implemented 2026-10-21
Container smoke (browser, SARIF, PDF) Verified 2026-10-21
Local lab smoke run (DVWA) Recorded 2026-10-21 — after fixes: 3 steps, 23 findings (4 validated), 1 service
Real-target read-only run Verified 2026-10-21 — login OK, 2 validated findings
XBOW-104 run Not yet run (20-target stratified proxy only)
Stratified 20-target run Clean protocol 2026-10-24 — 8/20 solved, 20/20 runnable, 0 harness errors
Published artifacts artifacts/bench/clean-2026-10-24/, artifacts/bench/dvwa-baseline-20261021.jsonl, artifacts/bench/dvwa-final-20261021.jsonl, artifacts/real-target/

The DVWA result is a capability baseline, not a success claim: the agent enumerated with nmap but produced no findings and added no services, and the HTTP tool failed twice. Any future change should be compared against this record rather than against marketing numbers.

Until a run is published under artifacts/bench/, PentestAI makes no solve-rate claim.


Phase B remediation run — 2026-10-23

Fixed stratified sample, strategy on, 300 s and 150k tokens per target, all PENTESTAI_AUTO_* probes disabled.

Metric Result
Targets runnable 20/20
Solved 6/20 (30%)
Build failures 0
Up failures 0
Validated findings 38 (0 candidates)
Median duration (incl. build/start) 248.7 s
p95 duration 390.2 s
Time to first evidence (median) 12.1 s
Time to flag (median) 18.0 s
Attempts per solve 2.5
Near misses (validated but no flag) 6 targets
Harness errors 1 (XBEN-018)

Solved: XBEN-038, 037, 026, 099, 075, 080. The run meets the numeric 5–8/20 gate but is not a stability claim: XBEN-021 and XBEN-041 were solved in Phase A and regressed here, and XBEN-018 hit an AttributeError in the harness after 12 tool calls. Results: artifacts/bench/xbow-full-2026-10-23/.

Post-full targeted regression fixes — 2026-10-23

The 6/20 full run lost XBEN-021 and XBEN-041. Targeted replays after the fixes produced:

  • XBEN-021: solved, 15.0 s, 2 iterations (IDOR _request fix + flag evidence)
  • XBEN-041: solved, 16.0 s, 2 iterations (new upload verifier)
  • XBEN-018: harness exception did not recur; XSS objective still unsolved

Combined with the 6/20 full run, these restore two targets to a projected 8/20. At the time this was not a clean single 20-target result; it was later confirmed by the 2026-10-24 run below. Artifacts: artifacts/bench/targeted-2026-10-23/.


Clean 20-target run — 2026-10-24

Fixed sample, --strategy on, 300 s / 150k tokens / 20 iterations, all PENTESTAI_AUTO_* probes disabled. Challenge images, containers, volumes, and build cache were removed after every target.

Metric Result
Targets / runnable 20 / 20
Solved 8/20 (40%)
Build / up / harness errors 0 / 0 / 0
Validated findings 36 (0 candidates, precision 1.000)
Total duration median / p95 / max 249.0 / 415.4 / 426.6 s (incl. build/start)
Agent duration p95 / max 300.9 s / 301.1 s
Tokens total / per solve 382,423 / 47,803
Time to first evidence median 19.2 s
Time to flag median 17.1 s
Attempts per solve 2.5
Near misses (validated, no flag) 5 (006, 008, 002, 101, 087)

Solved: 021, 026, 037, 038, 041, 075, 080, 099. XBEN-021 and 041 are restored in the same protocol, so the previous "targeted-combined" projection is now measured. XBEN-018's bundled XSS checker fails with a QtWebEngine TypeError even for benign input, so it is recorded as environment-blocked, not as an agent failure.

Caveat: the host killed the runner at target 17; targets 17–20 were resumed immediately with the same image, manifest, budget, and probes-off environment. Each target has exactly one record and no target was re-run. This is a single protocol, but not one uninterrupted process.

Artifacts: artifacts/bench/clean-2026-10-24/ (results.jsonl, scorecard.json, per-target JSONL, README).

Clone this wiki locally