1use anyhow::Error;
2use anyhow::{Context, Result};
3use clap::{Parser, Subcommand};
4use fehler::throws;
5
6mod command;
8use command::FuzzCommand;
10use termimad::MadSkin;
11
12macro_rules! load_template {
13 ($file:expr) => {
14 include_str!(concat!(env!("CARGO_MANIFEST_DIR"), $file))
15 };
16}
17
18#[derive(Parser)]
20#[command(
21 name = "Trident",
22 about = "Trident is Rust based fuzzer for Solana programs written using Anchor framework."
23)]
24struct Cli {
25 #[clap(subcommand)]
26 command: Command,
27}
28
29#[derive(Subcommand)]
30enum Command {
31 #[command(about = "Show the HowTo message.")]
32 How,
33 #[command(
34 about = "Initialize Trident in the current Anchor workspace.",
35 override_usage = "\nTrident will skip initialization if Trident.toml already exists."
36 )]
37 Init {
38 #[arg(
39 short,
40 long,
41 required = false,
42 help = "Force Trident initialization. Trident dependencies will be updated based on the version of Trident CLI."
43 )]
44 force: bool,
45 #[arg(
46 short,
47 long,
48 required = false,
49 help = "Specify the name of the program for which fuzz test will be generated.",
50 value_name = "FILE"
51 )]
52 program_name: Option<String>,
53 #[arg(
54 short,
55 long,
56 required = false,
57 help = "Name of the fuzz test to initialize.",
58 value_name = "NAME"
59 )]
60 test_name: Option<String>,
61 },
62 #[command(
63 about = "Run fuzz subcommands.",
64 override_usage = "With fuzz subcommands you can add new fuzz test \
65 template or you can run fuzz test on already initialzied one.\
66 \n\n\x1b[1m\x1b[4mEXAMPLE:\x1b[0m\
67 \n trident add\
68 \n trident fuzz run-hfuzz fuzz_0\
69 \n trident fuzz debug-hfuzz \x1b[92m<FUZZ_TARGET>\x1b[0m \x1b[92m<PATH_TO_CRASHFILE>\x1b[0m"
70 )]
71 Fuzz {
72 #[clap(subcommand)]
73 subcmd: FuzzCommand,
74 },
75 #[command(about = "Clean Honggfuzz build targets ,additionally perform `anchor clean`")]
76 Clean,
77}
78
79#[throws]
80pub async fn start() {
81 let cli = Cli::parse();
82
83 match cli.command {
84 Command::How => command::howto()?,
85 Command::Fuzz { subcmd } => command::fuzz(subcmd).await?,
86 Command::Init {
87 force,
88 program_name,
89 test_name,
90 } => command::init(force, program_name, test_name).await?,
91 Command::Clean => command::clean().await?,
92 }
93}
94
95fn _discover(target: &str) -> Result<Option<String>> {
97 let _cwd = std::env::current_dir()?;
98 let mut cwd_opt = Some(_cwd.as_path());
99
100 while let Some(cwd) = cwd_opt {
101 for f in std::fs::read_dir(cwd)
102 .with_context(|| format!("Error reading the directory with path: {}", cwd.display()))?
103 {
104 let p = f
105 .with_context(|| {
106 format!("Error reading the directory with path: {}", cwd.display())
107 })?
108 .path();
109 if let Some(filename) = p.file_name() {
110 if filename.to_str() == Some(target) {
111 return Ok(Some(cwd.to_string_lossy().to_string()));
112 }
113 }
114 }
115
116 cwd_opt = cwd.parent();
117 }
118
119 Ok(None)
120}
121
122fn show_howto() {
123 let markdown_input = load_template!("/src/howto.md");
124
125 let skin = MadSkin::default();
127
128 skin.print_text(markdown_input);
130}