forge-guard 0.1.3

Pre-deployment smart contract auditing framework for Foundry
Documentation
//! Fuzzing adapter — provides integration points for fuzzing campaigns.

use crate::core::ForgeGuardError;

/// Configuration for a fuzzing campaign.
pub struct FuzzingConfig {
    /// Number of fuzz runs.
    pub runs: u32,
    /// Fuzz seed for reproducibility.
    pub seed: Option<u64>,
    /// Test function filter.
    pub test_filter: Option<String>,
    /// Contract filter.
    pub contract_filter: Option<String>,
    /// Whether to fail on revert.
    pub fail_on_revert: bool,
}

impl Default for FuzzingConfig {
    fn default() -> Self {
        Self {
            runs: 10_000,
            seed: None,
            test_filter: None,
            contract_filter: None,
            fail_on_revert: false,
        }
    }
}

/// Adapter for running fuzzing campaigns via Forge.
pub struct FuzzingAdapter;

impl FuzzingAdapter {
    /// Run a fuzzing campaign using forge.
    pub fn run_forge_fuzz(config: &FuzzingConfig) -> Result<FuzzingReport, ForgeGuardError> {
        let mut cmd = std::process::Command::new("forge");
        cmd.arg("test");
        cmd.arg("--fuzz-runs").arg(config.runs.to_string());

        if let Some(seed) = config.seed {
            cmd.arg("--fuzz-seed").arg(seed.to_string());
        }

        if let Some(test) = &config.test_filter {
            cmd.arg("--match-test").arg(test);
        }

        if let Some(contract) = &config.contract_filter {
            cmd.arg("--match-contract").arg(contract);
        }

        if config.fail_on_revert {
            cmd.arg("--fail-on-revert");
        }

        let start = std::time::Instant::now();
        let output = cmd
            .output()
            .map_err(|e| ForgeGuardError::Command(format!("Failed to run fuzz: {}", e)))?;

        let duration = start.elapsed();

        Ok(FuzzingReport {
            passed: output.status.success(),
            runs: config.runs,
            duration_secs: duration.as_secs_f64(),
            output: String::from_utf8_lossy(&output.stdout).to_string(),
            errors: if output.status.success() {
                String::new()
            } else {
                String::from_utf8_lossy(&output.stderr).to_string()
            },
        })
    }
}

/// Result of a fuzzing campaign.
#[derive(Debug, Clone)]
pub struct FuzzingReport {
    pub passed: bool,
    pub runs: u32,
    pub duration_secs: f64,
    pub output: String,
    pub errors: String,
}