Skip to content

dmbs335/fuzzer-orchestrator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Fuzzer Orchestrator

A web-based orchestration platform for running grammar-based fuzzers, collecting results, analyzing findings, and enabling team collaboration.

Automatically parses results from web-fuzzer, stores them in a database, and provides a web UI for finding classification, triage, commenting, and collaborative workflows.

Key Features

  • Dashboard — View active sessions, recent findings, and overall statistics at a glance
  • Project Management — Group fuzzing sessions by project with aggregated statistics
  • Session Control — Start/stop web-fuzzer directly from the web UI with real-time log and coverage monitoring (WebSocket)
  • Result Import — Import existing web-fuzzer results by specifying the output directory path
  • Finding Analysis — Filter by severity/oracle/triage, bulk triage, deduplication (fingerprint), input preview, per-finding tag management
  • Corpus Management — Browse corpus entries, preview inputs, adjust seed priority (forwarded to running fuzzer in real-time)
  • Collaboration — Per-finding comments, tags, triage workflow (new → confirmed / false_positive / fixed / wont_fix)
  • Target Presets — 13 pre-configured fuzzing targets (URL parsers, sanitizers, etc.) for quick session setup
  • Extensibility — Designed to accommodate results from other fuzzers (AFL, LibFuzzer, etc.) via the fuzzer_type field

Tech Stack

Layer Technology
Frontend React 19, TypeScript, Vite 7, TailwindCSS v4, Recharts, Lucide React
Backend Python FastAPI, SQLAlchemy 2.0, SQLite (WAL mode)
Real-time WebSocket (FastAPI native), Redis Pub/Sub

Quick Start

Prerequisites

  • Python 3.11+
  • Node.js 18+
  • web-fuzzer installed at C:\Users\dmbs3\web-fuzzer (configurable via environment variables)
  • (Optional) Redis server — required for real-time data pipeline. Falls back to stderr parsing without it

Installation

cd fuzzer-orchestrator

# Install backend dependencies
pip install -r backend/requirements.txt

# Install frontend dependencies
cd frontend
npm install
cd ..

Running

Development mode (frontend + backend separately):

# Terminal 1: Backend (port must match vite proxy target)
uvicorn backend.main:app --reload --port 8003

# Terminal 1 (Redis real-time mode):
set FUZZER_REDIS_URL=redis://localhost:6379/0
uvicorn backend.main:app --reload --port 8003

# Terminal 2: Frontend (Vite dev server, proxies /api and /ws to backend)
cd frontend
npm run dev

Open http://localhost:5175 in your browser

Production mode (single server):

# Build frontend
cd frontend && npm run build && cd ..

# Backend automatically serves frontend/dist
uvicorn backend.main:app --port 8003

Open http://localhost:8003 in your browser

Environment Variables

All environment variables use the FUZZER_ prefix (via pydantic-settings).

