FuzzForge Backend
A stateless API server for security testing workflow orchestration using Prefect. This system dynamically discovers workflows, executes them in isolated Docker containers with volume mounting, and returns findings in SARIF format.
Architecture Overview
Core Components
- Workflow Discovery System: Automatically discovers workflows at startup
- Module System: Reusable components (scanner, analyzer, reporter) with a common interface
- Prefect Integration: Handles container orchestration, workflow execution, and monitoring
- Volume Mounting: Secure file access with configurable permissions (ro/rw)
- SARIF Output: Standardized security findings format
Key Features
- Stateless: No persistent data, fully scalable
- Generic: No hardcoded workflows, automatic discovery
- Isolated: Each workflow runs in its own Docker container
- Extensible: Easy to add new workflows and modules
- Secure: Read-only volume mounts by default, path validation
- Observable: Comprehensive logging and status tracking
Quick Start
Prerequisites
- Docker and Docker Compose
Installation
From the project root, start all services:
docker-compose up -d
This will start:
- Prefect server (API at http://localhost:4200/api)
- PostgreSQL database
- Redis cache
- Docker registry (port 5001)
- Prefect worker (for running workflows)
- FuzzForge backend API (port 8000)
- FuzzForge MCP server (port 8010)
Note: The Prefect UI at http://localhost:4200 is not currently accessible from the host due to the API being configured for inter-container communication. Use the REST API or MCP interface instead.
API Endpoints
Workflows
GET /workflows- List all discovered workflowsGET /workflows/{name}/metadata- Get workflow metadata and parametersGET /workflows/{name}/parameters- Get workflow parameter schemaGET /workflows/metadata/schema- Get metadata.yaml schemaPOST /workflows/{name}/submit- Submit a workflow for execution
Runs
GET /runs/{run_id}/status- Get run statusGET /runs/{run_id}/findings- Get SARIF findings from completed runGET /runs/{workflow_name}/findings/{run_id}- Alternative findings endpoint with workflow name
Workflow Structure
Each workflow must have:
toolbox/workflows/{workflow_name}/
workflow.py # Prefect flow definition
metadata.yaml # Mandatory metadata (parameters, version, etc.)
Dockerfile # Optional custom container definition
requirements.txt # Optional Python dependencies
Example metadata.yaml
name: security_assessment
version: "1.0.0"
description: "Comprehensive security analysis workflow"
author: "FuzzForge Team"
category: "comprehensive"
tags:
- "security"
- "analysis"
- "comprehensive"
supported_volume_modes:
- "ro"
- "rw"
requirements:
tools:
- "file_scanner"
- "security_analyzer"
- "sarif_reporter"
resources:
memory: "512Mi"
cpu: "500m"
timeout: 1800
has_docker: true
parameters:
type: object
properties:
target_path:
type: string
default: "/workspace"
description: "Path to analyze"
volume_mode:
type: string
enum: ["ro", "rw"]
default: "ro"
description: "Volume mount mode"
scanner_config:
type: object
description: "Scanner configuration"
properties:
max_file_size:
type: integer
description: "Maximum file size to scan (bytes)"
output_schema:
type: object
properties:
sarif:
type: object
description: "SARIF-formatted security findings"
summary:
type: object
description: "Scan execution summary"
Metadata Field Descriptions
- name: Workflow identifier (must match directory name)
- version: Semantic version (x.y.z format)
- description: Human-readable description of the workflow
- author: Workflow author/maintainer
- category: Workflow category (comprehensive, specialized, fuzzing, focused)
- tags: Array of descriptive tags for categorization
- requirements.tools: Required security tools that the workflow uses
- requirements.resources: Resource requirements enforced at runtime:
memory: Memory limit (e.g., "512Mi", "1Gi")cpu: CPU limit (e.g., "500m" for 0.5 cores, "1" for 1 core)timeout: Maximum execution time in seconds
- parameters: JSON Schema object defining workflow parameters
- output_schema: Expected output format (typically SARIF)
Resource Requirements
Resource requirements defined in workflow metadata are automatically enforced. Users can override defaults when submitting workflows:
curl -X POST "http://localhost:8000/workflows/security_assessment/submit" \
-H "Content-Type: application/json" \
-d '{
"target_path": "/tmp/project",
"volume_mode": "ro",
"resource_limits": {
"memory_limit": "1Gi",
"cpu_limit": "1"
}
}'
Resource precedence: User limits > Workflow requirements > System defaults
Module Development
Modules implement the BaseModule interface:
from src.toolbox.modules.base import BaseModule, ModuleMetadata, ModuleResult
class MyModule(BaseModule):
def get_metadata(self) -> ModuleMetadata:
return ModuleMetadata(
name="my_module",
version="1.0.0",
description="Module description",
category="scanner",
...
)
async def execute(self, config: Dict, workspace: Path) -> ModuleResult:
# Module logic here
findings = [...]
return self.create_result(findings=findings)
def validate_config(self, config: Dict) -> bool:
# Validate configuration
return True
Submitting a Workflow
curl -X POST "http://localhost:8000/workflows/security_assessment/submit" \
-H "Content-Type: application/json" \
-d '{
"target_path": "/home/user/project",
"volume_mode": "ro",
"parameters": {
"scanner_config": {"patterns": ["*.py"]},
"analyzer_config": {"check_secrets": true}
}
}'
Getting Findings
curl "http://localhost:8000/runs/{run_id}/findings"
Returns SARIF-formatted findings:
{
"workflow": "security_assessment",
"run_id": "abc-123",
"sarif": {
"version": "2.1.0",
"runs": [{
"tool": {...},
"results": [...]
}]
}
}
Security Considerations
- Volume Mounting: Only allowed directories can be mounted
- Read-Only Default: Volumes mounted as read-only unless explicitly set
- Container Isolation: Each workflow runs in an isolated container
- Resource Limits: Can set CPU/memory limits via Prefect
- Network Isolation: Containers use bridge networking
Development
Adding a New Workflow
- Create directory:
toolbox/workflows/my_workflow/ - Add
workflow.pywith a Prefect flow - Add mandatory
metadata.yaml - Restart backend:
docker-compose restart fuzzforge-backend
Adding a New Module
- Create module in
toolbox/modules/{category}/ - Implement
BaseModuleinterface - Use in workflows via import