Skip to main content

forge_guard/fuzzing/
mod.rs

1//! Fuzzing adapter — provides integration points for fuzzing campaigns.
2
3use crate::core::ForgeGuardError;
4
5/// Configuration for a fuzzing campaign.
6pub struct FuzzingConfig {
7    /// Number of fuzz runs.
8    pub runs: u32,
9    /// Fuzz seed for reproducibility.
10    pub seed: Option<u64>,
11    /// Test function filter.
12    pub test_filter: Option<String>,
13    /// Contract filter.
14    pub contract_filter: Option<String>,
15    /// Whether to fail on revert.
16    pub fail_on_revert: bool,
17}
18
19impl Default for FuzzingConfig {
20    fn default() -> Self {
21        Self {
22            runs: 10_000,
23            seed: None,
24            test_filter: None,
25            contract_filter: None,
26            fail_on_revert: false,
27        }
28    }
29}
30
31/// Adapter for running fuzzing campaigns via Forge.
32pub struct FuzzingAdapter;
33
34impl FuzzingAdapter {
35    /// Run a fuzzing campaign using forge.
36    pub fn run_forge_fuzz(config: &FuzzingConfig) -> Result<FuzzingReport, ForgeGuardError> {
37        let mut cmd = std::process::Command::new("forge");
38        cmd.arg("test");
39        cmd.arg("--fuzz-runs").arg(config.runs.to_string());
40
41        if let Some(seed) = config.seed {
42            cmd.arg("--fuzz-seed").arg(seed.to_string());
43        }
44
45        if let Some(test) = &config.test_filter {
46            cmd.arg("--match-test").arg(test);
47        }
48
49        if let Some(contract) = &config.contract_filter {
50            cmd.arg("--match-contract").arg(contract);
51        }
52
53        if config.fail_on_revert {
54            cmd.arg("--fail-on-revert");
55        }
56
57        let start = std::time::Instant::now();
58        let output = cmd
59            .output()
60            .map_err(|e| ForgeGuardError::Command(format!("Failed to run fuzz: {}", e)))?;
61
62        let duration = start.elapsed();
63
64        Ok(FuzzingReport {
65            passed: output.status.success(),
66            runs: config.runs,
67            duration_secs: duration.as_secs_f64(),
68            output: String::from_utf8_lossy(&output.stdout).to_string(),
69            errors: if output.status.success() {
70                String::new()
71            } else {
72                String::from_utf8_lossy(&output.stderr).to_string()
73            },
74        })
75    }
76}
77
78/// Result of a fuzzing campaign.
79#[derive(Debug, Clone)]
80pub struct FuzzingReport {
81    pub passed: bool,
82    pub runs: u32,
83    pub duration_secs: f64,
84    pub output: String,
85    pub errors: String,
86}