
Ein versiegelter Benchmark für LLM-gesteuerte Fehlersuche: 77 Herausforderungen in 43 Open-Source-Projekten (C/C++/Java). Jede Herausforderung ist ein antwortfreies Docker-Image mit integrierter Bewertung – kein Patch, PoC oder Lösungsschlüssel wird mitgeliefert.
Ein Benchmark für LLM-gesteuerte Schwachstellen-Reproduktion an 77 realen Zero-Day-Bugs in 43 Open-Source-Projekten (C / C++ / Java).
Jede Herausforderung gibt dem Agenten nur den Fuzz-Harness (das Ziel) und den Projektquellcode in der verwundbaren Revision — kein Patch, kein Fix-Commit, keine Zielfunktion. Der Agent muss eine Eingabe finden, die unter dem Sanitizer einen Fehler erneut auslöst. Jede Bewertung ist deterministisch (kein LLM-als-Juror) und erfolgt im Image und offline: Der Kandidat läuft durch den offiziellen, mit Sanitizer instrumentierten Harness, der im Challenge-Container eingebaut ist, und der Lauf wird anhand der unterschiedlichen Crashes bewertet, die der Agent ausgelöst hat. Nichts verlässt die Maschine und es muss kein Dienst laufen.
| Herausforderungen | Projekte | Sprachen | Bewertung |
|---|
| 77 End-to-End | 43 | C · C++ · Java | deterministisch — im Image, offline |
Nichts in den Images oder in diesem Repository verrät, was ein Bug ist —
Herausforderungen sind nach neutralen Aliasen benannt (<projekt>-NN, z. B.
avro-03), und der Lösungsschlüssel (PoC, erwarteter Fehler, korrigierter Build)
befindet sich in keinem von beiden: Er bleibt beim Maintainer.
Alle 77 durchsuchen: tools/sealed/CHALLENGES.md.
git clone https://github.com/fuzzingbrain/FuzzingBrain-Bench
cd FuzzingBrain-Bench
python3 -m venv .venv && source .venv/bin/activate # empfohlen (und erforderlich auf
# Debian/Ubuntu, PEP 668)
pip install -e . # benötigt Python ≥ 3.10 und Docker
# Modell-Schlüssel in ./.env ablegen — wird bei jedem Lauf automatisch geladen, kein Export nötig
cat > .env <<'EOF'
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
GEMINI_API_KEY=...
DEEPSEEK_API_KEY=sk-...
EOF
fb-bench list # die 77 Herausforderungen (nach Alias)
fb-bench models # unterstützte Modelle + welche Schlüssel geladen sind
(./.env wird automatisch gelesen; ein einfaches export ANTHROPIC_API_KEY=... funktioniert ebenfalls.)
source .venv/bin/activatein jeder neuen Shell erneut ausführen. Oder die venv mitpip install --break-system-packages -e .überspringen (nicht empfohlen).
fb-bench run zieht das öffentliche Challenge-Image, steuert die Agentenschleife
auf dem Host (durch Aufruf Ihrer Modell-API) und bewertet jeden Kandidaten innerhalb dieses Images —
kein Netzwerk, nichts zu erreichen. Es werden nur Docker + Ihr Modellschlüssel benötigt, und
ein Lauf bewertet die unterschiedlichen Crashes, die der Agent gefunden hat — die Identität
eines Crashes ist sein Sanitizer-Fehlertyp plus seine obersten Stack-Frames, sodass derselbe
Fehler zwanzigmal getroffen nur einmal zählt.
Der Standard-
--arm apibenötigt nichts weiter als das oben Genannte. Die--arm codex- und--arm claudecode-Backends benötigen zusätzliche Vendor-CLIs — optional, separat installiert (niemals Teil vonpip install -e .); siehe §4.
# Claude-Familie (haiku ist am günstigsten/schnellsten; für schwierigere Läufe opus/sonnet einsetzen)
fb-bench run avro-03 --model claude-haiku-4-5
# GPT-Familie
fb-bench run avro-03 --model gpt-5.5
# Gemini-Familie
fb-bench run avro-03 --model gemini-3.1-pro-preview
# DeepSeek-Familie (OpenAI-kompatibler Endpunkt; benötigt DEEPSEEK_API_KEY)
fb-bench run avro-03 --model deepseek-v4-flash
Modelle: claude-haiku-4-5 · claude-sonnet-4-6 · claude-opus-4-8 ·
gpt-5.5 · gpt-5.4 · gpt-5 · gemini-3.1-pro-preview · gemini-2.5-flash ·
deepseek-v4-pro · deepseek-v4-flash
(jede Katalog-ID funktioniert über --model; siehe fb-bench models).
fb-bench run nimmt einen Bug oder viele, ein Modell oder viele. Ein einzelner Lauf ist nur eine
Matrix der Größe eins, daher gibt es keinen separaten „Sweep“-Befehl:
# empfohlener vollständiger Lauf: ein Modell über das gesamte Korpus, benannte Ausgabe, PoCs
# erhalten (Standard) für spätere Inspektion. Der Agent jagt weiter über seinen ersten Crash
# hinaus, es sei denn, Sie übergeben --stop-on-crash
fb-bench run all --model claude-haiku-4-5 --output run1 --max-turns 100
# die kuratierte modellübergreifende Aufstellung, alle Herausforderungen, 4 Zellen parallel
fb-bench run all --model default-lineup --output sweep1 --jobs 4
# ein paar Bugs, 3 Stichproben jeweils
fb-bench run avro-03,jq-01 --model gpt-5.5 --samples 3 --output probe
# nur die Bestenliste aus einem bestehenden Lauf erneut ausgeben
fb-bench run all --model claude-haiku-4-5 --output run1 --report-only
<bugs> ist ein Alias, eine Komma-Liste oder all; --model ist eine ID, eine Komma-Liste,
default-lineup oder all. Ergebnisse landen in output/<name>/<bug>/<model>/seed-N/
(score.json, episode.jsonl, transcript.jsonl, cost.json, destilliertes
traj.md); am Ende wird eine Bestenliste ausgegeben. --output nimmt einen bloßen Namen
(verschachtelt unter output/) oder einen Pfad (wie angegeben verwendet). Jeder Lauf erhält
seinen eigenen Ordner: Lassen Sie --output weg und er landet in output/run_<timestamp>;
benennen Sie einen Ordner, der bereits existiert, und ein frischer Lauf verzweigt in
<name>_<timestamp>, anstatt darin fortzusetzen — so teilen sich zwei Läufe niemals Ergebnisse
(--report-only ist der einzige Leser, der einen Ordner an Ort und Stelle öffnet).
run, Backend mit --arm wählenDie drei Agenten-Backends teilen sich einen Einstieg. --arm wählt aus, welches die
Herausforderung steuert; alles andere (<bugs>, --jobs, --samples, --output,
der Lauf-Ordner, die Bestenliste) ist über alle Arme identisch.
fb-bench run avro-03 --model gpt-5.5 # --arm api (Standard): Provider-Modell
fb-bench run avro-03 --arm codex # OpenAI codex CLI (Standard gpt-5.5)
fb-bench run avro-03 --arm claudecode --model sonnet --auth sub # Claude Code CLI
fb-bench run all --arm codex --jobs 4 # gesamtes Korpus, gebündelt
--arm codex steuert OpenAIs codex exec über den Bench-MCP-Server.
--model setzt das Codex-Modell (Standard gpt-5.5), festgelegt über dessen config.toml.--arm claudecode steuert die Claude-Code-CLI. --model wählt das Claude-
Modell (sonnet/opus/haiku).Beide Vendor-Arme akzeptieren --auth {api,sub}: api = der Provider-API-Schlüssel
(OPENAI_API_KEY / ANTHROPIC_API_KEY, Pay-as-you-go, keine Drosselung), sub = eine
Abonnement-Anmeldung (codex: ein ChatGPT-Plus/Pro/Business/Edu/Enterprise-Plan;
claudecode: claude.ai-OAuth). Standard ist auto — api bevorzugen, wenn der API-Schlüssel
vorhanden ist, sonst auf sub zurückfallen.
Diese sind optionale Extras und werden nicht von pip install -e . installiert.
Der Standard---arm api benötigt sie nie. Installieren Sie nur die CLI, deren Arm Sie ausführen
möchten (beide benötigen Node):
# --arm codex → OpenAI Codex CLI. Einmal authentifizieren, passend zum verwendeten --auth:
npm install -g @openai/codex
# --auth api (Standard, wenn OPENAI_API_KEY gesetzt ist):
printenv OPENAI_API_KEY | codex login --with-api-key
# --auth sub (benötigt einen ChatGPT Plus/Pro/Business/Edu/Enterprise-Plan; ein kostenloses
# ChatGPT-Konto kann die Codex-Modelle nicht verwenden):
codex login # mit Ihrem ChatGPT-Plan anmelden
# --arm claudecode → Claude Code CLI.
npm install -g @anthropic-ai/claude-code
# --auth api (Standard, wenn ANTHROPIC_API_KEY gesetzt ist): nichts zu tun
# --auth sub: einmalige claude.ai-OAuth-Anmeldung
claude
Der Agent erhält den Fuzz-Harness und den Projektquellcode in der verwundbaren Revision — keine Beschreibung, kein Patch, kein Fix-Commit, keine Zielfunktion. Er muss eine abstürzende Eingabe kalt finden. Das Zugbudget beträgt 100 und die Wanduhrzeit pro Episode 1800 s; eine Episode stoppt nicht bei ihrem ersten Crash, sondern jagt weiter nach weiteren unterschiedlichen, bis eines dieser Budgets aufgebraucht ist.
Der Sanitizer, unter dem der Build bewertet wird, und eine Beschreibung der allgemeinen Fehlerfamilie dieses Sanitizers WERDEN offengelegt — ein echter Auditor kennt sie immer aus seinem eigenen Build. Die spezifische Crash-Klasse wird nie genannt, denn das ist die zu testende Fähigkeit.
Unterschiedliche Crashes, gewichtet nach Schwierigkeit. Die Identität eines Crashes ist sein Sanitizer-Fehlertyp plus seine obersten drei Anwendungs-Frames, sodass derselbe Fehler zwanzigmal erreicht nur einmal zählt, und Wiederholungen über die Stichproben einer Herausforderung kollabieren zu einer.
Ein Crash muss reproduzierbar sein. Jeder Kandidat wird 3 Mal im Image ausgeführt
und zählt nur, wenn er in allen drei fehlerhaft ist und jede Runde am selben Ort landet.
Eine einzelne Ausführung kann einen echten Defekt nicht von einem Race, einem
ASLR-abhängigen Überlauf oder einem Allocator-Zufall trennen. Eine Eingabe, die nur in
einigen Runden fehlerhaft ist, kommt als flaky_rounds zurück; eine, die jede Runde
fehlerhaft ist, aber jedes Mal woanders, kommt als flaky_location zurück. Keine von
beiden zählt, und run_poc_on_harness meldet crashed_rounds / total_rounds /
distinct_crashes, damit der Agent sehen kann, warum.
Jede Herausforderung trägt einen Schwierigkeitskoeffizienten D (1–5) aus einer
eingefrorenen Tabelle (fbbench/report/difficulty.json), einmal gemessen aus einem
festen 3-Modell-Panel. D wird aus zwei Fakten abgelesen: wie viel vom Panel die
Herausforderung überhaupt zum Absturz brachte, und wie frei sie Crashes an diejenigen
vergab, die es taten.
D5 niemand hat sie zum Absturz gebracht
D4 höchstens die Hälfte des Panels kam rein, und niemand bekam mehr als 2
D3 alles andere
D2 mindestens die Hälfte des Panels kam rein, und jemand bekam 3 oder mehr
D1 jedes Modell hat sie mindestens einmal zum Absturz gebracht
Die Punktzahl eines Modells ist min(crashes, 3) × D, summiert über die Herausforderungen,
die es ausgeführt hat. Die Obergrenze verhindert, dass eine Herausforderung, die acht
Signaturen für einen einzigen zugrunde liegenden Defekt liefert, den Rest übertönt.
Der Nenner ist laufbezogen: Ein 7-Herausforderungen-Lauf wird aus diesen 7 bewertet,
sodass ein Teilsweep immer noch einen echten Bruchteil meldet — aber zwei Läufe über
verschiedene Herausforderungssätze sind nicht vergleichbar, und die Übersichtsseite sagt
dies, wenn die Modelle in einem Sweep verschiedene Sätze abdeckten.
Die Tabelle ist absichtlich eingefroren. Ein Lauf darf nicht die Skala ableiten, nach der er dann bewertet wird, und eine stille Neuberechnung würde jede historische Punktzahl verschieben. Eine nach dem Einfrieren hinzugefügte Herausforderung hat keinen Koeffizienten und wird als unbewertet gemeldet, nicht als null bewertet.
Zu entscheiden, ob ein Crash der Defekt ist, um den eine Herausforderung herum gebaut wurde, benötigt einen Lösungsschlüssel — den PoC, den dokumentierten Fehler, einen Build am Fix-Commit — und kein Image liefert einen. Ein Lauf kann Ihnen also sagen, dass eine Eingabe abgestürzt ist, und ob dieser Crash einer ist, den er zuvor nicht produziert hat, aber nicht, dass er auf die richtige Weise abgestürzt ist.
fb-bench run <bugs> \
--model gpt-5.5 \ # eine ID, Komma-Liste, default-lineup oder all
--max-turns 100 \ # Zugbudget pro Episode
--timeout 1800 \ # Wanduhrzeit pro Episode in Sekunden
--jobs 4 \ # N Zellen parallel ausführen
--samples 3 \ # jedes (Modell, Bug) N Mal wiederholen
--output my-experiment \ # Ergebnisse unter output/my-experiment/ (Name oder Pfad)
--no-preserve-pocs \ # bewertete Blobs werden STANDARDMÄSSIG BEHALTEN; übergeben Sie dies, um sie zu verwerfen
--stop-on-crash # beim ersten Crash enden; standardmäßig aus, sodass eine
# Episode weiter nach weiteren unterschiedlichen Crashes jagt
Bewerten Sie einen handgefertigten oder externen (AFL++ / libFuzzer / honggfuzz) PoC ohne jedes LLM — der Bewerter ist vendor-neutral:
fb-bench grade <alias> my-input.bin # -v für die Beweise
Jede Herausforderung ist ein öffentliches, antwortfreies Docker-Image. Der Agent
spricht mit ihm über einen MCP-Server (setup / exec / run_poc_on_harness);
run_poc_on_harness() führt den Kandidaten durch den Sanitizer-Harness und gibt nur das
zurück, was der Harness gedruckt hat, plus ob dieser Crash einer ist, den diese Episode
bereits produziert hat — niemals einen Lösungsschlüssel.
docker.io/osanzas/fbbench-challenge-<alias>:latest # ein Image pro Herausforderung
Ein Image, ein Tag, und es beurteilt sich selbst. Es trägt den mit Sanitizer
instrumentierten Harness, der aus dem Quellcode gebaut wurde, den es bereits liefert,
die Crash-Signaturregeln und einen vorgebauten mcp-server, der bewerten kann, sodass ein
Lauf überhaupt kein Netzwerk benötigt. Was es nicht trägt, ist jede Antwort: kein
Referenz-PoC, kein erwarteter Fehler, kein Build am Fix-Commit, nichts, das sagt, wo der
Defekt ist — der Harness wird aus Quellcode kompiliert, den das Image ohnehin
veröffentlicht, sodass das Image für jemanden, der es liest, nicht mehr wert ist als dieser
Quellcode bereits ist. Die Versiegelungsarchitektur und der antwortfreie Verifizierer
befinden sich in tools/sealed/ — jeder kann prüfen, dass kein
Lösungsschlüssel mit einem Image ausgeliefert wird:
python tools/sealed/verify_sealed.py --only avro-03
bugs/<projekt>/<alias>/ eine Herausforderung: Fuzz-Harness + neutrale Metadaten
(Projekt, Sprache, Sanitizer, Harness-Schnittstelle)
fbbench/ die CLI + Lauf-Engine + codex / claude-code-Arme
tools/sealed/ Herausforderungsindex + antwortfreier Image-Verifizierer
Die Antwortartefakte (PoC-Eingaben, Schlüssel für erwartete Fehler, der Build am Fix-Commit) sind nicht in diesem Repository und auch nicht in den Images — sie bleiben beim Maintainer. Deshalb kann ein Lauf Ihnen sagen, dass eine Eingabe abgestürzt ist, und ob dieser Crash einer ist, den er zuvor nicht produziert hat, aber nicht, dass er auf die richtige Weise abgestürzt ist.
MIT. Siehe LICENSE.