Variable Default Description
FUZZER_WEBFUZZER_PATH C:/Users/dmbs3/web-fuzzer Root path of the web-fuzzer project
FUZZER_WEBFUZZER_CMD python -m webfuzzer Command to run web-fuzzer
FUZZER_DB_URL sqlite:///backend/data/fuzzer.db Database URL
FUZZER_REDIS_URL "" (disabled) Redis connection URL (e.g., redis://localhost:6379/0). Enables real-time Pub/Sub mode when set
FUZZER_CORS_ORIGINS ["http://localhost:5175", "http://localhost:5174", "http://localhost:5173", "http://localhost:3000"] Allowed CORS origins

Real-time Data Pipeline (Redis Pub/Sub)

Overview

web-fuzzer publishes events (stats, findings, coverage, corpus, status) to Redis Pub/Sub in real-time during fuzzing. The Orchestrator subscribes and immediately saves them to the database + broadcasts via WebSocket.

web-fuzzer (Publisher)          Redis            Orchestrator (Subscriber)
========================      =========      ====================================
engine.run()           ──────> fuzzer:42:status   ──> DB status update + WS broadcast
_maybe_print_status()  ──────> fuzzer:42:stats    ──> DB stats update + WS broadcast
_check_oracles()       ──────> fuzzer:42:finding  ──> DB real-time insert + WS broadcast
corpus.add() success   ──────> fuzzer:42:coverage ──> DB snapshot insert + WS broadcast
                       ──────> fuzzer:42:corpus   ──> DB entry insert

Without Redis (Fallback)

If FUZZER_REDIS_URL is not set, both sides fall back to the legacy stderr regex parsing mode. No frontend changes required.

Enabling Redis

# 1. Start Redis server
redis-server

# 2. Set environment variable and start Orchestrator
set FUZZER_REDIS_URL=redis://localhost:6379/0
uvicorn backend.main:app --port 8003

When the Orchestrator launches a session, it automatically passes FUZZER_REDIS_URL and FUZZER_SESSION_ID to the subprocess environment.

Redis Channel Design

Channel Pattern Data Frequency
fuzzer:{session_id}:stats {elapsed_seconds, total_execs, execs_per_sec, corpus_size, total_edges, unique_findings} ~5s
fuzzer:{session_id}:finding {title, severity, oracle_name, fingerprint, input_hex, input_preview, exit_code, duration_ms, metadata} On discovery
fuzzer:{session_id}:coverage {edge_count, elapsed_sec, exec_count, corpus_size} On new coverage
fuzzer:{session_id}:corpus {seed_id, size_bytes, input_hex, parent_id, depth, energy, metadata} On new seed
fuzzer:{session_id}:log {line} Per log line
fuzzer:{session_id}:status {status} started/completed/failed/stopped

Publishing from External Fuzzers

External processes (not web-fuzzer) can also publish to the channels above in the same format, and the Orchestrator will automatically collect the data.

import redis, json

r = redis.Redis.from_url("redis://localhost:6379/0", decode_responses=True)
session_id = 42

# Send a finding
r.publish(f"fuzzer:{session_id}:finding", json.dumps({
    "title": "Buffer overflow in parse()",
    "severity": "critical",
    "oracle_name": "sanitizer",
    "fingerprint": "abc123",
    "input_hex": "deadbeef",
    "input_preview": "...",
    "exit_code": -11,
    "duration_ms": 15,
    "metadata": {"info": "heap-buffer-overflow"}
}))

# Notify session completion
r.publish(f"fuzzer:{session_id}:status", json.dumps({"status": "completed"}))

Importing web-fuzzer Results

Method 1: Import via Web UI (Recommended)

If you already have results from running web-fuzzer via CLI:

  1. Start the Fuzzer Orchestrator server

  2. Click Import in the left sidebar

  3. Select a Project (create one first on the Projects page if needed)

  4. Enter the absolute path to the web-fuzzer output directory in Results Directory

    Example: C:\Users\dmbs3\web-fuzzer\results\json_diff
    
  5. (Optional) Enter a Session Name — defaults to the directory name if left blank

  6. Click Import Results

After import completes, the number of findings, corpus entries, and duplicates will be displayed. Click View Session to inspect the results immediately.

Method 2: Import via REST API

Use this for automated imports from scripts or CI/CD pipelines.

# 1. Create a project (skip if already exists)
curl -X POST http://localhost:8003/api/projects \
  -H "Content-Type: application/json" \
  -d '{"name": "My JSON Fuzzing", "description": "JSON parser fuzz testing"}'

# Note the project id from the response (e.g., 1)

# 2. Import results
curl -X POST http://localhost:8003/api/import/webfuzzer \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": 1,
    "output_dir": "C:/Users/dmbs3/web-fuzzer/results/json_diff",
    "session_name": "json_diff_run1"
  }'

Example response:

{
  "session_id": 1,
  "findings_imported": 270,
  "corpus_imported": 165,
  "duplicates_found": 0
}

