Управляемый LLM конвейер фаззинга на базе GitHub Security Lab Taskflow Agent
Управляемый LLM конвейер фаззинга в стиле OSS-Fuzz для нативных проектов на C/C++. AFL++ для выполнения, clang+lcov для покрытия, LLM-агент для написания харнессов, решений на основе обратной связи по покрытию, триажа и отчётности.
Этот репозиторий содержит таскфлоу фаззинга для
GitHub Security Lab Taskflow Agent.
Он зависит от сопутствующего репозитория
seclab-taskflows
для нескольких общих строительных блоков
(таскфлоу fetch_source_code, тулбоксы local_file_viewer / gh_file_viewer
и стандартный model_config) — они устанавливаются
автоматически как зависимость Python.
Вклад приветствуется! См. CONTRIBUTING.md для рекомендаций.
aptgh)pip install git+https://github.com/GitHubSecurityLab/seclab-taskflows-fuzzing
Это подтягивает `seclab-taskflow-agent` и `seclab-taskflows` (родительский)
транзитивно, поэтому каждая точечная ссылка вида
`seclab_taskflows.taskflows.audit.*`,
`seclab_taskflows.toolboxes.local_file_viewer`,
`seclab_taskflows.toolboxes.gh_file_viewer` и
`seclab_taskflows.configs.model_config` разрешается из родительского
дистрибутива во время выполнения.
---
## Содержание
1. [Что это такое](#what-this-is)
2. [Быстрый старт](#quick-start)
3. [Архитектура](#architecture)
4. [Пайплайн, этап за этапом](#the-pipeline-stage-by-stage)
5. [Цикл обратной связи по покрытию](#the-coverage-feedback-loop)
6. [Структурно-осведомлённый фаззинг](#structure-aware-fuzzing)
7. [Постоянный корпус между итерациями и кампаниями](#persistent-corpus-across-iterations-and-campaigns)
8. [Триаж и отчёты об уязвимостях](#triage-and-vulnerability-reports)
9. [Живая панель мониторинга](#live-dashboard)
10. [Выходные файлы](#output-files)
11. [Схема базы данных](#database-schema)
12. [Инструменты MCP (словарь агента)](#mcp-tools-the-agents-vocabulary)
13. [Настраиваемые параметры (переменные окружения)](#tunable-knobs-environment-variables)
14. [Расширение пайплайна](#extending-the-pipeline)
15. [Бенчмарк-проекты и результаты](#benchmark-projects-and-results)
16. [Ограничения и подводные камни](#limitations-and-gotchas)
17. [Предупреждение о безопасности](#security-warning)
18. [Разработка: тестирование, линтинг, участие](#development-testing-linting-contributing)
19. [Глоссарий](#glossary)
---
## Что это такое
Этот taskflow представляет собой полностью автономный пайплайн фаззинга. Получив GitHub-репозиторий
нативного проекта на C/C++, он:
1. установит AFL++ + clang/llvm/lcov + ctags/cscope/graphviz, если их нет,
2. получит исходный код,
3. определит кандидатов в цели фаззинга (парсеры, декодеры, валидаторы, …),
4. проанализирует систему сборки,
5. напишет одного или нескольких кандидатов в harness'ы на каждую цель, соберёт каждого как
AFL-инструментированный `.afl` бинарник и как coverage-инструментированный `.cov` бинарник,
6. (опционально) отберёт кандидатов по 60-секундному покрытию и оставит лучших,
7. запустит цикл фаззинг/покрытие/улучшение с удвоением временных бюджетов,
8. проведёт триаж каждого краша, подтвердит, что ранее известные краши всё ещё воспроизводятся, и
напишет по каждому крашу markdown-отчёт об уязвимости с вердиктами, эксплуатируемостью,
предлагаемыми патчами и набросками регрессионных тестов,
9. построит граф вызовов в стиле Fuzz-Introspector + отчёт о нетронутых API для
следующей кампании,
10. опубликует всё на живой HTML-панели мониторинга.
Пайплайн **в духе OSS-Fuzz**: он использует многие из тех же
техник (мутаторы и словари для каждого формата, структурно-осведомлённое сращивание токенов, улучшения harness'ов на основе покрытия, машиночитаемые отчёты,
дедуплицированные краши с хешированием стеков), но он гораздо меньше и самодостаточен.
---
## Быстрый старт```bash
# Inside the codespace (or a host with python + git available):
./scripts/fuzzing/run_fuzzing.sh tukaani-project/xz
Это весь интерфейс. Скрипт автономен; при первом запуске он установит AFL++,
а затем будет управлять остальным рабочим процессом. Выходные файлы записываются
в ~/.local/share/seclab-taskflow-agent/seclab-taskflows/.
Панель управления автоматически запускается в фоновом режиме; в Codespace порт 8765
автоматически перенаправляется — откройте его в любом браузере, чтобы наблюдать за ходом выполнения в реальном времени.
Для быстрой проверки используйте небольшую цель:```bash ./scripts/fuzzing/run_fuzzing.sh DaveGamble/cJSON
## Архитектура
Три слоя, сверху вниз:```
┌────────────────────────────────────────────────────────────────────┐
│ scripts/fuzzing/run_fuzzing.sh │
│ shell driver; chains the taskflow stages with `set +e` │
└────────────────────┬───────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────────┐
│ src/seclab_taskflows/taskflows/fuzzing/*.yaml │
│ LLM agent prompts; one YAML per pipeline stage │
└────────────────────┬───────────────────────────────────────────────┘
│ (calls MCP tools)
▼
┌────────────────────────────────────────────────────────────────────┐
│ src/seclab_taskflows/mcp_servers/ │
│ ├ fuzz_context.py persistence (SQLite via SQLAlchemy) │
│ └ fuzz_runner.py subprocess wrappers (AFL, clang, lcov, ...) │
│ │
│ scripts/fuzzing/dashboard.py │
│ read-only HTML view of fuzz_context.db │
└────────────────────────────────────────────────────────────────────┘
Ключевые правила проектирования:
fuzz_context.db.run_afl_for, compile_harness, store_crash и т. д.afl-clang-lto -fsanitize=address,undefined (бинарник .afl) и один раз
с clang -fprofile-instr-generate -fcoverage-mapping (бинарник .cov).
Бинарник .afl фаззит; бинарник .cov воспроизводит очередь AFL,
чтобы получить реальное покрытие строк/функций/ветвей исходного кода.| # | Этап | Taskflow YAML |
|---|---|---|
| 1 | Установка AFL++ + инструментария | scripts/fuzzing/install_afl.sh |
| 2 | Получение исходного кода | seclab_taskflows.taskflows.audit.fetch_source_code |
| 3 | Определение целей фаззинга | seclab_taskflows_fuzzing.taskflows.fuzzing.identify_fuzz_targets |
| 4 | Анализ системы сборки | seclab_taskflows_fuzzing.taskflows.fuzzing.analyze_build_system |
| 5a | Написание начальных harness-ов (×N кандидатов, если запрошено) | seclab_taskflows_fuzzing.taskflows.fuzzing.write_initial_harnesses |
| 5b | Сборка harness-ов (AFL + покрытие) | seclab_taskflows_fuzzing.taskflows.fuzzing.build_harnesses |
| 5c | Отбор кандидатов (когда HARNESS_CANDIDATES > 1) | seclab_taskflows_fuzzing.taskflows.fuzzing.qualify_harnesses |
| 6 | Цикл фаззинга/покрытия/улучшения (×N итераций) | seclab_taskflows_fuzzing.taskflows.fuzzing.fuzz_iteration |
| 7 | Триаж крашей | seclab_taskflows_fuzzing.taskflows.fuzzing.triage_crashes |
| 8 | Подтверждение, что ранее известные краши всё ещё воспроизводятся | seclab_taskflows_fuzzing.taskflows.fuzzing.confirm_fixed_crashes |
| 9 | Построение графа вызовов + отчёт о незатронутых API | seclab_taskflows_fuzzing.taskflows.fuzzing.analyze_call_graph |
| 10 | Написание отчётов об уязвимостях по каждому крашу | seclab_taskflows_fuzzing.taskflows.fuzzing.write_vuln_reports |
| 11 | Написание отчёта о кампании | seclab_taskflows_fuzzing.taskflows.fuzzing.write_report |
Каждый этап — это самодостаточный taskflow YAML, который агент выполняет
от начала до конца. Этапы взаимодействуют исключительно через базу данных SQLite
в fuzz_context.db — никакой передачи данных в памяти нет.
Это сердце пайплайна. Бюджеты времени удваиваются на каждой итерации:``` 30s → 60s → 120s → 240s → 480s → 960s (≈ 32 min/target)
На каждой итерации, для каждого харнесса агент:
1. Запрашивает `get_persistent_corpus_dir(harness_id)` для получения стабильного
каталога корпуса этого харнесса.
2. Вызывает `run_afl_for(afl_binary_path, seed_dir=<persistent corpus>,
output_dir=<run dir>, seconds=<budget>, dictionary=<auto.dict>)`.
3. Вызывает `run_coverage(cov_binary_path, inputs_dir=<run>/default/queue,
output_dir=<run>/coverage)` для создания LCOV-трейсфайла и HTML-отчёта.
4. Вызывает `store_coverage_from_lcov(run_id, lcov_path, html_path)` для сохранения
строки `coverage_report` + строк `coverage_gap` для каждого непокрытого элемента.
5. Вызывает `fold_queue_into_persistent_corpus(...)` для слияния очереди итерации
AFL в постоянный корпус и запускает `cmin` для ограничения размера.
6. Читает `get_coverage_summary` + `get_coverage_gaps`, затем либо:
- добавляет новое зерно (с тегом `coverage_feedback`) для достижения непокрытой
ветви,
- редактирует исходный код харнесса для вызова дополнительного API,
- вызывает `enrich_dictionary_from_uncovered(...)` для автоматического добавления
словарных записей для магических констант, необходимых AFL для прохождения
проверки, либо
- пропускает пробел (холодный путь ошибки / код вендора).
7. Вызывает `store_iteration_note(repo, iteration_number, harness_id, note=<одна
строка с итогом>)`, чтобы временная шкала итераций на дашборде отслеживала, что
изменилось.
**Обнаружение плато.** Цикл завершается досрочно, как только две последовательные
итерации обе дали прирост < `FUZZ_PLATEAU_THRESHOLD_PCT` (по умолчанию `1.0`)
абсолютных процентных пунктов покрытия строк.
---
## Структурно-осведомлённый фаззинг
Три взаимодополняющих механизма позволяют получать более сильные входные данные,
чем чистая побайтовая мутация.
### 1. Словари для каждого формата + пользовательские мутаторы
Для целей, чей `input_kind` соответствует известному формату, taskflow поставляет
готовые словари и исходные файлы `LLVMFuzzerCustomMutator` на C:
| Формат | Словарь | Мутатор | Примечания |
|--------|------------|---------|-------|
| `json` | `json.dict` | `json_mutator.c` | Сплайсинг токенов, дублирование/удаление сбалансированных скобок, смена типа |
| `xml` | `xml.dict` | `xml_mutator.c` | Теги, сущности, DTD, токены billion-laughs |
| `regex` | `regex.dict` | `regex_mutator.c` | Якоря, классы, квантификаторы, реальные ReDoS-паттерны |
| `binary_tlv` | _(нет)_ | `binary_tlv_mutator.c` | Записи с префиксом длины: переполнение длины / дублирование / удаление |
| `png` | `png.dict` | _(переиспользует binary_tlv)_ | Словарь PNG + мутатор binary_tlv |
Они подхватываются автоматически функциями `write_initial_harnesses` (словарь
копируется рядом с зёрнами) и `build_harnesses` (мутатор линкуется в бинарник AFL).
Каждый мутатор делегирует 50% мутаций стандартному побайтовому мутатору AFL, чтобы
не потерять рандомизацию движка.
Чтобы добавить новый формат: поместите `<name>.dict` и/или `<name>_mutator.c` в
`src/seclab_taskflows/dictionaries/`, затем зарегистрируйте его в карте
`_FORMAT_ASSETS` в конце `fuzz_runner.py`.
### 2. Осведомлённый об исходниках (проектно-специфичный) умный мутатор
Для незнакомых форматов или когда нужны более сильные проектно-специфичные токены,
`generate_smart_mutator` сканирует собственные файлы `.c`/`.h` целевого репозитория
и генерирует C-файл `LLVMFuzzerCustomMutator`, чьи словари сплайсинга извлекаются из:
- строковых литералов с ≥3 буквенными символами (после фильтрации шума
компилятора/лицензий, путей, заголовков, asm-ограничений, спецификаторов формата),
- 32-битных числовых констант из `#define`, `case` и `enum` (после фильтрации
общего шума малых целых, такого как 0, 1, 256, 0xff…).
Доступны три фокуса:
| Фокус | Что сплайсится | Когда использовать |
|-------|-----------------|-------------|
| `strings` | Только строковые литералы проекта | Текстовые форматы (JSON, XML, YAML, CSV) |
| `constants` | Только 32-битные числовые магические значения | Бинарные протоколы, заголовки с магическими числами |
| `combined` | Оба | По умолчанию; обычно лучший |
Сочетайте `generate_smart_mutators(...)` (во множественном числе) с
`HARNESS_CANDIDATES >= 3`, чтобы каждый фокус стал кандидатом-харнессом в
квалификационном раунде.
### 3. Проектно-осведомлённый словарь AFL + обогащение на основе покрытия
Два взаимодополняющих инструмента создают и развивают словарь AFL `-x` по мере
продвижения кампании:
- **`generate_project_dictionary(source_root, output_path)`** — запускается один раз
перед итерацией 1, статически извлекает тот же набор исходных токенов, что
используется умным мутатором, и записывает его как словарь AFL. Числовые константы
выводятся в ОБОИХ порядках байтов, чтобы фаззер мог удовлетворить
`memcmp(x, &magic, 4)` независимо от порядка байтов хоста.
- **`enrich_dictionary_from_uncovered(source_root, dictionary_path,
uncovered_locations)`** — запускается после шага покрытия каждой итерации,
сканирует окружающий исходный код на предмет условных проверок
(`strncmp/memcmp/strstr`, `case 0xN:`, `== 0xN`, `== 'X'`) рядом с
непокрытыми строками и ДОБАВЛЯЕТ любые новые токены в словарь. Идемпотентен:
никогда не добавляет повторно уже присутствующую запись.
### 4. Операция сплайсинга корпуса
Когда в `generate_smart_mutator` передаётся `corpus_dir`, сгенерированный C также
получает оператор сплайсинга корпуса: при первом вызове он загружает до 64 файлов
из этого каталога (с ограничением 4 КиБ каждый), и с этого момента может
сплайсить случайные подобласти этих файлов в мутируемый вход. Это даёт мутатору
оператор в стиле рекомбинации, который стандартный havoc AFL выполняет плохо.
Сочетайте с `get_persistent_corpus_dir(...)`, чтобы библиотека сплайсинга
представляла собой «ремикс того, что AFL уже обнаружил».
---
## Постоянный корпус между итерациями и кампаниями
У каждого харнесса есть стабильный каталог корпуса по пути:```
<workspace>/corpus/harness_<id>/
Это то, что fuzz_iteration использует как seed_dir для run_afl_for (а
не <harness>/seeds). В конце каждой итерации
fold_queue_into_persistent_corpus(...) объединяет очередь итерации AFL в
этот каталог и запускает afl-cmin, чтобы держать его в разумных пределах.
Результат: вчерашняя очередь переносится в сегодняшний запуск И между повторными запусками одного и того же проекта. Остановка и перезапуск кампании не теряет прогресс.
После завершения цикла fuzz/coverage/improve автоматически запускаются три этапа:
triage_crashesДля каждого файла краша в <run>/default/crashes/:
afl-tmin для минимизации входных данных,replay_under_asan для захвата трассировки стека и stack_top_hash
(top-N нормализованных кадров; шаблоны, встроенные пространства имён
libcxx, анонимные пространства имён и числовые суффиксы LTO удаляются,
чтобы семантически идентичные краши хешировались одинаково),crash с классификацией класса
бага + примечанием об уверенности (high / medium / low).confirm_fixed_crashesПовторно воспроизводит каждый ранее классифицированный краш (чей вердикт
ещё не fixed/duplicate/non_reproducible) через текущий бинарник
AFL+ASan. Если он больше не крашится, помечает verdict="fixed". Полезно
при повторном запуске кампании против проекта, в который с момента прошлой
кампании были внесены исправления в upstream.
write_vuln_reportsДля каждого уникального краша агент читает исходный код харнесса + исходный код функции, вызывающей краш, проходит по цепочке вызовов от публичного API, затем присваивает один из десяти вердиктов в стиле OSS-Fuzz и пишет markdown-отчёт об уязвимости:
| Вердикт | Значение |
|---|---|
vulnerability | Реальная, эксплуатируемая через публичный API |
library_hardening | Реальный баг, но нет реалистичного пути через публичный API; библиотека всё равно должна защищаться |
harness_bug | Баг в нашем харнессе, а не в библиотеке |
non_reproducible | Воспроизведение не воспроизводит краш на минимизированных входных данных |
oom | Out-of-memory; уязвимость только если контролируемый атакующим размер неограничен |
timeout | DoS через алгоритмический взрыв |
assertion_failure | Сработал assert(); значимость для безопасности варьируется |
fixed | Устанавливается confirm_fixed_crashes: входные данные больше не воспроизводят краш |
duplicate | Та же первопричина, что и у другого краша с другим хешем стека |
needs_investigation | Не удалось определить; помечено для ручной проверки |
Каждый отчёт об уязвимости включает:
Дашборд запускается автоматически в фоне скриптом
run_fuzzing.sh. Отключается через FUZZ_NO_DASHBOARD=1; порт
переопределяется через FUZZ_DASHBOARD_PORT (по умолчанию 8765).
В Codespace порт 8765 автоматически перенаправляется — откройте
перенаправленный URL в любом браузере. Страница автообновляется каждые 5 с
и показывает:
fuzz_runvulnerability первым),
со ссылками на каждый отчёт об уязвимости и минимизированные входные данныеДашборд также предоставляет крошечный JSON API только для чтения для скриптов:```bash
curl 'http://127.0.0.1:8765/api/json?repo=kkos/oniguruma' | jq .
---
## Выходные файлы
Все находятся в `~/.local/share/seclab-taskflow-agent/seclab-taskflows/`.
| Путь | Содержимое |
|------|----------|
| `fuzz_context/fuzz_context.db` | SQLite — цели, харнессы, запуски, покрытие, краши, вердикты, графы вызовов, предложения по харнессам, заметки об итерациях |
| `fuzz_runner/builds/` | Собранные бинарники `.afl` и `.cov` |
| `fuzz_runner/runs/` | Выходные каталоги AFL + файлы LCOV + HTML-отчёты о покрытии |
| `fuzz_runner/corpus/harness_<id>/` | Постоянный корпус для каждого харнесса (переносится между итерациями и кампаниями) |
| `fuzz_runner/repo/<owner>__<repo>/REPORT.md` | Markdown-сводка кампании, краши сгруппированы по вердикту |
| `fuzz_runner/repo/<owner>__<repo>/vuln_<crash_id>.md` | Markdown-отчёт об уязвимости для каждого краша |
| `fuzz_runner/repo/<owner>__<repo>/call_graph.{dot,svg,md}` | Статический граф вызовов + наложение достигнутых/недостигнутых |
---
## Схема базы данных
Таблицы в `fuzz_context.db` (SQLite через SQLAlchemy):
| Таблица | Интересующие столбцы |
|-------|--------------------|
| `fuzz_target` | `repo, file, function, signature, input_kind` |
| `harness` | `target_id, repo, harness_path, afl_binary_path, cov_binary_path, build_status, version, sanitizers` |
| `seed_corpus` | `target_id, source, path, bytes_count, added_in_iteration` |
| `fuzz_run` | `harness_id, iteration_number, exec_per_sec, paths_total, crashes_count, status, output_dir, started_at, ended_at` |
| `coverage_report` | `run_id, lines_total, lines_hit, line_pct, fns_*, branches_*, lcov_path, html_path` |
| `coverage_gap` | `report_id, file, function, line, kind, reason_hint` |
| `crash` | `run_id, input_blob_path, minimized_path, stack_top_hash, sanitizer_output, verdict, bug_class, cwe, severity, vuln_report_path, reproducer_path, classification, notes` |
| `call_graph` | `repo, target_id, dot_path, svg_path, functions_total, functions_in_graph, functions_reached, functions_unreached, untouched_surface_json` |
| `harness_suggestion` | `repo, function_name, file, rationale, input_kind, priority` |
| `iteration_note` | `repo, harness_id, iteration_number, note, created_at` |
Миграции схемы находятся в `_migrate()` в `fuzz_context.py`. Новые ТАБЛИЦЫ
автоматически создаются через `Base.metadata.create_all()`; только для новых
СТОЛБЦОВ требуется `ALTER TABLE` на основе PRAGMA.
---
## Инструменты MCP (словарь агента)
Агент никогда не вызывает AFL или clang напрямую — он собирает конвейер,
вызывая инструменты MCP. Полный набор, сгруппированный по назначению:
### Персистентность (`fuzz_context.py`)
- `store_fuzz_target`, `get_fuzz_targets`
- `store_harness`, `update_harness_build`, `get_harnesses`
- `store_seed`, `start_fuzz_run`, `finish_fuzz_run`, `get_fuzz_runs`
- `store_coverage_from_lcov`, `get_coverage_summary`, `get_coverage_gaps`,
`coverage_plateau_reached`
- `store_crash`, `update_crash_verdict`, `get_crashes`,
`get_crashes_grouped`, `suggest_severity`
- `store_call_graph`, `get_call_graphs`, `get_repo_reached_functions`
- `store_harness_suggestion`, `get_harness_suggestions`
- `store_iteration_note`, `get_iteration_notes`
### Сборка / фаззинг / покрытие (`fuzz_runner.py`)
- `check_tooling`, `workspace_paths`
- `compile_harness` — собирает бинарники `.afl` и `.cov`
- `run_afl_for`, `cmin`, `tmin`, `replay_under_asan`, `reproduce_crash`
- `run_coverage` — воспроизводит очередь AFL против бинарника `.cov`, экспортирует LCOV
- `extract_dictionary` — извлекает печатаемые строки из бинарника
- `package_reproducer` — упаковывает `.tgz` для одного краша
### Постоянный корпус (v8)
- `get_persistent_corpus_dir`, `fold_queue_into_persistent_corpus`
### Ресурсы форматов (C5)
- `list_format_assets`, `get_format_dictionary`, `write_format_mutator`
### Умный мутатор + словарь с учётом проекта
- `generate_smart_mutator`, `generate_smart_mutators`
- `generate_project_dictionary`, `enrich_dictionary_from_uncovered`
Функции инструментов декорированы `@mcp.tool()` (FastMCP). В тестах
вызывайте их через атрибут `.fn`, например
`fr.run_afl_for.fn(afl_binary_path=..., ...)`.
---
## Настраиваемые параметры (переменные окружения)
| Переменная | По умолчанию | Назначение |
|----------|---------|---------|
| `HARNESS_CANDIDATES` | `1` | Количество кандидатных харнессов, создаваемых для каждой цели. Установите 2 или 3 для конкуренции в стиле OSS-Fuzz-Gen. Этап квалификации запускает каждый в течение `QUALIFIER_SECONDS` и оставляет лучший по проценту строк. |
| `QUALIFIER_SECONDS` | `60` | Бюджет реального времени на каждого кандидата на этапе квалификации. |
| `FUZZ_PLATEAU_THRESHOLD_PCT` | `1.0` | Прирост покрытия строк (в абсолютных п.п.), ниже которого две последовательные итерации считаются плато и цикл останавливается досрочно. |
| `FUZZ_DASHBOARD_PORT` | `8765` | Порт для живого дашборда. |
| `FUZZ_NO_DASHBOARD` | (не задано) | Установите `1`, чтобы пропустить запуск дашборда. |
| `FUZZ_RUNNER_TIMEOUT` | `1200` | Таймаут подпроцесса на инструмент в `fuzz_runner` (секунды). |
| `LOCAL_SHELL_TIMEOUT` | `180` | Таймаут на команду в `local_shell` (секунды). |
Плюс стандартные переменные агента (`COPILOT_TOKEN`, `LOG_DIR`,
`FUZZ_CONTEXT_DIR`, …). Полный список см. в README в корне проекта.
---
## Расширение конвейера
### Добавление нового формата (мутатор + словарь)
1. Поместите `dictionaries/<name>.dict` (формат AFL `-x`) и/или
`dictionaries/<name>_mutator.c` (пользовательский мутатор libFuzzer).
2. Зарегистрируйте в `_FORMAT_ASSETS` в конце `fuzz_runner.py`: ```python
"<name>": {
"dictionary": "<name>.dict",
"mutator": "<name>_mutator.c",
"description": "Short one-liner about the format",
},
list_format_assets().@mcp.tool() в fuzz_context.py (для
персистентности) или fuzz_runner.py (для работы с подпроцессами).Annotated[type, Field(description=...)] для каждого аргумента —
именно описание видит LLM.tests/test_fuzz_context.py /
tests/test_fuzz_runner.py. Вызывайте инструмент через его атрибут .fn
(соглашение FastMCP).user_prompt соответствующего taskflow YAML.src/seclab_taskflows/taskflows/fuzzing/. Используйте
один из существующих файлов (например, triage_crashes.yaml) как шаблон.scripts/fuzzing/run_fuzzing.sh между нужными двумя
существующими этапами.scripts/fuzzing/dashboard.py.При добавлении новой таблицы SQL:
fuzz_context_models.py.Base.metadata.create_all() вызывается при
инициализации движка и автоматически создаёт новые таблицы.При добавлении нового СТОЛБЦА в существующую таблицу:
PRAGMA table_info + ALTER TABLE ADD COLUMN в
_migrate() в fuzz_context.py, чтобы старые БД обновлялись прозрачно._migrate_if_writable() в scripts/fuzzing/dashboard.py.benchmark/projects.yaml содержит список эталонных проектов. Они выбраны так,
чтобы полный конвейер v4+ мог выполняться от начала до конца на dev-образе
codespace без вмешательства человека.
| # | Репозиторий | Чем интересен | Примечания |
|---|---|---|---|
| 1 | tukaani-project/xz | Реальная библиотека с интенсивным парсингом (liblzma); богатая цепочка фильтров + поверхность парсинга integer/VLI | Базовый |
| 2 | DaveGamble/cJSON | Небольшой однофайловый парсер JSON на C; тривиальный CMake | Быстрая проверка конвейера |
| 3 | akheron/jansson | Компактная библиотека JSON на C с документированной точкой входа json_loadb() для байтового буфера | CMake; очень высокая скорость exec/sec |
| 4 | libexpat/libexpat | Зрелый потоковый парсер XML; множество исторических CVE | CMake или autotools |
| 5 | kkos/oniguruma | Движок регулярных выражений; принимает шаблон атакующего + subject | Autotools; компиляция шаблона — горячий путь |
Эталонные показатели полного запуска конвейера v4 на dev-образе codespace (≈32 мин на цель):
| Репозиторий | Цели | Харнессы | Запуски AFL | Крэши | Вердикты |
|---|---|---|---|---|---|
tukaani-project/xz | 8 | 8 | 48 | 0 | — |
DaveGamble/cJSON | 6 | 6 | 36 | 0 | — |
akheron/jansson | 7 | 7 | 35 | 10 | harness_bug, library_hardening, duplicate, needs_investigation |
libexpat/libexpat | 3 | 3 | 18 | 0 | — |
kkos/oniguruma | 10 | 10 | 60 | 13 | vulnerability (×2 OOB read в regerror.c), library_hardening, harness_bug, non_reproducible |
Нулевые результаты по крэшам для xz / cJSON / libexpat ожидаемы: эти проекты
интенсивно фаззятся в upstream. Две находки, классифицированные как
vulnerability, в oniguruma — это реальные чтения за границами буфера в пути
кода форматирования предупреждений onig_snprintf_with_pattern (чтение одного
байта за pat_end, когда шаблон заканчивается обратной косой чертой); отчёты
markdown по каждому крэшу включают предлагаемые патчи.
Чтобы добавить новый эталонный проект, добавьте запись в benchmark/projects.yaml
и (необязательно) задокументируйте причину в benchmark/README.md. Всё, что
существующий этап analyze_build_system может собрать с флагами clang + AFL++,
является разумным кандидатом. Чистые парсеры, декодеры и сериализаторы на C
обычно работают лучше всего.
BUILD_FAILED: и пропускает их.kernel.core_pattern=core и
настройки CPU governor. В Codespace они недоступны, поэтому taskflow по
умолчанию экспортирует AFL_SKIP_CPUFREQ=1 и
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1. AFL печатает предупреждения, но
всё равно находит крэши через обработку abort в стиле libFuzzer.<dirent.h>. Подходит для Linux/macOS; не скомпилируется
на Windows.compile_harness,
используют libAFLDriver в режиме argv. Поэтому replay_under_asan и tmin
по умолчанию используют stdin_input=False, так как libAFLDriver зацикливается
навсегда при управлении через stdin.generate_smart_mutator + generate_smart_mutators используют Python
.format() — каждый литеральный { / } в шаблоне C должен быть удвоен
({{ / }}). Если вы отредактируете шаблон и начнёте видеть KeyError,
вот почему.Этот taskflow запускает afl-fuzz, clang, llvm-cov, а также произвольные
команды сборки, выбранные LLM, напрямую на хосте (без контейнера).
Агент, подверженный prompt injection, в принципе может делать всё, что может
ваш пользователь. Запускайте только:
git, apt и системе
сборки.Инструментарий local_shell НЕ защищён запросом подтверждения — taskflow
автономен и работает без участия человека, поэтому интерактивное подтверждение
просто зависло бы навсегда. Каждая команда оболочки логируется в
$LOG_DIR/mcp_local_shell.log для последующего анализа.
hatch test
hatch fmt --linter --check
hatch fmt --linter
hatch fmt --linter --check -- src/seclab_taskflows/mcp_servers/fuzz_runner.py
Соглашения кодовой базы (см. также `benchmark/improvements.md` для
версии этих соглашений в контексте истории кампаний):
- Используйте `os.environ.get(NAME) or "default"` вместо
`os.environ.get(NAME, "default")`. Иначе пустые строки из подстановки
шаблона YAML будут возвращены.
- Используйте `X | None` (PEP 604) в новых аннотациях, а не `Optional[X]`.
- Тесты вызывают MCP-инструменты через `.fn(...)`, а не через
декорированное имя напрямую.
- Избегайте литералов `/tmp/...` в тестах — используйте pytest-фикстуру
`tmp_path` (правило линтера `S108`).
- Все inline-импорты внутри методов тестов нуждаются в `# noqa: PLC0415`,
если вы не можете переместить их в начало файла (например, при
условном импорте после `pytest.skip`).
- Одно утверждение на строку для составных проверок истинности (правило
линтера `PT018`).
Трекер улучшений (`benchmark/improvements.md`) — это постоянный
журнал того, что было добавлено в пайплайн across версий. Когда вы
добавляете существенную функцию, добавьте туда раздел с описанием того,
что изменилось, где это находится и какие тесты это защищают.
---
## Глоссарий
- **AFL++** — Грейбокс-фаззер с покрытием; здесь — движок исполнения.
- **libAFLDriver** — Статическая библиотека, позволяющая харнессам AFL++
использовать соглашение о точке входа libFuzzer
(`LLVMFuzzerTestOneInput`).
- **LCOV** — Отраслевой стандартный формат файла трассировки покрытия.
Мы экспортируем в него через `llvm-cov export -format=lcov` и
разбираем его сами.
- **`stack_top_hash`** — 16-символьный хеш верхних N нормализованных
кадров трассировки стека ASan/UBSan. Используется для дедупликации
крашей.
- **Постоянный корпус** — Директория для каждого харнесса по пути
`<workspace>/corpus/harness_<id>/`, которая переносит интересные
входные данные AFL между итерациями и повторными запусками одной и
той же кампании.
- **Умный мутатор** — `LLVMFuzzerCustomMutator`, чьи токены для
сплайсинга извлекаются из исходного кода самой цели
(`generate_smart_mutator`).
- **Пользовательский мутатор (libFuzzer)** — Предоставляемая
пользователем C-функция, вызываемая движком с полной свободой в
отношении того, как мутировать буфер; AFL++ поддерживает тот же ABI.
- **MCP-инструмент** — Функция, декорированная FastMCP, которую может
вызывать LLM-агент.
- **OSS-Fuzz / Fuzz-Introspector** — Инфраструктура Google для
открытого фаззинга и сопутствующий инструмент анализа графа вызовов
и покрытия. Некоторые функции этого taskflow (мутаторы для каждого
формата, дедупликация по стеку, отчёт по графу вызовов и
нетронутым API, многокандидатные харнессы) вдохновлены ими.
---
## Лицензия
Этот проект лицензирован на условиях открытой лицензии MIT. Полные
условия см. в файле [LICENSE](https://github.com/githubsecuritylab/seclab-taskflows-fuzzing/blob/main/LICENSE.txt).
## Сопровождающие
См. [CODEOWNERS](https://github.com/githubsecuritylab/seclab-taskflows-fuzzing/blob/main/CODEOWNERS) или свяжитесь с командой GitHub
Security Lab.
## Поддержка
См. [SUPPORT.md](https://github.com/githubsecuritylab/seclab-taskflows-fuzzing/blob/main/SUPPORT.md) для получения подробной информации о
том, как получить помощь по этому проекту.
## Благодарности
Этот проект построен на основе концепций и техник
[AFL++](https://github.com/AFLplusplus/AFLplusplus),
[OSS-Fuzz](https://github.com/google/oss-fuzz) и
[Fuzz-Introspector](https://github.com/ossf/fuzz-introspector).