trident-cli 0.12.0

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

use fehler::throws;

mod command;

use crate::command::FuzzCommand;

#[derive(Parser)]
#[command(
    name = "Trident",
    about = "Trident is Rust based fuzzer for Solana programs written using Anchor framework.",
    version = env!("CARGO_PKG_VERSION")
)]
struct Cli {
    #[clap(subcommand)]
    command: Command,
}

#[derive(Subcommand)]
enum Command {
    #[command(about = "Show the HowTo message.")]
    How,
    #[command(
        about = "Initialize Trident in the current Anchor workspace.",
        override_usage = "\nTrident will skip initialization if Trident.toml already exists."
    )]
    Init {
        #[arg(
            short,
            long,
            required = false,
            help = "Force Trident initialization. Trident dependencies will be updated based on the version of Trident CLI."
        )]
        force: bool,
        #[arg(
            short,
            long,
            required = false,
            help = "Skip building the program before initializing Trident."
        )]
        skip_build: bool,
        #[arg(
            short,
            long,
            required = false,
            help = "Specify the name of the program for which fuzz test will be generated.",
            value_name = "FILE"
        )]
        program_name: Option<String>,
        #[arg(
            short,
            long,
            required = false,
            help = "Name of the fuzz test to initialize.",
            value_name = "NAME"
        )]
        test_name: Option<String>,
    },
    #[command(
        about = "Run fuzz subcommands.",
        override_usage = "With fuzz subcommands you can add new fuzz test \
        template or you can run fuzz test on already initialzied one.\
        \n\n\x1b[1m\x1b[4mEXAMPLE:\x1b[0m\
        \n    trident fuzz add\
        \n    trident fuzz run fuzz_0\
        \n    trident fuzz debug \x1b[92m<FUZZ_TARGET>\x1b[0m \x1b[92m<SEED>\x1b[0m\
        \n    trident fuzz refresh fuzz_0"
    )]
    Fuzz {
        #[clap(subcommand)]
        subcmd: FuzzCommand,
    },
    #[command(about = "Clean build target, additionally perform `anchor clean`")]
    Clean,
    #[command(about = "Start HTTP server to serve fuzzing dashboards")]
    Server {
        #[arg(
            short,
            long,
            required = false,
            help = "Directory to monitor for dashboard files",
            value_name = "DIR",
            default_value = ".fuzz-artifacts"
        )]
        directory: String,
        #[arg(
            short,
            long,
            required = false,
            help = "Port to run the server on",
            value_name = "PORT",
            default_value = "8000"
        )]
        port: u16,
        #[arg(
            long,
            required = false,
            help = "Host to bind the server to",
            value_name = "HOST",
            default_value = "localhost"
        )]
        host: String,
    },
    #[command(about = "Compare two regression JSON files and identify differing iteration seeds")]
    Compare {
        #[arg(help = "Path to the first regression JSON file", value_name = "FILE1")]
        file1: String,
        #[arg(help = "Path to the second regression JSON file", value_name = "FILE2")]
        file2: String,
    },
}

#[throws]
pub async fn start() {
    let cli = Cli::parse();

    match cli.command {
        Command::How => command::howto()?,
        Command::Fuzz { subcmd } => command::fuzz(subcmd).await?,
        Command::Init {
            force,
            skip_build,
            program_name,
            test_name,
        } => command::init(force, skip_build, program_name, test_name).await?,
        Command::Clean => command::clean().await?,
        Command::Server {
            directory,
            port,
            host,
        } => command::server(directory, port, host).await?,
        Command::Compare { file1, file2 } => command::compare_regression(file1, file2)?,
    }
}