Files
fuzzforge_ai/setup.py
2025-09-30 12:10:47 +02:00

313 lines
13 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# Copyright (c) 2025 FuzzingLabs
#
# Licensed under the Business Source License 1.1 (BSL). See the LICENSE file
# at the root of this repository for details.
#
# After the Change Date (four years from publication), this version of the
# Licensed Work will be made available under the Apache License, Version 2.0.
# See the LICENSE-APACHE file or http://www.apache.org/licenses/LICENSE-2.0
#
# Additional attribution and requirements are provided in the NOTICE file.
"""
FuzzForge Setup Script - One-command setup for development
This script automates the entire FuzzForge development setup process,
from checking prerequisites to running your first security scan.
"""
import os
import sys
import subprocess
import platform
import time
from pathlib import Path
from typing import List, Tuple, Optional
class Colors:
"""ANSI color codes for terminal output"""
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BLUE = '\033[94m'
CYAN = '\033[96m'
WHITE = '\033[97m'
BOLD = '\033[1m'
END = '\033[0m'
class FuzzForgeSetup:
"""Automated FuzzForge development environment setup"""
def __init__(self):
self.system = platform.system().lower()
self.project_root = Path(__file__).parent
self.errors: List[str] = []
self.warnings: List[str] = []
def print_header(self):
"""Print welcome header"""
print(f"""{Colors.CYAN}{Colors.BOLD}
╔══════════════════════════════════════════╗
║ FuzzForge Setup Script ║
║ Automated Development Setup ║
╚══════════════════════════════════════════╝
{Colors.END}""")
print(f"{Colors.WHITE}Welcome to FuzzForge! This script will set up your development environment.{Colors.END}\n")
def run_command(self, command: str, description: str, critical: bool = True) -> Tuple[bool, str]:
"""Run a shell command and return success status and output"""
print(f"{Colors.YELLOW}🔄 {description}...{Colors.END}")
try:
result = subprocess.run(
command.split(),
capture_output=True,
text=True,
timeout=120 # 2 minute timeout
)
if result.returncode == 0:
print(f"{Colors.GREEN}{description} completed successfully{Colors.END}")
return True, result.stdout
else:
error_msg = f"{description} failed: {result.stderr.strip()}"
if critical:
self.errors.append(error_msg)
print(f"{Colors.RED}{error_msg}{Colors.END}")
else:
self.warnings.append(error_msg)
print(f"{Colors.YELLOW}⚠️ {error_msg}{Colors.END}")
return False, result.stderr
except subprocess.TimeoutExpired:
error_msg = f"{description} timed out"
if critical:
self.errors.append(error_msg)
else:
self.warnings.append(error_msg)
print(f"{Colors.RED}{error_msg}{Colors.END}")
return False, "Timeout"
except Exception as e:
error_msg = f"{description} failed with exception: {e}"
if critical:
self.errors.append(error_msg)
else:
self.warnings.append(error_msg)
print(f"{Colors.RED}💥 {error_msg}{Colors.END}")
return False, str(e)
def check_prerequisites(self) -> bool:
"""Check if required tools are installed"""
print(f"{Colors.BOLD}📋 Step 1: Checking Prerequisites{Colors.END}\n")
all_good = True
# Check Python version
python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
if sys.version_info >= (3, 11):
print(f"{Colors.GREEN}✅ Python {python_version} (required: 3.11+){Colors.END}")
else:
print(f"{Colors.RED}❌ Python {python_version} (required: 3.11+){Colors.END}")
self.errors.append(f"Python version {python_version} is too old. Please install Python 3.11+")
all_good = False
# Check Docker daemon is running
docker_success, docker_output = self.run_command("docker ps", "Checking Docker", critical=False)
if not docker_success:
self.errors.append("Docker daemon is not running. Please start Docker Desktop")
all_good = False
# Check Docker Compose with actual compose file validation
if docker_success:
compose_success, _ = self.run_command("docker compose config --quiet", "Checking Docker Compose", critical=False)
if not compose_success:
self.errors.append("Docker Compose validation failed. Please ensure docker-compose.yaml is valid and Docker is running")
all_good = False
else:
print(f"{Colors.RED}❌ Checking Docker Compose failed: Docker daemon not running{Colors.END}")
self.errors.append("Docker Compose cannot be validated - Docker daemon not running")
all_good = False
# Check UV
uv_success, _ = self.run_command("uv --version", "Checking UV package manager", critical=False)
if not uv_success:
print(f"{Colors.YELLOW}📦 UV not found, installing UV...{Colors.END}")
if self.system == "darwin": # macOS
subprocess.run(["curl", "-LsSf", "https://astral.sh/uv/install.sh", "|", "sh"], shell=True)
else:
subprocess.run(["pip", "install", "uv"])
# Recheck UV
uv_success, _ = self.run_command("uv --version", "Re-checking UV installation", critical=False)
if not uv_success:
self.warnings.append("UV installation failed. You can install it manually later")
return all_good
def setup_docker_environment(self) -> bool:
"""Set up Docker environment"""
print(f"\n{Colors.BOLD}🐳 Step 2: Setting Up Docker Environment{Colors.END}\n")
# Check if Docker daemon is running
docker_running, _ = self.run_command("docker ps", "Checking Docker daemon", critical=False)
if not docker_running:
print(f"{Colors.YELLOW}⚠️ Docker daemon is not running. Please start Docker Desktop and try again.{Colors.END}")
return False
# Start FuzzForge services
os.chdir(self.project_root)
# Warning about first launch
print(f"{Colors.CYAN} First launch will take longer due to Docker image building (5-10 minutes).{Colors.END}")
print(f"{Colors.CYAN} Subsequent starts will be much faster!{Colors.END}\n")
# Build and start services
print(f"{Colors.YELLOW}🔨 Building and starting FuzzForge services (this may take a while)...{Colors.END}")
# Use longer timeout for Docker build (10 minutes)
try:
result = subprocess.run(
["docker", "compose", "up", "-d"],
capture_output=True,
text=True,
timeout=600, # 10 minute timeout for Docker build
cwd=self.project_root
)
if result.returncode == 0:
print(f"{Colors.GREEN}✅ Docker services started successfully{Colors.END}")
else:
self.errors.append(f"Docker services failed to start: {result.stderr.strip()}")
print(f"{Colors.RED}❌ Docker services failed to start: {result.stderr.strip()}{Colors.END}")
return False
except subprocess.TimeoutExpired:
self.errors.append("Docker build timed out after 10 minutes")
print(f"{Colors.RED}⏰ Docker build timed out after 10 minutes{Colors.END}")
return False
except Exception as e:
self.errors.append(f"Docker setup failed: {e}")
print(f"{Colors.RED}💥 Docker setup failed: {e}{Colors.END}")
return False
# Wait for services to be ready with extended timeout
print(f"{Colors.YELLOW}⏳ Waiting for services to be ready...{Colors.END}")
for i in range(120): # Wait up to 2 minutes for services to be ready
time.sleep(1)
health_success, _ = self.run_command("curl -s http://localhost:8000/health", "Health check", critical=False)
if health_success:
print(f"\n{Colors.GREEN}✅ FuzzForge API is ready at http://localhost:8000!{Colors.END}")
return True
if i % 10 == 0: # Print progress every 10 seconds
print(f"\n{Colors.CYAN} Still starting... ({i+1}s){Colors.END}")
else:
print(".", end="", flush=True)
print(f"\n{Colors.YELLOW}⚠️ Services may still be starting. Check status with 'docker compose logs'{Colors.END}")
print(f"{Colors.CYAN}💡 You can monitor progress with: docker compose logs -f{Colors.END}")
return True
def install_cli(self) -> bool:
"""Install FuzzForge CLI"""
print(f"\n{Colors.BOLD}💻 Step 3: Installing FuzzForge CLI (Final Step){Colors.END}\n")
cli_dir = self.project_root / "cli"
if not cli_dir.exists():
self.errors.append("CLI directory not found")
return False
# Install from root, pointing to the 'cli' directory
success, _ = self.run_command("uv tool install --python python3.12 .", "Installing FuzzForge CLI with Python 3.12")
return success
def print_next_steps(self):
"""Print next steps for the user"""
print(f"\n{Colors.BOLD}{Colors.GREEN}🎉 Setup Complete!{Colors.END}")
if not self.errors:
print(f"""
{Colors.CYAN}🚀 FuzzForge is now ready! Here's what you can do next:{Colors.END}
{Colors.BOLD}📖 Learn More:{Colors.END}
{Colors.BLUE}docs/QUICKSTART.md{Colors.END} - 5-minute module creation tutorial
{Colors.BLUE}docs/PATTERNS.md{Colors.END} - Common patterns and recipes
{Colors.BLUE}cli/README.md{Colors.END} - Complete CLI documentation
{Colors.BOLD}🔍 Try Some Commands:{Colors.END}
{Colors.WHITE}cd test_projects/vulnerable_app{Colors.END} - Navigate to test project
{Colors.WHITE}fuzzforge init{Colors.END} - Initialize a FuzzForge project
{Colors.WHITE}ff workflow security_assessment .{Colors.END} - Run security assessment
{Colors.WHITE}ff workflow secret_detection_scan .{Colors.END} - Run secret detection
{Colors.WHITE}fuzzforge status{Colors.END} - Check project and workflow status
{Colors.WHITE}fuzzforge --help{Colors.END} - See all available commands
{Colors.BOLD}🔧 Available Workflows:{Colors.END}
{Colors.CYAN}security_assessment{Colors.END} - Comprehensive security scanning
{Colors.CYAN}secret_detection_scan{Colors.END} - Credential and secret detection
{Colors.BOLD}🌐 Web Interface:{Colors.END}
• API: {Colors.WHITE}http://localhost:8000{Colors.END}
• Health: {Colors.WHITE}http://localhost:8000/health{Colors.END}
• API Docs: {Colors.WHITE}http://localhost:8000/docs{Colors.END}
""")
if self.warnings:
print(f"\n{Colors.YELLOW}⚠️ Warnings:{Colors.END}")
for warning in self.warnings:
print(f"{warning}")
if self.errors:
print(f"\n{Colors.RED}❌ Errors that need attention:{Colors.END}")
for error in self.errors:
print(f"{error}")
print(f"\n{Colors.YELLOW}🔧 Please fix these issues and run the setup again.{Colors.END}")
def run(self):
"""Run the complete setup process"""
self.print_header()
# Step 1: Prerequisites
if not self.check_prerequisites():
if self.errors:
self.print_next_steps()
return False
# Step 2: Docker setup
if not self.setup_docker_environment():
if self.errors:
self.print_next_steps()
return False
# Step 3: CLI installation
self.install_cli()
# Final summary
self.print_next_steps()
return len(self.errors) == 0
def main():
"""Main entry point"""
setup = FuzzForgeSetup()
try:
success = setup.run()
sys.exit(0 if success else 1)
except KeyboardInterrupt:
print(f"\n{Colors.YELLOW}Setup interrupted by user. You can run this script again anytime.{Colors.END}")
sys.exit(1)
except Exception as e:
print(f"\n{Colors.RED}Unexpected error: {e}{Colors.END}")
print(f"{Colors.YELLOW}Please report this issue with the error details above.{Colors.END}")
sys.exit(1)
if __name__ == "__main__":
main()