
A sealed benchmark for LLM-driven bug discovery: 77 challenges across 43 open-source projects (C/C++/Java). Each challenge is an answer-free Docker image with in-image grading — no patch, Poc or answer key ships.
LLM 기반 취약점 재현을 위한 벤치마크 — 43개 오픈소스 프로젝트(C / C++ / Java)의 실제 제로데이 버그 77건 대상.
각 챌린지는 에이전트에게 퍼즈 하니스(타깃)와 취약한 리비전의 프로젝트 소스만 제공합니다 — 패치, 수정 커밋, 타깃 라인은 없습니다. 에이전트는 샌나타이저(sanitizer) 하에서 결함을 다시 트리거하는 입력을 발견해야 합니다. 모든 채점은 결정적(LLM-as-judge 없음)이며 이미지 내부에서 오프라인으로 이루어집니다: 후보는 챌린지 컨테이너에 내장된 공식 샌나타이저 계측 하니스를 통해 실행되고, 에이전트가 트리거한 고유 크래시(distinct crashes)로 점수가 매겨집니다. 어떤 것도 머신 밖으로 나가지 않으며 서비스가 떠 있을 필요도 없습니다.
| 챌린지 | 프로젝트 | 언어 | 채점기 |
|---|---|---|---|
| 77개 종단 간 | 43개 | C · C++ · Java | 결정적 — 이미지 내부, 오프라인 |
이미지나 이 저장소에는 버그가 무엇인지 밝히는 어떤 것도 없습니다 — 챌린지는 중립 별칭(<project>-NN, 예: avro-03)으로 명명되며, 정답 키(PoC, 예상 결함, 수정 빌드)는 어디에도 없습니다: 유지보수자만 보관합니다.
77개 전체 보기: tools/sealed/CHALLENGES.md
git clone https://github.com/fuzzingbrain/FuzzingBrain-Bench
cd FuzzingBrain-Bench
python3 -m venv .venv && source .venv/bin/activate # 권장 (Debian/Ubuntu에서는 필수,
# PEP 668)
pip install -e . # Python ≥ 3.10 및 Docker 필요
# 모델 키를 ./.env에 넣으세요 — 모든 실행 시 자동 로드, export 불필요
cat > .env <<'EOF'
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
GEMINI_API_KEY=...
DEEPSEEK_API_KEY=sk-...
EOF
fb-bench list # 77개 챌린지 (별칭 기준)
fb-bench models # 지원 모델 + 로드된 키 확인
(./.env는 자동으로 읽힙니다; 일반 export ANTHROPIC_API_KEY=...도 동작합니다.)
새 셸마다
source .venv/bin/activate를 다시 실행하세요. 또는 venv 없이pip install --break-system-packages -e .를 사용할 수도 있습니다(권장하지 않음).
fb-bench run은 공개 챌린지 이미지를 가져오고, 호스트에서 에이전트 루프를 구동하며(모델 API 호출), 모든 후보를 그 이미지 내부에서 채점합니다 — 네트워크 없음, 연결할 대상 없음. Docker와 모델 키만 있으면 되며, 실행은 에이전트가 찾은 고유 크래시를 점수로 매깁니다 — 크래시의 정체는 샌나타이저 결함 유형과 상위 스택 프레임이므로, 같은 결함을 스무 번 맞아도 한 번으로 계산됩니다.
기본
--arm api는 위의 것만으로 충분합니다.--arm codex및--arm claudecode백엔드는 추가 벤더 CLI — 선택 사항이 필요하며, 별도로 설치합니다(pip install -e .에 포함되지 않음); §4 참조.
# Claude 계열 (haiku가 가장 저렴/빠름; 더 어려운 실행에는 opus/sonnet으로 교체)
fb-bench run avro-03 --model claude-haiku-4-5
# GPT 계열
fb-bench run avro-03 --model gpt-5.5
# Gemini 계열
fb-bench run avro-03 --model gemini-3.1-pro-preview
# DeepSeek 계열 (OpenAI 호환 엔드포인트; DEEPSEEK_API_KEY 필요)
fb-bench run avro-03 --model deepseek-v4-flash
모델: 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
(--model로 카탈로그 ID 아무거나 사용 가능; fb-bench models 참조).
fb-bench run은 버그 하나 또는 다수, 모델 하나 또는 다수를 받습니다. 단일 실행은 크기 1의 행렬일 뿐이므로 별도의 "스윕(sweep)" 명령은 없습니다:
# 권장 전체 실행: 전체 코퍼스에 모델 하나, 이름 있는 출력, PoC 보존(기본값) — 나중에 검사용.
# 에이전트는 --stop-on-crash를 전달하지 않는 한 첫 크래시 이후에도 계속 탐색합니다
fb-bench run all --model claude-haiku-4-5 --output run1 --max-turns 100
# 선별된 교차 모델 로스터, 전체 챌린지, 4개 셀 병렬
fb-bench run all --model default-lineup --output sweep1 --jobs 4
# 버그 몇 개, 각각 3개 샘플
fb-bench run avro-03,jq-01 --model gpt-5.5 --samples 3 --output probe
# 기존 실행에서 리더보드만 다시 출력
fb-bench run all --model claude-haiku-4-5 --output run1 --report-only
<bugs>는 별칭 하나, 쉼표 목록 또는 all입니다; --model은 ID 하나, 쉼표 목록, default-lineup 또는 all입니다. 결과는 output/<name>/<bug>/<model>/seed-N/에 저장됩니다(score.json, episode.jsonl, transcript.jsonl, cost.json, 요약된 traj.md); 마지막에 리더보드가 출력됩니다. --output은 단순 이름(output/ 아래 중첩) 또는 경로(그대로 사용)를 받습니다. 모든 실행은 고유 폴더를 가집니다: --output을 생략하면 output/run_<timestamp>에 저장됩니다; 이미 존재하는 폴더 이름을 지정하면 새 실행이 그 안으로 이어지지 않고 로 분기합니다 — 따라서 두 실행이 결과를 공유하지 않습니다(만이 폴더를 그 자리에서 여는 유일한 읽기 모드입니다).
run, 백엔드는 --arm으로 선택세 가지 에이전트 백엔드는 하나의 진입점을 공유합니다. --arm이 챌린지를 구동할 백엔드를 선택합니다; 나머지 모든 것(<bugs>, --jobs, --samples, --output, 실행별 폴더, 리더보드)은 모든 arm에서 동일합니다.
fb-bench run avro-03 --model gpt-5.5 # --arm api (기본값): 제공자 모델
fb-bench run avro-03 --arm codex # OpenAI codex CLI (기본 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 # 전체 코퍼스, 배치 처리
--arm codex**는 벤치 MCP 서버를 통해 OpenAI의 codex exec를 구동합니다.
--model은 codex 모델을 설정합니다(기본 gpt-5.5), config.toml로 고정.--arm claudecode**는 Claude Code CLI를 구동합니다. --model은 claude 모델을 선택합니다(sonnet/opus/haiku).두 벤더 arm 모두 **--auth {api,sub}**를 받습니다: api = 제공자 API 키(OPENAI_API_KEY / ANTHROPIC_API_KEY, 종량제, 스로틀 없음), sub = 구독 로그인(codex: ChatGPT Plus/Pro/Business/Edu/Enterprise 요금제; claudecode: claude.ai OAuth). 기본값은 auto — API 키가 있으면 api를 선호하고, 없으면 sub로 폴백합니다.
이들은 선택적 추가 기능이며 pip install -e .로 설치되지 않습니다.
기본 --arm api는 이들이 필요 없습니다. 실행할 arm의 CLI만 설치하세요(둘 다 Node 필요):
# --arm codex → OpenAI Codex CLI. 사용할 --auth에 맞춰 한 번 인증:
npm install -g @openai/codex
# --auth api (OPENAI_API_KEY가 설정된 경우 기본값):
printenv OPENAI_API_KEY | codex login --with-api-key
# --auth sub (ChatGPT Plus/Pro/Business/Edu/Enterprise 요금제 필요; 무료
# ChatGPT 계정은 codex 모델을 사용할 수 없음):
codex login # ChatGPT 요금제로 로그인
# --arm claudecode → Claude Code CLI.
npm install -g @anthropic-ai/claude-code
# --auth api (ANTHROPIC_API_KEY가 설정된 경우 기본값): 할 일 없음
# --auth sub: 일회성 claude.ai OAuth 로그인
claude
에이전트는 퍼즈 하니스와 취약한 리비전의 프로젝트 소스를 받습니다 — 설명, 패치, 수정 커밋, 타깃 라인 없음. 차갑게 크래시 입력을 찾아야 합니다. 턴 예산은 100, 에피소드당 벽시계 시간은 1800초입니다; 에피소드는 첫 크래시에서 멈추지 않고 예산 중 하나가 소진될 때까지 더 많은 고유 크래시를 계속 찾습니다.
빌드가 채점되는 샌나타이저와 해당 샌나타이저의 일반적인 결함 계열에 대한 설명은 공개됩니다 — 실제 감사자는 항상 자신의 빌드에서 이를 알고 있습니다. 특정 크래시 클래스는 절대 명시되지 않습니다. 그것이 바로 테스트 대상 능력이기 때문입니다.
난이도 가중치가 적용된 고유 크래시. 크래시의 정체는 샌나타이저 결함 유형과 상위 3개 애플리케이션 프레임이므로, 같은 결함에 스무 번 도달해도 한 번으로 계산되며, 챌린지 샘플 간 반복도 하나로 합쳐집니다.
크래시는 재현되어야 합니다. 모든 후보는 이미지 내부에서 3회 실행되며, 세 번 모두 결함이 발생하고 그리고 매 라운드가 같은 위치에 도달해야만 인정됩니다. 단일 실행으로는 실제 결함과 레이스, ASLR 의존 오버플로 또는 할당자 우연을 구분할 수 없습니다. 일부 라운드에서만 결함이 발생하는 입력은 flaky_rounds로 반환됩니다; 매 라운드 결함이 발생하지만 매번 다른 위치인 경우 flaky_location으로 반환됩니다. 둘 다 점수를 얻지 못하며, run_poc_on_harness는 crashed_rounds / total_rounds / distinct_crashes를 보고하므로 에이전트가 이유를 알 수 있습니다.
각 챌린지는 고정된(frozen) 테이블(fbbench/report/difficulty.json)의 난이도 계수 **D (1–5)**를 가집니다. 이는 고정된 3-모델 패널로 한 번 측정됩니다. D는 두 가지 사실에서 읽힙니다: 패널 중 얼마나 많은 모델이 챌린지를 크래시시켰는지, 그리고 크래시를 얼마나 자유롭게 내놓았는지.
D5 아무도 크래시시키지 못함
D4 패널의 절반 이하만 진입했고, 아무도 2개 이상 얻지 못함
D3 그 외의 경우
D2 패널의 절반 이상이 진입했고, 누군가 3개 이상 얻음
D1 모든 모델이 최소 한 번 크래시시킴
모델 점수는 실행한 챌린지에 대해 min(crashes, 3) × D의 합입니다. 상한은 단일 근본 결함에 대해 8개 시그니처를 산출하는 챌린지가 나머지를 압도하는 것을 방지합니다. 분모는 실행 범위로 한정됩니다: 7-챌린지 실행은 그 7개 기준으로 점수가 매겨지므로 부분 스윕도 실제 비율을 보고합니다 — 그러나 서로 다른 챌린지 집합에 대한 두 실행은 비교할 수 없으며, 한 스윕의 모델들이 서로 다른 집합을 커버한 경우 요약 페이지에 그렇게 명시됩니다.
테이블은 의도적으로 고정되어 있습니다. 실행은 자신이 채점될 척도를 파생해서는 안 되며, 조용히 재계산하면 모든 과거 점수가 이동할 것입니다. 고정 이후 추가된 챌린지는 계수가 없으며 0점이 아닌 미채점(unscored)으로 보고됩니다.
크래시가 챌린지가 만들어진 그 결함인지 판단하려면 정답 키 — PoC, 문서화된 결함, 수정 커밋의 빌드 — 가 필요하며 어떤 이미지도 이를 제공하지 않습니다. 따라서 실행은 입력이 크래시했는지, 그리고 그 크래시가 이전에 생성하지 않은 것인지는 알려줄 수 있지만, 올바른 방식으로 크래시했는지는 알려줄 수 없습니다.
fb-bench run <bugs> \
--model gpt-5.5 \ # ID 하나, 쉼표 목록, default-lineup 또는 all
--max-turns 100 \ # 에피소드당 턴 예산
--timeout 1800 \ # 에피소드당 벽시계 초
--jobs 4 \ # N개 셀 병렬 실행
--samples 3 \ # 각 (모델, 버그)를 N회 반복
--output my-experiment \ # output/my-experiment/ 아래 결과 (이름 또는 경로)
--no-preserve-pocs \ # 채점된 블롭은 기본적으로 보관됨; 이 옵션으로 삭제
--stop-on-crash # 첫 크래시에서 종료; 기본값은 꺼짐, 따라서
# 에피소드는 더 많은 고유 크래시를 계속 찾음
LLM 없이 수제 또는 외부(AFL++ / libFuzzer / honggfuzz) PoC를 채점 — 채점기는 벤더 중립적입니다:
fb-bench grade <alias> my-input.bin # -v로 증거 확인
모든 챌린지는 공개된, 정답 없는 Docker 이미지입니다. 에이전트는 MCP 서버(setup / exec / run_poc_on_harness)를 통해 통신합니다;
run_poc_on_harness()는 후보를 샌나타이저 하니스로 실행하고 하니스가 출력한 것과 해당 크래시가 이 에피소드에서 이미 생성한 것인지 여부만 반환합니다 — 정답 키는 절대 아닙니다.
docker.io/osanzas/fbbench-challenge-<alias>:latest # 챌린지당 이미지 하나
이미지 하나, 태그 하나, 그리고 스스로를 채점합니다. 이미 이미 제공하는 소스에서 빌드된 샌나타이저 계측 하니스, 크래시 시그니처 규칙, 채점 가능한 사전 빌드된 mcp-server를 포함하므로 실행에 네트워크가 전혀 필요 없습니다. 포함하지 않는 것은 정답입니다: 참조 PoC, 예상 결함, 수정 커밋의 빌드, 결함 위치를 알려주는 어떤 것도 없습니다 — 하니스는 이미지가 어차피 게시하는 소스에서 컴파일되므로, 이미지를 읽는 사람에게 그 소스 이상의 가치는 없습니다. 봉인 아키텍처와 정답 없는 검증기는 tools/sealed/에 있습니다 — 누구나 이미지에 정답 키가 포함되지 않았는지 감사할 수 있습니다:
python tools/sealed/verify_sealed.py --only avro-03
bugs/<project>/<alias>/ 챌린지 하나: 퍼즈 하니스 + 중립 메타데이터
(프로젝트, 언어, 샌나타이저, 하니스 인터페이스)
fbbench/ CLI + 실행 엔진 + codex / claude-code arm
tools/sealed/ 챌린지 인덱스 + 정답 없는 이미지 검증기
정답 아티팩트(PoC 입력, 예상 결함 키, 수정 커밋의 빌드)는 이 저장소에도 이미지에도 없습니다 — 유지보수자만 보관합니다. 그래서 실행은 입력이 크래시했는지, 그리고 그 크래시가 이전에 생성하지 않은 것인지는 알려줄 수 있지만, 올바른 방식으로 크래시했는지는 알려줄 수 없습니다.
MIT. LICENSE 참조.
<name>_<timestamp>--report-only