Method 3: Run Fuzzer Directly from Web UI

When you start web-fuzzer from the Orchestrator, results are collected automatically.

  1. Click New Session in the left sidebar
  2. Configure the following:
    • Project: Select a target project
    • Grammar: Select an input grammar (json, html, ecmascript, uri, csp, cookie, multipart)
    • Target: Choose from 13 pre-configured target presets or enter a custom command
    • Mutators: Select mutation strategies (grammar, havoc, token, splice, dictionary, mxss, structural)
    • Scheduler: Seed scheduling strategy (random, entropic, ecofuzz, rare-branch)
    • Oracles: Bug detection oracles (crash, response, sanitizer, xss, mxss, ssrf, url_confusion)
    • Max Iterations / Max Time: Execution limits (0 = unlimited)
    • Differential Reference Commands: Reference commands for differential fuzzing (one per line)
  3. Click Create & Start to launch the fuzzer immediately

During execution, the Session Detail page shows real-time:

  • Execution statistics (exec/s, corpus size, edges, findings)
  • Coverage growth chart
  • Mutator efficiency chart
  • Live logs

Redis mode (when FUZZER_REDIS_URL is set): Findings, corpus, and coverage are saved to the database in real-time as they are discovered. No need to wait for the session to end.

Fallback mode (without Redis): Only stats are collected in real-time via stderr parsing. Findings and corpus are bulk-imported from the filesystem after the session completes.

web-fuzzer Output Directory Structure

The import recognizes the following directory structure:

output_dir/
├── report.json                          # Session statistics (duration, exec count, coverage, etc.)
├── findings/
│   ├── 0000_low_crash/                  # {index}_{severity}_{oracle}
│   │   ├── info.json                    # Title, severity, fingerprint, exit_code, metadata
│   │   └── input                        # The input that triggered the crash
│   ├── 0001_high_differential/
│   │   ├── info.json
│   │   └── input
│   └── ...
└── corpus/
    ├── id_000000                        # Seed input data
    ├── id_000000.meta                   # Seed metadata (depth, energy, exec_count, etc.)
    ├── id_000001
    ├── id_000001.meta
    └── ...

findings/info.json format:

{
  "title": "Differential: accept/reject mismatch (ref[0])",
  "severity": "high",
  "oracle": "differential",
  "fingerprint": "d8be2a24a5fedfc7",
  "exit_code": 1,
  "duration_ms": 187.0,
  "metadata": {
    "strategy": "exit_code",
    "primary_exit": 1,
    "ref_exit": 0
  }
}

report.json format:

{
  "elapsed_seconds": 612.71,
  "total_executions": 530,
  "executions_per_second": 0.9,
  "corpus_size": 165,
  "total_edges": 171,
  "peak_edges": 171,
  "unique_findings": 270,
  "findings_by_severity": { "low": 248, "high": 22 },
  "findings_by_oracle": { "crash": 248, "differential": 22 },
  "mutations_by_mutator": { "grammar": 126, "havoc": 135, "token": 114, "dictionary": 125 },
  "new_coverage_by_mutator": { "grammar": 96, "token": 23, "dictionary": 15, "havoc": 1 }
}

Importing Results from Other Fuzzers

Currently only the web-fuzzer format is supported, but extension points are ready:

  1. Add a new parser class in backend/services/result_parser.py
  2. Add a new endpoint in backend/routers/import_.py (e.g., POST /api/import/afl)
  3. Differentiate fuzzer types using the FuzzSession.fuzzer_type field

Planned support: AFL/AFL++, LibFuzzer, Honggfuzz


API Endpoints

Projects

Method Endpoint Description
GET /api/projects List projects (with statistics)
POST /api/projects Create a project
GET /api/projects/{id} Project details
PUT /api/projects/{id} Update a project
DELETE /api/projects/{id} Delete a project

Sessions

