1use fenir::cpe::{create_cpe_from_type, Cpe, CPE_ALL_LABEL, CPE_OS_LABEL, CPE_SOFT_LABEL};
2use fenir::database::execute_query;
3use fenir::facilities::Cleaning;
4use fenir::facilities::Uppercase;
5use fenir::package::errors::write_error_message_and_exit;
6use fenir::query::QueryType::ByCpeMatch;
7use fenir::query::{build_query, QueryType};
8use fenir::{header, section, section_level};
9use serde_json::Value;
10use termint::enums::Color;
11use termint::enums::Modifier;
12use termint::widgets::ToSpan;
13
14pub fn explain_cpe(cpe_str_id: &str) {
27 let cpe = Cpe::from(cpe_str_id);
28 println!("{}", header!("CPE"));
29 section_level!(2, "Explanation");
30 cpe.explains();
31}
32
33pub fn search_with_keywords(criteria: Vec<String>, mode: QueryType) -> Vec<Cpe> {
57 let words = criteria.join(" ");
58 let result = execute_query(build_query(mode, words.as_str()));
59 collect_result(result)
60}
61
62const INVALID_APPS_TYPE_MSG: &str = "Invalid apps type";
64
65const CPE_NOT_MATCHED_MSG: &str = "CPE not matched";
67
68pub fn create_cpe(args: Vec<String>) -> Cpe {
111 let type_option = args
112 .iter()
113 .position(|a| a == "--type" || a == "-t")
114 .and_then(|pos| args.get(pos + 1));
115
116 let effective_type = match type_option {
117 Some(value) => value.as_str(),
118 _ => "*",
119 };
120
121 validate_apps_type(effective_type);
122
123 let mut cpe = create_cpe_from_type(effective_type);
124 apply_cpe_options(&mut cpe, &args);
125 cpe
126}
127
128fn validate_apps_type(apps_type: &str) {
155 if ![CPE_ALL_LABEL, CPE_SOFT_LABEL, CPE_OS_LABEL].contains(&apps_type) {
156 write_error_message_and_exit(INVALID_APPS_TYPE_MSG, Some(apps_type));
157 }
158}
159
160fn apply_cpe_options(cpe: &mut Cpe, args: &[String]) {
177 cpe.vendor(&get_cpe_option_value("--vendor", "-V", args));
178 cpe.product(&get_cpe_option_value("--product", "-p", args));
179 cpe.version(&get_cpe_option_value("--version", "-v", args));
180 cpe.update(&get_cpe_option_value("--update", "-u", args));
181 cpe.edition(&get_cpe_option_value("--edition", "-e", args));
182 cpe.language(&get_cpe_option_value("--lang", "-l", args));
183 cpe.sw_edition(&get_cpe_option_value("--sw-edition", "-s", args));
184 cpe.target_hw(&get_cpe_option_value("--target-hw", "-h", args));
185 cpe.target_sw(&get_cpe_option_value("--target-sw", "-S", args));
186 cpe.other(&get_cpe_option_value("--other", "-o", args));
187}
188
189pub fn match_cpe(cpe: Cpe) -> Vec<Cpe> {
217 let result = execute_query(build_query(ByCpeMatch, cpe.name().as_str()));
218 if result.is_null() {
219 return vec![];
220 }
221
222 collect_result(result)
223}
224
225fn collect_result(result: Value) -> Vec<Cpe> {
226 if let Some(products_list) = result["products"].as_array() {
227 let mut cpe_list: Vec<Cpe> = products_list
228 .iter()
229 .map(|product| {
230 let raw = String::cleaning(product["cpe"]["cpeName"].to_string());
231 Cpe::from(raw.as_str())
232 })
233 .collect();
234 cpe_list.dedup();
235 cpe_list
236 } else {
237 Vec::new()
238 }
239}
240
241pub fn show_cpe_values(cpe_list: &[Cpe]) {
258 if cpe_list.is_empty() {
259 write_error_message_and_exit(CPE_NOT_MATCHED_MSG, None);
260 }
261
262 cpe_list.iter().for_each(|cpe| {
263 section_level!("CPE");
264 cpe.explains();
265 println!();
266 });
267}
268
269fn get_cpe_option_value(name: &str, short_arg: &str, args: &[String]) -> String {
286 if let Some(option) = args.iter().position(|a| a == name || a == short_arg) {
287 if let Some(option_value) = args.get(option + 1) {
288 if option_value.len() > 1 && option_value.starts_with('-') {
289 eprintln!("Invalid option value for: {}. Replace it by: '*'.", name);
290 String::from("*")
291 } else {
292 option_value.to_string()
293 }
294 } else {
295 String::from("*")
296 }
297 } else {
298 String::from("*")
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use fenir::cpe::NVD_CPE_LINK;
305 use fenir::cpe::{Cpe, CpeType};
306 use fenir::database::execute_query;
307 use fenir::query::build_query;
308 use fenir::query::QueryType::{ByCpe, ByCpeSearchWords};
309 use serde_json::Value::Null;
310 use std::thread::sleep;
311 use std::time::Duration;
312
313 fn setup() {
314 let waiting = Duration::from_millis(30);
315 sleep(waiting);
316 }
317
318 #[test]
319 fn cpe_new_default() {
320 for t in [
321 CpeType::Application,
322 CpeType::All,
323 CpeType::OperatingSystem,
324 CpeType::Hardware,
325 ] {
326 let result = Cpe::new(t.clone());
327 let ref_value = Cpe {
328 cpe_type: t,
329 vendor: "*".to_string(),
330 product: "*".to_string(),
331 version: "*".to_string(),
332 update: "*".to_string(),
333 edition: "*".to_string(),
334 language: "*".to_string(),
335 sw_edition: "*".to_string(),
336 target_hw: "*".to_string(),
337 target_sw: "*".to_string(),
338 other: "*".to_string(),
339 };
340
341 assert_eq!(ref_value, result);
342 }
343 }
344
345 #[test]
346 fn cpe_new_application_representation() {
347 let result = Cpe::new(CpeType::Application);
348 let representation = format!("{}", result);
349
350 assert_eq!("cpe:2.3:a:*:*:*:*:*:*:*:*:*:*", representation);
351 }
352
353 #[test]
354 fn cpe_new_os_representation() {
355 let result = Cpe::new(CpeType::OperatingSystem);
356 let representation = format!("{}", result);
357
358 assert_eq!("cpe:2.3:o:*:*:*:*:*:*:*:*:*:*", representation);
359 }
360
361 #[test]
362 fn cpe_new_hardware_representation() {
363 let result = Cpe::new(CpeType::Hardware);
364 let representation = format!("{}", result);
365
366 assert_eq!("cpe:2.3:h:*:*:*:*:*:*:*:*:*:*", representation);
367 }
368
369 #[test]
370 fn cpe_new_application_name_valid() {
371 let result = Cpe::new(CpeType::Application);
372
373 assert_eq!("cpe:2.3:a:*:*:*:*:*:*:*:*:*:*", result.name());
374 }
375
376 #[test]
377 fn cpe_os_name_valid() {
378 let result = Cpe::new(CpeType::OperatingSystem);
379
380 assert_eq!("cpe:2.3:o:*:*:*:*:*:*:*:*:*:*", result.name());
381 }
382
383 #[test]
384 fn cpe_vendor_name_valid() {
385 for s in ["microsoft", "MICROSOFT"] {
386 let mut result = Cpe::new(CpeType::OperatingSystem);
387 result.vendor(s);
388
389 assert_eq!("cpe:2.3:o:microsoft:*:*:*:*:*:*:*:*:*", result.name());
390 }
391 }
392
393 #[test]
394 fn cpe_product_name_valid() {
395 for s in ["ntp", "NTP"] {
396 let mut result = Cpe::new(CpeType::Application);
397 result.product(s);
398
399 assert_eq!("cpe:2.3:a:*:ntp:*:*:*:*:*:*:*:*", result.name());
400 }
401 }
402
403 #[test]
404 fn cpe_version_value_valid() {
405 for s in ["2.b5", "2.B5"] {
406 let mut result = Cpe::new(CpeType::Application);
407 result.version(s);
408
409 assert_eq!("cpe:2.3:a:*:*:2.b5:*:*:*:*:*:*:*", result.name());
410 }
411 }
412
413 #[test]
414 fn cpe_update_value_valid() {
415 for s in ["sp2", "SP2"] {
416 let mut result = Cpe::new(CpeType::Application);
417 result.update(s);
418
419 assert_eq!("cpe:2.3:a:*:*:*:sp2:*:*:*:*:*:*", result.name());
420 }
421 }
422
423 #[test]
424 fn cpe_edition_value_valid() {
425 for s in ["beta", "BETA"] {
426 let mut result = Cpe::new(CpeType::Application);
427 result.edition(s);
428
429 assert_eq!("cpe:2.3:a:*:*:*:*:beta:*:*:*:*:*", result.name());
430 }
431 }
432
433 #[test]
434 fn cpe_language_value_valid() {
435 for s in ["alpha", "ALPHA"] {
436 let mut result = Cpe::new(CpeType::Application);
437 result.language(s);
438
439 assert_eq!("cpe:2.3:a:*:*:*:*:*:alpha:*:*:*:*", result.name());
440 }
441 }
442
443 #[test]
444 fn cpe_sw_edition_value_valid() {
445 for s in ["Ed1", "ED1"] {
446 let mut result = Cpe::new(CpeType::Application);
447 result.sw_edition(s);
448
449 assert_eq!("cpe:2.3:a:*:*:*:*:*:*:ed1:*:*:*", result.name());
450 }
451 }
452
453 #[test]
454 fn cpe_target_sw_valid() {
455 for s in ["target_sw", "TARGET_SW"] {
456 let mut result = Cpe::new(CpeType::Application);
457 result.target_sw(s);
458
459 assert_eq!("cpe:2.3:a:*:*:*:*:*:*:*:target_sw:*:*", result.name());
460 }
461 }
462
463 #[test]
464 fn cpe_target_hw_valid() {
465 for s in ["target_hw", "TARGET_HW"] {
466 let mut result = Cpe::new(CpeType::Application);
467 result.target_hw(s);
468
469 assert_eq!("cpe:2.3:a:*:*:*:*:*:*:*:*:target_hw:*", result.name());
470 }
471 }
472
473 #[test]
474 fn cpe_other_valid() {
475 for s in ["other", "OTHER"] {
476 let mut result = Cpe::new(CpeType::Application);
477 result.other(s);
478
479 assert_eq!("cpe:2.3:a:*:*:*:*:*:*:*:*:*:other", result.name());
480 }
481 }
482
483 #[test]
484 fn cpe_query_cpe_name() {
485 setup();
486
487 let mut cpe = Cpe::new(CpeType::Application);
488 cpe.vendor("debian");
489 cpe.product("debian_linux");
490
491 let result = build_query(ByCpe, cpe.name().as_str());
492
493 assert_eq!(format!("{NVD_CPE_LINK}?cpeName={}", cpe), result.unwrap());
494 }
495
496 #[test]
497 fn cpe_query_execute_valid() {
498 setup();
499
500 let cpe = create_cpe_test();
501
502 let result = execute_query(build_query(ByCpe, cpe.name().as_str()));
503
504 assert_ne!(0, result["resultsPerPage"]);
505 }
506
507 fn create_cpe_test() -> Cpe {
508 let mut cpe = Cpe::new(CpeType::OperatingSystem);
509 cpe.vendor("microsoft");
510 cpe.product("windows_10");
511 cpe.version("1607");
512 cpe
513 }
514
515 #[test]
516 fn cpe_query_bad_values_invalid() {
517 setup();
518
519 let mut cpe = Cpe::new(CpeType::Application);
520 cpe.product("foo");
521 cpe.vendor("bar");
522
523 let result = execute_query(build_query(ByCpe, cpe.name().as_str()));
524
525 assert_eq!(Null, result["resultsPerPage"]);
526 }
527
528 #[test]
529 fn cpe_parse_from_string_valid() {
530 let cpe_ref = create_cpe_test();
531 let cpe_result = Cpe::from(cpe_ref.name().as_str());
532
533 assert_eq!(cpe_ref, cpe_result);
534 }
535
536 #[test]
537 fn cpe_parse_from_simple_string_valid() {
538 let mut result = Cpe::new(CpeType::Application);
539 result.edition("BETA");
540 let cpe_result = Cpe::from(result.name().as_str());
541
542 assert_eq!(result, cpe_result);
543 }
544
545 #[test]
546 fn cpe_search_with_one_word_only_valid() {
547 setup();
548
549 let result = execute_query(build_query(ByCpeSearchWords, "Java"));
550
551 assert_ne!(Null, result["resultsPerPage"]);
552 }
553
554 #[test]
555 fn cpe_search_with_multiple_words_valid() {
556 setup();
557
558 let result = execute_query(build_query(ByCpeSearchWords, "Red Hat"));
559
560 assert_ne!(Null, result["resultsPerPage"]);
561 }
562
563 #[test]
564 fn cpe_search_with_invalid_words_not_valid() {
565 setup();
566
567 let result = execute_query(build_query(ByCpeSearchWords, "!!! รงรงรงรง"));
568
569 assert_eq!(Null, result["resultsPerPage"]);
570 }
571}