mirror of
https://github.com/FuzzingLabs/fuzzforge_ai.git
synced 2026-03-27 09:30:20 +01:00
This commit implements a complete Python fuzzing workflow using Atheris: ## Python Worker (workers/python/) - Dockerfile with Python 3.11, Atheris, and build tools - Generic worker.py for dynamic workflow discovery - requirements.txt with temporalio, boto3, atheris dependencies - Added to docker-compose.temporal.yaml with dedicated cache volume ## AtherisFuzzer Module (backend/toolbox/modules/fuzzer/) - Reusable module extending BaseModule - Auto-discovers fuzz targets (fuzz_*.py, *_fuzz.py, fuzz_target.py) - Recursive search to find targets in nested directories - Dynamically loads TestOneInput() function - Configurable max_iterations and timeout - Real-time stats callback support for live monitoring - Returns findings as ModuleFinding objects ## Atheris Fuzzing Workflow (backend/toolbox/workflows/atheris_fuzzing/) - Temporal workflow for orchestrating fuzzing - Downloads user code from MinIO - Executes AtherisFuzzer module - Uploads results to MinIO - Cleans up cache after execution - metadata.yaml with vertical: python for routing ## Test Project (test_projects/python_fuzz_waterfall/) - Demonstrates stateful waterfall vulnerability - main.py with check_secret() that leaks progress - fuzz_target.py with Atheris TestOneInput() harness - Complete README with usage instructions ## Backend Fixes - Fixed parameter merging in REST API endpoints (workflows.py) - Changed workflow parameter passing from positional args to kwargs (manager.py) - Default parameters now properly merged with user parameters ## Testing ✅ Worker discovered AtherisFuzzingWorkflow ✅ Workflow executed end-to-end successfully ✅ Fuzz target auto-discovered in nested directories ✅ Atheris ran 100,000 iterations ✅ Results uploaded and cache cleaned
91 lines
3.0 KiB
Python
91 lines
3.0 KiB
Python
"""
|
|
Atheris Fuzzing Workflow Activities
|
|
|
|
Activities specific to the Atheris fuzzing workflow.
|
|
"""
|
|
|
|
import logging
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Dict, Any
|
|
|
|
from temporalio import activity
|
|
|
|
# Configure logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Add toolbox to path for module imports
|
|
sys.path.insert(0, '/app/toolbox')
|
|
|
|
|
|
@activity.defn(name="fuzz_with_atheris")
|
|
async def fuzz_activity(workspace_path: str, config: dict) -> dict:
|
|
"""
|
|
Fuzzing activity using the AtherisFuzzer module on user code.
|
|
|
|
This activity:
|
|
1. Imports the reusable AtherisFuzzer module
|
|
2. Sets up real-time stats callback
|
|
3. Executes fuzzing on user's TestOneInput() function
|
|
4. Returns findings as ModuleResult
|
|
|
|
Args:
|
|
workspace_path: Path to the workspace directory (user's uploaded code)
|
|
config: Fuzzer configuration (target_file, max_iterations, timeout_seconds)
|
|
|
|
Returns:
|
|
Fuzzer results dictionary (findings, summary, metadata)
|
|
"""
|
|
logger.info(f"Activity: fuzz_with_atheris (workspace={workspace_path})")
|
|
|
|
try:
|
|
# Import reusable AtherisFuzzer module
|
|
from modules.fuzzer import AtherisFuzzer
|
|
|
|
workspace = Path(workspace_path)
|
|
if not workspace.exists():
|
|
raise FileNotFoundError(f"Workspace not found: {workspace_path}")
|
|
|
|
# Get activity info for real-time stats
|
|
info = activity.info()
|
|
run_id = info.workflow_id
|
|
|
|
# Define stats callback for real-time monitoring
|
|
async def stats_callback(stats_data: Dict[str, Any]):
|
|
"""Callback for live fuzzing statistics"""
|
|
try:
|
|
logger.info("LIVE_STATS", extra={
|
|
"stats_type": "fuzzing_live_update",
|
|
"workflow_type": "atheris_fuzzing",
|
|
"run_id": run_id,
|
|
"executions": stats_data.get("total_execs", 0),
|
|
"executions_per_sec": stats_data.get("execs_per_sec", 0.0),
|
|
"crashes": stats_data.get("crashes", 0),
|
|
"corpus_size": stats_data.get("corpus_size", 0),
|
|
"coverage": stats_data.get("coverage", 0.0),
|
|
"elapsed_time": stats_data.get("elapsed_time", 0),
|
|
"timestamp": datetime.utcnow().isoformat()
|
|
})
|
|
except Exception as e:
|
|
logger.warning(f"Error in stats callback: {e}")
|
|
|
|
# Add stats callback to config
|
|
config["stats_callback"] = stats_callback
|
|
|
|
# Execute the fuzzer module
|
|
fuzzer = AtherisFuzzer()
|
|
result = await fuzzer.execute(config, workspace)
|
|
|
|
logger.info(
|
|
f"✓ Fuzzing completed: "
|
|
f"{result.summary.get('total_executions', 0)} executions, "
|
|
f"{result.summary.get('crashes_found', 0)} crashes"
|
|
)
|
|
|
|
return result.dict()
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fuzzing failed: {e}", exc_info=True)
|
|
raise
|