Method Endpoint Description
GET /api/sessions List sessions
POST /api/sessions Create a session
GET /api/sessions/{id} Session details
PUT /api/sessions/{id} Update a session
DELETE /api/sessions/{id} Delete a session
POST /api/sessions/{id}/start Start fuzzer execution
POST /api/sessions/{id}/stop Stop fuzzer execution
GET /api/sessions/{id}/stats Session detailed statistics
GET /api/sessions/{id}/coverage Coverage timeline
POST /api/sessions/{id}/command Send command to running fuzzer (e.g., set_priority)

Findings

Method Endpoint Description
GET /api/findings List findings (filter/pagination)
GET /api/findings/stats Finding statistics
PUT /api/findings/bulk Bulk triage
GET /api/findings/{id} Finding details
PUT /api/findings/{id} Update finding triage
DELETE /api/findings/{id} Delete a finding
GET /api/findings/{id}/input Download finding input data
POST /api/findings/{id}/tags Add tag to finding
DELETE /api/findings/{id}/tags/{tag_id} Remove tag from finding

Corpus

Method Endpoint Description
GET /api/corpus List corpus entries
GET /api/corpus/{id} Corpus entry details
GET /api/corpus/{id}/preview Text preview of corpus input
GET /api/corpus/{id}/input Download corpus input data
DELETE /api/corpus/{id} Delete a corpus entry
PATCH /api/corpus/{id}/priority Update seed priority (forwarded to running fuzzer)

Tags & Comments

Method Endpoint Description
GET /api/tags List tags
POST /api/tags Create a tag
PUT /api/tags/{id} Update a tag
DELETE /api/tags/{id} Delete a tag
GET /api/comments List comments
POST /api/comments Create a comment
PUT /api/comments/{id} Update a comment
DELETE /api/comments/{id} Delete a comment

Import & Config

Method Endpoint Description
POST /api/import/webfuzzer Import web-fuzzer results
GET /api/import/formats List supported import formats
GET /api/config List all available grammars/mutators/schedulers/oracles
GET /api/config/grammars List available grammars
GET /api/config/mutators List available mutators
GET /api/config/schedulers List available schedulers
GET /api/config/oracles List available oracles

Other

Method Endpoint Description
GET /api/health Health check
WS /ws/{session_id} Real-time updates per session
WS /ws/global Global event stream

Project Structure

fuzzer-orchestrator/
├── backend/
│   ├── main.py                 # FastAPI app entry point
│   ├── config.py               # Configuration (DB URL, web-fuzzer path, etc.)
│   ├── database.py             # SQLAlchemy engine + session
│   ├── models.py               # ORM models (9 tables)
│   ├── schemas.py              # Pydantic request/response schemas
│   ├── routers/                # API routers
│   │   ├── projects.py         # Project CRUD
│   │   ├── sessions.py         # Session CRUD + execution control + commands
│   │   ├── findings.py         # Finding filter/triage/tags
│   │   ├── corpus.py           # Corpus queries + priority management
│   │   ├── tags.py             # Tag CRUD
│   │   ├── comments.py         # Comment CRUD
│   │   ├── import_.py          # Result import
│   │   ├── config_.py          # Fuzzer config + target presets
│   │   └── ws.py               # WebSocket
│   ├── services/
│   │   ├── fuzzer_service.py   # Run/stop web-fuzzer as subprocess (auto Redis/stderr fallback)
│   │   ├── redis_subscriber.py # Redis Pub/Sub subscriber + real-time DB writes
│   │   ├── result_parser.py    # Filesystem result parser
│   │   └── websocket_manager.py # WebSocket connection manager
│   └── requirements.txt
└── frontend/
    ├── src/
    │   ├── pages/              # 9 pages
    │   ├── components/         # Reusable UI components
    │   ├── api/                # Axios API client
    │   ├── hooks/              # useWebSocket, etc.
    │   ├── types/              # TypeScript interfaces
    │   └── utils/              # Formatters, color mappings
    └── package.json

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors