1pub mod config;
2mod info;
3mod scan;
4use crate::config::Verbosity;
5use crate::info::eps::EpTable;
6use crate::info::params::ParamTable;
7use crate::scan::active::active_scanner;
8use crate::scan::active::http_client::auth::Authorization;
9use cherrybomb_oas::legacy::legacy_oas::*;
10use config::Config;
11use scan::checks::{ActiveChecks, PassiveChecks};
12use scan::passive::passive_scanner;
13use scan::*;
14use serde_json::{json, Value};
15use std::collections::{HashMap, HashSet};
16use std::vec;
17use strum::IntoEnumIterator;
18use serde_yaml;
19use anyhow::anyhow;
20
21fn verbose_print(config: &Config, required: Option<Verbosity>, message: &str) {
22 let required = required.unwrap_or(Verbosity::Normal);
23 if config.verbosity >= required {
24 println!("{message}");
25 }
26}
27
28pub async fn run(config: &mut Config) -> anyhow::Result<Value> {
29 verbose_print(config, None, "Starting Cherrybomb...");
30 verbose_print(config, None, "Opening OAS file...");
31
32 let (oas,oas_json) = if let Some(ext)= config.file.extension(){
33 verbose_print(config, None, "Reading OAS file...");
34 let oas_file = match std::fs::read_to_string(&config.file) {
35 Ok(file) => file,
36 Err(e) => return Err(anyhow::Error::msg(format!("Error opening OAS file: {}", e))),
37 };
38 let oas_json: Value = match ext.to_str() {
39 Some("json") => {
40 verbose_print(config, None, "Parsing OAS file...");
41 match serde_json::from_str(&oas_file) {
42 Ok(json) => json,
43 Err(e) => return Err(anyhow::Error::msg(format!("Error parsing OAS file: {}", e))),
44 }
45 }
46 Some("yaml") | Some("yml") => {
47 verbose_print(config, None, "Parsing OAS file...");
48 match serde_yaml::from_str(&oas_file) {
49 Ok(yaml) => yaml,
50 Err(e) => return Err(anyhow::Error::msg(format!("Error parsing OAS file: {}", e))),
51 }
52 }
53 _ => return Err(anyhow::Error::msg("Unsupported config file extension")),
54 };
55 let oas: OAS3_1 = match serde_json::from_value(oas_json.clone().into()) {
56 Ok(oas) => oas,
57 Err(e) => return Err(anyhow::Error::msg(format!("Error creating OAS struct: {}", e))),
58 };
59 (oas,oas_json)
60 }else {
61 return Err(anyhow!("Misconfigured file extention"));
62 };
63 match config.profile {
64 config::Profile::Info => run_profile_info(&config, &oas, &oas_json),
65 config::Profile::Normal => run_normal_profile(config, &oas, &oas_json).await,
66 config::Profile::Active => {
67 if !&config.passive_include.is_empty() {
68 config.passive_checks = config.passive_include.clone(); run_normal_profile(config, &oas, &oas_json).await
70 } else {
71 run_active_profile(config, &oas, &oas_json).await
72 }
73 }
74
75 config::Profile::Passive => {
76 if !&config.active_include.is_empty() {
77 config.active_checks = config.active_include.clone();
78 run_normal_profile(config, &oas, &oas_json).await
79 } else {
80 run_passive_profile(config, &oas, &oas_json)
81 }
82 }
83 config::Profile::Full => run_full_profile(config, &oas, &oas_json).await,
84 config::Profile::OWASP => todo!("not implemented yet!"),
85 }
86}
87
88fn run_profile_info(config: &Config, oas: &OAS3_1, oas_json: &Value) -> anyhow::Result<Value> {
89 verbose_print(config, None, "Creating param list...");
91 let param_scan = ParamTable::new::<OAS3_1>(oas_json);
92 let param_result: HashMap<&str, Value> = param_scan
93 .params
94 .iter()
95 .map(|param| (param.name.as_str(), json!(param)))
96 .collect();
97
98 verbose_print(config, None, "Create endpoint list");
100 let ep_table = EpTable::new::<OAS3_1>(oas_json);
101 let endpoint_result: HashMap<&str, Value> = ep_table
102 .eps
103 .iter()
104 .map(|param| (param.path.as_str(), json!(param)))
105 .collect();
106
107 verbose_print(config, None, "Creating report...");
108 let report = json!({
109
110 "params": param_result,
111 "endpoints": endpoint_result,
112 });
113 Ok(report)
114}
115
116async fn run_active_profile(
117 config: &mut Config,
118 oas: &OAS3_1,
119 oas_json: &Value,
120) -> anyhow::Result<Value> {
121 verbose_print(
123 config,
124 Some(Verbosity::Debug),
125 "Creating active scan struct...",
126 );
127 let mut active_scan = match active_scanner::ActiveScan::new(oas.clone(), oas_json.clone()) {
128 Ok(scan) => scan,
129 Err(e) => {
130 return Err(anyhow::anyhow!("Error creating active scan struct: {}", e));
131 }
132 };
133
134 verbose_print(config, None, "Running active scan...");
136 let temp_auth = config.get_auth();
137 let active_result: HashMap<&str, Vec<Alert>> = match config.active_checks.is_empty() {
138 true => {
139 let all_active_checks = ActiveChecks::iter().map(|x| x.name().to_string()).collect();
143 config.update_checks_active(all_active_checks);
144
145 let active_checks = ActiveChecks::create_checks(&config.active_checks);
147
148 active_scan
149 .run(
150 active_scanner::ActiveScanType::Partial(active_checks),
151 &temp_auth,
152 )
153 .await;
154 active_scan
155 .checks
156 .iter()
157 .map(|check| (check.name(), check.inner()))
158 .collect()
159 }
160 false => {
161 let active_checks_to_run = ActiveChecks::create_checks(&config.active_checks.clone()); active_scan
168 .run(
169 active_scanner::ActiveScanType::Partial(active_checks_to_run),
170 &temp_auth,
171 )
172 .await;
173 active_scan
174 .checks
175 .iter()
176 .map(|check| (check.name(), check.inner()))
177 .collect()
178 }
179 };
180
181 Ok(json!({ "active": active_result }))
182}
183
184fn run_passive_profile(
185 config: &mut Config,
186 oas: &OAS3_1,
187 oas_json: &Value,
188) -> anyhow::Result<Value> {
189 verbose_print(
190 config,
191 Some(Verbosity::Debug),
192 "Creating passive scan struct...",
193 );
194 let mut passive_scan = passive_scanner::PassiveSwaggerScan {
196 swagger: oas.clone(),
197 swagger_value: oas_json.clone(),
198 passive_checks: vec![],
199 verbosity: 0,
200 };
201
202 verbose_print(config, None, "Running passive scan...");
204
205 let passive_result: HashMap<&str, Vec<Alert>> = match config.passive_checks.is_empty() {
206 true => {
207 let all_passive_checks = PassiveChecks::iter()
209 .map(|x| x.name().to_string())
210 .collect(); config.update_checks_passive(all_passive_checks);
212
213 let passive_checks_to_run = PassiveChecks::create_checks(&config.passive_checks);
220 passive_scan.run(passive_scanner::PassiveScanType::Partial(
221 passive_checks_to_run,
222 ));
223 passive_scan
224 .passive_checks
225 .iter()
226 .map(|check| (check.name(), check.inner()))
227 .collect()
228 }
229 false => {
230 let passive_checks_to_run = PassiveChecks::create_checks(&config.passive_checks);
237 passive_scan.run(passive_scanner::PassiveScanType::Partial(
238 passive_checks_to_run,
239 ));
240 passive_scan
241 .passive_checks
242 .iter()
243 .map(|check| (check.name(), check.inner()))
244 .collect()
245 }
246 };
247 Ok(json!({"passive": passive_result}))
248}
249
250async fn run_normal_profile(
251 config: &mut Config,
252 oas: &OAS3_1,
253 oas_json: &Value,
254) -> anyhow::Result<Value> {
255 let mut report = json!({});
256 let mut results = HashMap::from([
257 ("passive", run_passive_profile(config, oas, oas_json)),
258 ("active", run_active_profile(config, oas, oas_json).await),
259 ]);
260 for (key, value) in results.iter_mut() {
261 match value {
262 Ok(result) => {
263 if let Some(val) = result.get(key) {
264 report[key] = val.clone();
265 }
266 }
267 Err(e) => {
268 verbose_print(
269 config,
270 None,
271 &format!("WARNING: Error running {key} scan: {e}"),
272 );
273 }
274 }
275 }
276 Ok(report)
277}
278
279async fn run_full_profile(
280 config: &mut Config,
281 oas: &OAS3_1,
282 oas_json: &Value,
283) -> anyhow::Result<Value> {
284 let mut report = json!({});
285 let mut results = HashMap::from([
286 ("active", run_active_profile(config, oas, oas_json).await),
287 ("passive", run_passive_profile(config, oas, oas_json)),
288 ("params", run_profile_info(config, oas, oas_json)),
289 ("endpoints", run_profile_info(config, oas, oas_json)),
290 ]);
291 for (key, value) in results.iter_mut() {
292 match value {
293 Ok(result) => {
294 if let Some(val) = result.get(key) {
295 report[key] = val.clone();
296 }
297 }
298 Err(e) => {
299 verbose_print(
300 config,
301 None,
302 &format!("WARNING: Error running {} scan: {}", key, e),
303 );
304 }
305 }
306 }
307 Ok(report)
308}