swagger/scan/print.rs
1use super::*;
2use comfy_table::modifiers::UTF8_ROUND_CORNERS;
3use comfy_table::presets::UTF8_FULL;
4use comfy_table::*;
5pub const LEFT_PAD: usize = 40;
6pub const TBL_LEN: usize = 190;
7pub const URL_LEN: usize = 75;
8pub fn print_active_alerts(checks: Vec<ActiveChecks>) {
9 let mut table = Table::new();
10 table
11 .load_preset(UTF8_FULL)
12 .apply_modifier(UTF8_ROUND_CORNERS)
13 .set_content_arrangement(ContentArrangement::Dynamic)
14 .set_header(vec!["Check", "Top Severity", "Alerts"]);
15 for check in checks {
16 table.add_row(vec![
17 Cell::new(check.name()).add_attribute(Attribute::Bold),
18 check.top_severity().printable(),
19 check.alerts_text(),
20 ]);
21 }
22 println!("{table}");
23}
24pub fn print_active_alerts_verbose(checks: Vec<ActiveChecks>) {
25 let mut table = Table::new();
26 table
27 .load_preset(UTF8_FULL)
28 .apply_modifier(UTF8_ROUND_CORNERS)
29 .set_content_arrangement(ContentArrangement::Dynamic)
30 .set_header(vec![
31 "Check",
32 "Severity",
33 "Description",
34 "Location",
35 "Certainty",
36 ]);
37 for check in checks {
38 for alert in check.inner() {
39 table.add_row(vec![
40 Cell::new(check.name()).add_attribute(Attribute::Bold),
41 alert.level.printable(),
42 Cell::new(alert.description).add_attribute(Attribute::Bold),
43 Cell::new(alert.location).add_attribute(Attribute::Bold),
44 alert.certainty.printable(),
45 ]);
46 }
47 }
48 println!("{table}");
49}
50pub fn print_passive_alerts(checks: Vec<PassiveChecks>) {
51 let mut table = Table::new();
52 table
53 .load_preset(UTF8_FULL)
54 .apply_modifier(UTF8_ROUND_CORNERS)
55 .set_content_arrangement(ContentArrangement::Dynamic)
56 .set_header(vec!["Check", "Top Severity", "Alerts", "Description"]);
57 for check in checks {
58 table.add_row(vec![
59 Cell::new(check.name()).add_attribute(Attribute::Bold),
60 check.top_severity().printable(),
61 check.alerts_text(),
62 Cell::new(check.description()).add_attribute(Attribute::Bold),
63 ]);
64 }
65 println!("{table}");
66}
67pub fn print_passive_alerts_verbose(checks: Vec<PassiveChecks>) {
68 let mut table = Table::new();
69 table
70 .load_preset(UTF8_FULL)
71 .apply_modifier(UTF8_ROUND_CORNERS)
72 .set_content_arrangement(ContentArrangement::Dynamic)
73 .set_header(vec!["Check", "Severity", "Description", "Location"]);
74 for check in checks {
75 for alert in check.inner() {
76 table.add_row(vec![
77 Cell::new(check.name()).add_attribute(Attribute::Bold),
78 alert.level.printable(),
79 Cell::new(alert.description).add_attribute(Attribute::Bold),
80 Cell::new(trim_location(alert.location)).add_attribute(Attribute::Bold),
81 ]);
82 }
83 }
84 println!("{table}");
85}
86fn prep_param(param: &ParamForTable) -> (String, String, String, String, String, String, String) {
87 let max = if let Some(m) = param.max {
88 m.to_string()
89 } else {
90 "NULL".to_string()
91 };
92 let min = if let Some(m) = param.min {
93 m.to_string()
94 } else {
95 "NULL".to_string()
96 };
97 let mut statuses = String::new();
98 let mut dms = String::new();
99 let mut eps = String::new();
100 let mut parents = String::new();
101 let mut children = String::new();
102 for status in ¶m.statuses {
103 statuses.push_str(status);
104 statuses.push('\n');
105 }
106 for dm in ¶m.dms {
107 dms.push_str(&format!("{:?}", dm));
108 dms.push('\n');
109 }
110 for ep in ¶m.eps {
111 eps.push_str(ep);
112 eps.push('\n');
113 }
114 for parent in ¶m.parents {
115 parents.push_str(parent);
116 parents.push('\n');
117 }
118 for child in ¶m.children {
119 children.push_str(child);
120 children.push('\n');
121 }
122 (min, max, statuses, dms, eps, parents, children)
123}
124// NOT YET USABLE SINCE THERE IS NO WAY FOR MULTICOLORING ONE CELL
125pub fn print_param_table(params: &Vec<ParamForTable>) {
126 let mut table = Table::new();
127 table
128 .load_preset(UTF8_FULL)
129 .apply_modifier(UTF8_ROUND_CORNERS)
130 .set_content_arrangement(ContentArrangement::Dynamic)
131 .set_header(vec![
132 "Name",
133 "Type",
134 "Statuses",
135 "Delivery Methods",
136 "Endpoints",
137 "Parents",
138 "Children",
139 "Min-Max",
140 ]);
141 for param in params {
142 let (min, max, statuses, dms, eps, parents, children) = prep_param(param);
143 table.add_row(vec![
144 Cell::new(param.name.clone()).add_attribute(Attribute::Bold),
145 Cell::new(param.param_type.clone()).add_attribute(Attribute::Bold),
146 Cell::new(statuses).add_attribute(Attribute::Bold),
147 Cell::new(dms).add_attribute(Attribute::Bold),
148 Cell::new(eps).add_attribute(Attribute::Bold),
149 Cell::new(parents).add_attribute(Attribute::Bold),
150 Cell::new(children).add_attribute(Attribute::Bold),
151 Cell::new(format!("{}-{}", min, max)).add_attribute(Attribute::Bold),
152 ]);
153 }
154 println!("{table}");
155}
156fn prep_ep(eps: &EpForTable) -> (String, String, String, String, String, String) {
157 let mut q_p = String::new();
158 let mut h_p = String::new();
159 let mut r_b_p = String::new();
160 let mut r_p = String::new();
161 let mut statuses = String::new();
162 let mut methods = String::new();
163 for method in &eps.ops {
164 methods.push_str(&method.to_string());
165 methods.push('\n');
166 }
167 for status in &eps.statuses {
168 statuses.push_str(status);
169 statuses.push('\n');
170 }
171 for p in &eps.query_params {
172 q_p.push_str(&p.to_string());
173 q_p.push('\n');
174 }
175 for p in &eps.headers_params {
176 h_p.push_str(p);
177 h_p.push('\n');
178 }
179 for p in &eps.req_body_params {
180 r_b_p.push_str(p);
181 r_b_p.push('\n');
182 }
183 for p in &eps.res_params {
184 r_p.push_str(p);
185 r_p.push('\n');
186 }
187 (methods, q_p, h_p, r_b_p, r_p, statuses)
188}
189// NOT YET USABLE SINCE THERE IS NO WAY FOR MULTICOLORING ONE CELL
190pub fn print_ep_table(eps: &Vec<EpForTable>) {
191 let mut table = Table::new();
192 table
193 .load_preset(UTF8_FULL)
194 .apply_modifier(UTF8_ROUND_CORNERS)
195 .set_content_arrangement(ContentArrangement::Dynamic)
196 .set_header(vec![
197 "Path",
198 "Methods",
199 "Query Params",
200 "Header Params",
201 "Body Params",
202 "Response Params",
203 "Statuses",
204 ]);
205 for ep in eps {
206 let (methods, q_p, h_p, r_b_p, r_p, statuses) = prep_ep(ep);
207 table.add_row(vec![
208 Cell::new(ep.path.clone()).add_attribute(Attribute::Bold),
209 Cell::new(methods).add_attribute(Attribute::Bold),
210 Cell::new(q_p).add_attribute(Attribute::Bold),
211 Cell::new(h_p).add_attribute(Attribute::Bold),
212 Cell::new(r_b_p).add_attribute(Attribute::Bold),
213 Cell::new(r_p).add_attribute(Attribute::Bold),
214 Cell::new(statuses).add_attribute(Attribute::Bold),
215 ]);
216 }
217 println!("{table}");
218}
219fn trim_location(loc: String) -> String {
220 loc.replace("swagger root", "")
221 .replace("swagger rooot", "")
222 .replace("swagger", "")
223 .replace("media type:application/json", "")
224 .replace("response status", "status")
225}
226impl Level {
227 pub fn printable(&self) -> Cell {
228 match self {
229 Self::Info => Cell::new("INFO")
230 .fg(Color::Blue)
231 .add_attribute(Attribute::Bold),
232 Self::Low => Cell::new("LOW")
233 .fg(Color::Yellow)
234 .add_attribute(Attribute::Bold),
235 Self::Medium => Cell::new("MEDIUM")
236 .fg(Color::Rgb {
237 r: 255,
238 g: 167,
239 b: 38,
240 })
241 .add_attribute(Attribute::Bold),
242 Self::High => Cell::new("HIGH")
243 .fg(Color::Red)
244 .add_attribute(Attribute::Bold),
245 Self::Critical => Cell::new("CRITICAL")
246 .fg(Color::Black)
247 .add_attribute(Attribute::Bold),
248 }
249 }
250}
251impl Certainty {
252 pub fn printable(&self) -> Cell {
253 match self {
254 Self::Low => Cell::new("LOW")
255 .fg(Color::DarkGrey)
256 .add_attribute(Attribute::Bold),
257 Self::Medium => Cell::new("MEDIUM")
258 .fg(Color::DarkGrey)
259 .add_attribute(Attribute::Bold),
260 Self::High => Cell::new("HIGH")
261 .fg(Color::DarkGrey)
262 .add_attribute(Attribute::Bold),
263 Self::Certain => Cell::new("CERTAIN")
264 .fg(Color::DarkGrey)
265 .add_attribute(Attribute::Bold),
266 Self::Passive => Cell::new(""),
267 }
268 }
269}
270/*
271//pub const LEFT_PAD: usize = 40;
272//pub const TBL_LEN: usize = 190;
273//pub const URL_LEN: usize = 75;
274pub fn print_checks_table<T>(checks: &[T])
275where T:fmt::Display+Check{
276 println!(
277 "{:pad$}| RESULT | TOP SEVERITY | ALERTS |DESCRIPTION\n{:-<table_len$}",
278 "CHECK",
279 "",
280 pad = LEFT_PAD,
281 table_len = TBL_LEN
282 );
283 for check in checks {
284 println!("{}", check);
285 }
286}
287pub fn print_failed_checks_table<T>(checks: &[T])
288where T:fmt::Display+Check{
289 println!(
290 "{:pad$}| RESULT | TOP SEVERITY | ALERTS |DESCRIPTION\n{:-<table_len$}",
291 "CHECK",
292 "",
293 pad = LEFT_PAD,
294 table_len = TBL_LEN
295 );
296 for check in checks {
297 if check.result() == "FAILED" {
298 println!("{}", check);
299 }
300 }
301}
302fn split_text_to_lines(string:&str)->Vec<String>{
303 let mut new_vec = vec![];
304 let mut new_str = String::new();
305 let line_len = 75;
306 let mut c = 0;
307 for t in string.split(' '){
308 if !t.trim().is_empty(){
309 c+=t.len()+1;
310 if c>line_len{
311 c = t.len();
312 new_str.pop();
313 new_vec.push(new_str);
314 new_str = format!(" {}",t);
315 }else{
316 new_str.push_str(&format!("{} ",t.trim()));
317 }
318 }
319 }
320 new_vec.push(new_str);
321 new_vec
322}
323pub fn print_alerts_table(checks: &[PassiveChecks]) {
324 println!(
325 "{:pad$}| LEVEL |{:75}|DESCRIPTION\n{:-<table_len$}",
326 "CHECK",
327 "LOCATION",
328 pad = 30,
329 table_len = TBL_LEN
330 );
331 for check in checks {
332 if check.result() == "FAILED" {
333 for alert in check.inner() {
334 println!("{:pad$}|{}", check.name().cyan().bold(), alert, pad = 30)
335 }
336 }
337 }
338}
339
340pub fn print_attack_alerts_table(checks: &[ActiveChecks]) {
341 println!(
342 "{:pad$}| SEVERITY | CERTAINTY |{:thing$}|DESCRIPTION\n{:-<table_len$}",
343 "CHECK",
344 "LOCATION",
345 "",
346 table_len = TBL_LEN,
347 pad = 30,
348 thing = URL_LEN
349 );
350 for check in checks {
351 if check.result() == "FAILED" {
352 for _ in check.inner() {
353 // println!("{:pad$}|{}", check.name().cyan().bold(), alert, pad = 30)
354 println!("{}",serde_json::to_string(&check).unwrap());
355 }
356 }
357 }
358}
359
360impl fmt::Display for PassiveChecks {
361 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362 if self.result() == "PASSED" {
363 write!(
364 f,
365 "{:pad$}| {} | {:8} | {:5} |{}\n{:-<table_len$}",
366 self.name().bold(),
367 self.result().green().bold().underline(),
368 "NONE".blue().bold(),
369 self.alerts_text(),
370 self.description(),
371 "",
372 pad = LEFT_PAD,
373 table_len = TBL_LEN
374 )
375 } else if self.result() == "FAILED" {
376 write!(
377 f,
378 "{:pad$}| {} | {} | {:5} |{}\n{:-<table_len$}",
379 self.name().bold(),
380 self.result().red().bold().underline(),
381 self.top_severity(),
382 self.alerts_text(),
383 self.description(),
384 "",
385 pad = LEFT_PAD,
386 table_len = TBL_LEN
387 )
388 } else {
389 write!(f, "")
390 }
391 }
392}
393impl fmt::Display for ActiveChecks {
394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395 if self.result() == "PASSED" {
396 write!(
397 f,
398 "{:pad$}| {} | {:8} | {:5} |{}\n{:-<table_len$}",
399 self.name().bold(),
400 self.result().green().bold().underline(),
401 "NONE".blue().bold(),
402 self.alerts_text(),
403 self.description(),
404 "",
405 pad = LEFT_PAD,
406 table_len = TBL_LEN
407 )
408 } else if self.result() == "FAILED" {
409 write!(
410 f,
411 "{:pad$}| {} | {} | {:5} |{}\n{:-<table_len$}",
412 self.name().bold(),
413 self.result().red().bold().underline(),
414 self.top_severity(),
415 self.alerts_text(),
416 self.description(),
417 "",
418 pad = LEFT_PAD,
419 table_len = TBL_LEN
420 )
421 } else {
422 write!(f, "")
423 }
424 }
425}
426impl fmt::Display for Alert {
427 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428 if self.certainty==Certainty::Passive{
429 let location = self
430 .location
431 .replace("swagger root", "")
432 .replace("swagger rooot", "")
433 .replace("swagger", "")
434 .replace("media type:application/json", "")
435 .replace("response status", "status");
436 let mut string = String::new();
437 let location = split_text_to_lines(&location);
438 string.push_str(&format!(
439 " {:10}|{:75}| {}\n",
440 self.level,
441 location[0].bright_magenta().bold(),
442 self.description.bright_red().bold(),
443 ));
444 for loc in location.iter().skip(1){
445 string.push_str(&format!(
446 "{:30}|{:9}|{:75}| {}\n",
447 "",
448 "",
449 loc.bright_magenta().bold(),
450 ""
451 ));
452 }
453 string.push_str(&format!("\n{:-<190}",""));
454 write!(f,"{}",string)
455 }else{
456 /*write!(
457 f,
458 " {}| {} |{:thing$}| {:}\n{:-<table_len$}",
459 self.level,
460 self.certainty,
461 self.location.bright_magenta().bold(),
462 self.description.bright_red().bold(),
463 "",
464 thing=URL_LEN,
465 table_len = TBL_LEN
466 )*/
467 write!(f,"")
468 }
469 }
470}
471impl fmt::Display for Level {
472 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473 match self {
474 Self::Info => write!(f, "{:8}", "INFO".blue().bold()),
475 Self::Low => write!(f, "{:8}", "LOW".yellow().bold()),
476 Self::Medium => write!(f, "{:8}", "MEDIUM".truecolor(255, 167, 38).bold()),
477 Self::High => write!(f, "{:8}", "HIGH".red().bold()),
478 Self::Critical => write!(f, "{:8}", "CRITICAL".red().bold().blink()),
479 }
480 }
481}
482impl fmt::Display for Certainty {
483 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
484 match self {
485 Self::Low => write!(f, "{:8}", "LOW".bright_black().bold()),
486 Self::Medium => write!(f, "{:8}", "MEDIUM".bright_black()/*.truecolor(255, 167, 38)*/.bold()),
487 Self::High => write!(f, "{:8}", "HIGH".bright_black().bold()),
488 Self::Certain => write!(f, "{:8}", "CERTAIN".bright_black().bold()),
489 Self::Passive=> write!(f, ""),
490 }
491 }
492}
493*/