trident-cli 0.12.0

Trident is Rust based fuzzing framework for Solana programs written in Anchor.
Documentation
use anyhow::Error;

use clap::Subcommand;
use fehler::throws;
use heck::ToSnakeCase;
use trident_client::___private::Commander;
use trident_client::___private::TestGenerator;

use crate::command::check_anchor_initialized;
use crate::command::check_fuzz_test_exists;
use crate::command::check_fuzz_test_not_exists;
use crate::command::check_trident_uninitialized;

#[derive(Subcommand)]
#[allow(non_camel_case_types)]
pub(crate) enum FuzzCommand {
    #[command(about = "Generate new Fuzz Test template.")]
    Add {
        #[arg(
            short,
            long,
            required = false,
            help = "Specify the name of the program for which the fuzz test will be generated.",
            value_name = "FILE"
        )]
        program_name: Option<String>,
        #[arg(
            short,
            long,
            required = false,
            help = "Name of the fuzz test to add.",
            value_name = "NAME"
        )]
        test_name: Option<String>,
        #[arg(
            short,
            long,
            required = false,
            help = "Skip building the program before adding new fuzz test."
        )]
        skip_build: bool,
    },
    Run {
        #[arg(
            required = true,
            help = "Name of the desired fuzz template to execute (for example fuzz_0)."
        )]
        target: String,
        #[arg(
            short,
            long,
            required = false,
            help = "Run the fuzzing with exit code, i.e. if it discovers invariant failures or panics the Trident will exit with exit code."
        )]
        with_exit_code: bool,
        #[arg(
            required = false,
            help = "Master seed used for fuzzing, if not provided it will be generated randomly."
        )]
        seed: Option<String>,
    },
    Debug {
        #[arg(
            required = true,
            help = "Name of the desired fuzz template to execute (for example fuzz_0)."
        )]
        target: String,
        #[arg(
            required = true,
            help = "Master seed of the desired fuzz template to execute."
        )]
        seed: String,
    },
    Refresh {
        #[arg(
            required = true,
            help = "Name of the fuzz test to refresh (for example fuzz_0)."
        )]
        target: String,
        #[arg(
            short,
            long,
            required = false,
            help = "Specify the name of the program for which the fuzz test will be refreshed.",
            value_name = "FILE"
        )]
        program_name: Option<String>,
        #[arg(
            short,
            long,
            required = false,
            help = "Skip building the program before refreshing the types file."
        )]
        skip_build: bool,
    },
}

#[throws]
pub(crate) async fn fuzz(subcmd: FuzzCommand) {
    let root = check_anchor_initialized()?;

    check_trident_uninitialized(&root)?;

    match subcmd {
        FuzzCommand::Run {
            target,
            with_exit_code,
            seed,
        } => {
            let commander = Commander::new(&root);

            commander.run(target, with_exit_code, seed).await?;
        }
        FuzzCommand::Debug { target, seed } => {
            let commander = Commander::new(&root);

            commander.run_debug(target, seed).await?;
        }

        FuzzCommand::Add {
            program_name,
            test_name,
            skip_build,
        } => {
            let test_name_snake = test_name.map(|name| name.to_snake_case());

            let mut generator = TestGenerator::new_with_root(&root, skip_build)?;

            if let Some(test_name) = &test_name_snake {
                check_fuzz_test_exists(&root, test_name)?;
            }

            generator
                .add_fuzz_test(program_name, test_name_snake)
                .await?;
        }
        FuzzCommand::Refresh {
            target,
            program_name,
            skip_build,
        } => {
            check_fuzz_test_not_exists(&root, &target)?;

            let mut generator = TestGenerator::new_with_root(&root, skip_build)?;

            generator.refresh_fuzz_test(target, program_name).await?;
        }
    };
}