1use std::collections::HashMap;
2use std::fs::File;
3use std::io::BufReader;
4mod macro_vector_values;
5use macro_vector_values::build_macro_vector_values;
6
7fn build_av_levels() -> HashMap<String, f64> {
8 HashMap::from([
9 ("N".to_string(), 0.0),
10 ("A".to_string(), 0.1),
11 ("L".to_string(), 0.2),
12 ("P".to_string(), 0.3),
13 ])
14}
15
16fn build_pr_levels() -> HashMap<String, f64> {
17 HashMap::from([
18 ("N".to_string(), 0.0),
19 ("L".to_string(), 0.1),
20 ("H".to_string(), 0.2),
21 ])
22}
23
24fn build_ui_levels() -> HashMap<String, f64> {
25 HashMap::from([
26 ("N".to_string(), 0.0),
27 ("P".to_string(), 0.1),
28 ("A".to_string(), 0.2),
29 ])
30}
31
32fn build_ac_levels() -> HashMap<String, f64> {
33 HashMap::from([("L".to_string(), 0.0), ("H".to_string(), 0.1)])
34}
35
36fn build_at_levels() -> HashMap<String, f64> {
37 HashMap::from([("N".to_string(), 0.0), ("P".to_string(), 0.1)])
38}
39
40fn build_vc_levels() -> HashMap<String, f64> {
41 HashMap::from([
42 ("H".to_string(), 0.0),
43 ("L".to_string(), 0.1),
44 ("N".to_string(), 0.2),
45 ])
46}
47
48fn build_vi_levels() -> HashMap<String, f64> {
49 HashMap::from([
50 ("H".to_string(), 0.0),
51 ("L".to_string(), 0.1),
52 ("N".to_string(), 0.2),
53 ])
54}
55
56fn build_va_levels() -> HashMap<String, f64> {
57 HashMap::from([
58 ("H".to_string(), 0.0),
59 ("L".to_string(), 0.1),
60 ("N".to_string(), 0.2),
61 ])
62}
63
64fn build_sc_levels() -> HashMap<String, f64> {
65 HashMap::from([
66 ("H".to_string(), 0.1),
67 ("L".to_string(), 0.2),
68 ("N".to_string(), 0.3),
69 ])
70}
71
72fn build_si_levels() -> HashMap<String, f64> {
73 HashMap::from([
74 ("S".to_string(), 0.0),
75 ("H".to_string(), 0.1),
76 ("L".to_string(), 0.2),
77 ("N".to_string(), 0.3),
78 ])
79}
80
81fn build_sa_levels() -> HashMap<String, f64> {
82 HashMap::from([
83 ("S".to_string(), 0.0),
84 ("H".to_string(), 0.1),
85 ("L".to_string(), 0.2),
86 ("N".to_string(), 0.3),
87 ])
88}
89
90fn build_cr_levels() -> HashMap<String, f64> {
91 HashMap::from([
92 ("H".to_string(), 0.0),
93 ("M".to_string(), 0.1),
94 ("L".to_string(), 0.2),
95 ])
96}
97
98fn build_ir_levels() -> HashMap<String, f64> {
99 HashMap::from([
100 ("H".to_string(), 0.0),
101 ("M".to_string(), 0.1),
102 ("L".to_string(), 0.2),
103 ])
104}
105
106fn build_ar_levels() -> HashMap<String, f64> {
107 HashMap::from([
108 ("H".to_string(), 0.0),
109 ("M".to_string(), 0.1),
110 ("L".to_string(), 0.2),
111 ])
112}
113
114fn build_default_metric_values() -> HashMap<String, String> {
115 HashMap::from([
116 ("AV".to_string(), "N".to_string()),
117 ("AC".to_string(), "L".to_string()),
118 ("AT".to_string(), "N".to_string()),
119 ("PR".to_string(), "N".to_string()),
120 ("UI".to_string(), "N".to_string()),
121 ("VC".to_string(), "N".to_string()),
122 ("VI".to_string(), "N".to_string()),
123 ("VA".to_string(), "N".to_string()),
124 ("SC".to_string(), "N".to_string()),
125 ("SI".to_string(), "N".to_string()),
126 ("SA".to_string(), "N".to_string()),
127 ("S".to_string(), "X".to_string()),
128 ("AU".to_string(), "X".to_string()),
129 ("R".to_string(), "X".to_string()),
130 ("V".to_string(), "X".to_string()),
131 ("RE".to_string(), "X".to_string()),
132 ("U".to_string(), "X".to_string()),
133 ("MAV".to_string(), "X".to_string()),
134 ("MAC".to_string(), "X".to_string()),
135 ("MAT".to_string(), "X".to_string()),
136 ("MPR".to_string(), "X".to_string()),
137 ("MUI".to_string(), "X".to_string()),
138 ("MVC".to_string(), "X".to_string()),
139 ("MVI".to_string(), "X".to_string()),
140 ("MVA".to_string(), "X".to_string()),
141 ("MSC".to_string(), "X".to_string()),
142 ("MSI".to_string(), "X".to_string()),
143 ("MSA".to_string(), "X".to_string()),
144 ("CR".to_string(), "X".to_string()),
145 ("IR".to_string(), "X".to_string()),
146 ("AR".to_string(), "X".to_string()),
147 ("E".to_string(), "X".to_string()),
148 ])
149}
150
151fn build_full_vector(mut vector: HashMap<String, String>) -> HashMap<String, String> {
152 for (metric, value) in build_default_metric_values() {
153 if vector.contains_key(&metric) {
154 continue;
155 }
156
157 vector.insert(metric, value);
158 }
159
160 vector
161}
162
163fn preprocess_vector(vector: String) -> HashMap<String, String> {
164 let mut result: HashMap<String, String> = HashMap::new();
165 for metric_data in vector.split("/") {
166 if metric_data.contains("CVSS") {
167 continue;
168 }
169
170 let metric_and_value: Vec<&str> = metric_data.split(":").collect();
171 if metric_and_value == vec!["".to_string()] {
172 continue;
173 }
174
175 result.insert(
176 metric_and_value[0].to_string(),
177 metric_and_value[1].to_string(),
178 );
179 }
180
181 result
182}
183
184fn get_m(vector: HashMap<String, String>, metric: String) -> String {
185 if metric == "E" && vector[&metric] == "X" {
187 return "A".to_string();
188 }
189
190 if metric == "CR" && vector[&metric] == "X" {
192 return "H".to_string();
193 }
194
195 if metric == "IR" && vector[&metric] == "X" {
197 return "H".to_string();
198 }
199 if metric == "AR" && vector[&metric] == "X" {
201 return "H".to_string();
202 }
203
204 let metric_with_m: String = "M".to_string() + &metric;
207 if vector.contains_key(&metric_with_m) {
208 if vector[&metric_with_m] != "X" {
209 return match vector.get(&metric_with_m) {
210 Some(metric_value) => metric_value.to_string(),
211 None => "".to_string(),
212 };
213 }
214 }
215
216 match vector.get(&metric) {
217 Some(metric_value) => metric_value.to_string(),
218 None => "".to_string(),
219 }
220}
221
222fn get_eq_1(vector: HashMap<String, String>) -> u8 {
223 let av_value: String = get_m(vector.clone(), "AV".to_string());
224 let pr_value: String = get_m(vector.clone(), "PR".to_string());
225 let ui_value: String = get_m(vector.clone(), "UI".to_string());
226
227 if av_value == "N" && pr_value == "N" && ui_value == "N" {
228 return 0;
229 }
230
231 if (av_value == "N" || pr_value == "N" || ui_value == "N")
232 && !(av_value == "N" && pr_value == "N" && ui_value == "N")
233 && !(av_value == "P")
234 {
235 return 1;
236 }
237
238 if av_value == "P" || !(av_value == "N" || pr_value == "N" || ui_value == "N") {
239 return 2;
240 }
241
242 0
243}
244fn get_eq_2(vector: HashMap<String, String>) -> u8 {
245 let ac_value: String = get_m(vector.clone(), "AC".to_string());
248 let at_value: String = get_m(vector.clone(), "AT".to_string());
249 if ac_value == "L" && at_value == "N" {
250 return 0;
251 }
252
253 if !(ac_value == "L" && at_value == "N") {
254 return 1;
255 }
256
257 0
258}
259fn get_eq_3(vector: HashMap<String, String>) -> u8 {
260 let vc_value: String = get_m(vector.clone(), "VC".to_string());
264 let vi_value: String = get_m(vector.clone(), "VI".to_string());
265 let va_value: String = get_m(vector.clone(), "VA".to_string());
266
267 if vc_value == "H" && vi_value == "H" {
268 return 0;
269 }
270
271 if !(vc_value == "H" && vi_value == "H")
272 && (vc_value == "H" || vi_value == "H" || va_value == "H")
273 {
274 return 1;
275 }
276
277 if !(vc_value == "H" || vi_value == "H" || va_value == "H") {
278 return 2;
279 }
280
281 0
282}
283
284fn get_eq_4(vector: HashMap<String, String>) -> u8 {
285 let msi_value: String = get_m(vector.clone(), "MSI".to_string());
290 let msa_value: String = get_m(vector.clone(), "MSA".to_string());
291 let si_value: String = get_m(vector.clone(), "SI".to_string());
292 let sa_value: String = get_m(vector.clone(), "SA".to_string());
293 let sc_value: String = get_m(vector.clone(), "SC".to_string());
294
295 if msi_value == "S" || msa_value == "S" {
296 return 0;
297 }
298
299 if !(msi_value == "S" || msa_value == "S")
300 && (sc_value == "H" || si_value == "H" || sa_value == "H")
301 {
302 return 1;
303 }
304
305 if !(msi_value == "S" || msa_value == "S")
306 && !(sc_value == "H" || si_value == "H" || sa_value == "H")
307 {
308 return 2;
309 }
310
311 0
312}
313
314fn get_eq_5(vector: HashMap<String, String>) -> u8 {
315 match get_m(vector.clone(), "E".to_string()).as_str() {
320 "A" => 0,
321 "P" => 1,
322 "U" => 2,
323 _ => 0,
324 }
325}
326
327fn get_eq_6(vector: HashMap<String, String>) -> u8 {
328 let cr_value: String = get_m(vector.clone(), "CR".to_string());
332 let ir_value: String = get_m(vector.clone(), "IR".to_string());
333 let ar_value: String = get_m(vector.clone(), "AR".to_string());
334
335 let vc_value: String = get_m(vector.clone(), "VC".to_string());
336 let vi_value: String = get_m(vector.clone(), "VI".to_string());
337 let va_value: String = get_m(vector.clone(), "VA".to_string());
338
339 if (cr_value == "H" && vc_value == "H")
340 || (ir_value == "H" && vi_value == "H")
341 || (ar_value == "H" && va_value == "H")
342 {
343 return 0;
344 } else if !((cr_value == "H" && vc_value == "H")
345 || (ir_value == "H" && vi_value == "H")
346 || (ar_value == "H" && va_value == "H"))
347 {
348 return 1;
349 }
350
351 return 0;
352}
353
354fn calculate_macro_vector(vector: HashMap<String, String>) -> HashMap<String, u8> {
355 HashMap::from([
356 ("eq_1".to_string(), get_eq_1(vector.clone())),
357 ("eq_2".to_string(), get_eq_2(vector.clone())),
358 ("eq_3".to_string(), get_eq_3(vector.clone())),
359 ("eq_4".to_string(), get_eq_4(vector.clone())),
360 ("eq_5".to_string(), get_eq_5(vector.clone())),
361 ("eq_6".to_string(), get_eq_6(vector.clone())),
362 ])
363}
364
365fn build_max_composed() -> HashMap<String, HashMap<u8, Vec<String>>> {
366 HashMap::from([
367 (
368 "eq1".to_string(),
369 HashMap::from([
370 (0, vec!["AV:N/PR:N/UI:N/".to_string()]),
371 (
372 1,
373 vec![
374 "AV:A/PR:N/UI:N/".to_string(),
375 "AV:N/PR:L/UI:N/".to_string(),
376 "AV:N/PR:N/UI:P/".to_string(),
377 ],
378 ),
379 (
380 2,
381 vec!["AV:P/PR:N/UI:N/".to_string(), "AV:A/PR:L/UI:P/".to_string()],
382 ),
383 ]),
384 ),
385 (
386 "eq2".to_string(),
387 HashMap::from([
388 (0, vec!["AC:L/AT:N/".to_string()]),
389 (1, vec!["AC:H/AT:N/".to_string(), "AC:L/AT:P/".to_string()]),
390 ]),
391 ),
392 (
393 "eq4".to_string(),
394 HashMap::from([
395 (0 as u8, vec!["SC:H/SI:S/SA:S/".to_string()]),
396 (1 as u8, vec!["SC:H/SI:H/SA:H/".to_string()]),
397 (2 as u8, vec!["SC:L/SI:L/SA:L/".to_string()]),
398 ]),
399 ),
400 (
401 "eq5".to_string(),
402 HashMap::from([
403 (0 as u8, vec!["E:A/".to_string()]),
404 (1 as u8, vec!["E:P/".to_string()]),
405 (2 as u8, vec!["E:U/".to_string()]),
406 ]),
407 ),
408 ])
409}
410
411fn build_max_composed_eq_3() -> HashMap<u8, HashMap<String, Vec<String>>> {
412 HashMap::from([
413 (
414 0 as u8,
415 HashMap::from([
416 (
417 "0".to_string(),
418 vec!["VC:H/VI:H/VA:H/CR:H/IR:H/AR:H/".to_string()],
419 ),
420 (
421 "1".to_string(),
422 vec![
423 "VC:H/VI:H/VA:L/CR:M/IR:M/AR:H/".to_string(),
424 "VC:H/VI:H/VA:H/CR:M/IR:M/AR:M/".to_string(),
425 ],
426 ),
427 ]),
428 ),
429 (
430 1 as u8,
431 HashMap::from([
432 (
433 "0".to_string(),
434 vec![
435 "VC:L/VI:H/VA:H/CR:H/IR:H/AR:H/".to_string(),
436 "VC:H/VI:L/VA:H/CR:H/IR:H/AR:H/".to_string(),
437 ],
438 ),
439 (
440 "1".to_string(),
441 vec![
442 "VC:L/VI:H/VA:L/CR:H/IR:M/AR:H/".to_string(),
443 "VC:L/VI:H/VA:H/CR:H/IR:M/AR:M/".to_string(),
444 "VC:H/VI:L/VA:H/CR:M/IR:H/AR:M/".to_string(),
445 "VC:H/VI:L/VA:L/CR:M/IR:H/AR:H/".to_string(),
446 "VC:L/VI:L/VA:H/CR:H/IR:H/AR:M/".to_string(),
447 ],
448 ),
449 ]),
450 ),
451 (
452 2 as u8,
453 HashMap::from([(
454 "1".to_string(),
455 vec!["VC:L/VI:L/VA:L/CR:H/IR:H/AR:H/".to_string()],
456 )]),
457 ),
458 ])
459}
460
461fn get_eq_maxes(macro_vector: HashMap<String, u8>, eq: u8) -> Vec<String> {
462 let eq_number = format!("eq{}", eq);
463 let max_composed_value = &build_max_composed()[&eq_number];
464 let macro_vector_value: u8 = macro_vector[&format!("eq_{}", eq).to_string()];
465 max_composed_value[¯o_vector_value].clone()
466}
467
468fn get_eq_3_6_maxes(macro_vector: HashMap<String, u8>) -> Vec<String> {
469 let max_composed_value = &build_max_composed_eq_3();
470 let macro_vector_value: u8 = macro_vector["eq_3"];
471 let result = max_composed_value[¯o_vector_value].clone();
472 result[¯o_vector["eq_6"].to_string()].clone()
473}
474
475fn build_max_severity() -> HashMap<String, HashMap<i32, f64>> {
476 HashMap::from([
477 (
478 "eq1".to_string(),
479 HashMap::from([(0, 1.0), (1, 4.0), (2, 5.0)]),
480 ),
481 ("eq2".to_string(), HashMap::from([(0, 1.0), (1, 2.0)])),
482 (
483 "eq4".to_string(),
484 HashMap::from([(0, 6.0), (1, 5.0), (2, 4.0)]),
485 ),
486 (
487 "eq5".to_string(),
488 HashMap::from([(0, 1.0), (1, 1.0), (2, 1.0)]),
489 ),
490 ])
491}
492
493fn build_eq3eq6_max_severity() -> HashMap<i32, HashMap<i32, f64>> {
494 HashMap::from([
495 (0, HashMap::from([(0, 7.0), (1, 6.0)])),
496 (1, HashMap::from([(0, 8.0), (1, 8.0)])),
497 (2, HashMap::from([(1, 10.0)])),
498 ])
499}
500
501fn get_value_by_key(vector: HashMap<String, String>, key: String) -> String {
502 match vector.get(&key) {
503 Some(val) => (*val).clone(),
504 None => "".to_string(),
505 }
506}
507
508pub fn calculate_score_v_4(vector: String) -> f64 {
521 let av_levels = build_av_levels();
522 let pr_levels = build_pr_levels();
523 let ui_levels = build_ui_levels();
524 let ac_levels = build_ac_levels();
525 let at_levels = build_at_levels();
526 let vc_levels = build_vc_levels();
527 let vi_levels = build_vi_levels();
528 let va_levels = build_va_levels();
529 let sc_levels = build_sc_levels();
530 let si_levels = build_si_levels();
531 let sa_levels = build_sa_levels();
532 let cr_levels = build_cr_levels();
533 let ir_levels = build_ir_levels();
534 let ar_levels = build_ar_levels();
535
536 let vector = preprocess_vector(vector);
537 let vector = build_full_vector(vector);
538 let macro_vector: HashMap<String, u8> = calculate_macro_vector(vector.clone());
539
540 if ["VC", "VI", "VA", "SC", "SI", "SA"]
541 .iter()
542 .all(|&metric| get_m(vector.clone(), metric.to_string()) == "N")
543 {
544 return 0.0;
545 }
546
547 let macro_vector_ordered: Vec<u8> = vec![
548 macro_vector["eq_1"],
549 macro_vector["eq_2"],
550 macro_vector["eq_3"],
551 macro_vector["eq_4"],
552 macro_vector["eq_5"],
553 macro_vector["eq_6"],
554 ];
555 let mut score: f64 = match build_macro_vector_values().get(
556 ¯o_vector_ordered
557 .iter()
558 .map(|x| x.to_string())
559 .collect::<Vec<String>>()
560 .join(""),
561 ) {
562 Some(macro_vector_value) => *macro_vector_value,
563 None => 0.0,
564 };
565
566 let eq_1_next_lower_macro = vec![
568 macro_vector["eq_1"] + 1,
569 macro_vector["eq_2"],
570 macro_vector["eq_3"],
571 macro_vector["eq_4"],
572 macro_vector["eq_5"],
573 macro_vector["eq_6"],
574 ]
575 .iter()
576 .map(|x| x.to_string())
577 .collect::<Vec<String>>()
578 .join("");
579
580 let eq_2_next_lower_macro = vec![
581 macro_vector["eq_1"],
582 macro_vector["eq_2"] + 1,
583 macro_vector["eq_3"],
584 macro_vector["eq_4"],
585 macro_vector["eq_5"],
586 macro_vector["eq_6"],
587 ]
588 .iter()
589 .map(|x| x.to_string())
590 .collect::<Vec<String>>()
591 .join("");
592
593 let mut eq3eq6_next_lower_macro: String = String::new();
595 let mut eq3eq6_next_lower_macro_left: String = String::new();
596 let mut eq3eq6_next_lower_macro_right: String = String::new();
597 let eq3: u8 = macro_vector["eq_3"];
598 let eq6: u8 = macro_vector["eq_6"];
599 if eq3 == 1 && eq6 == 1 {
600 eq3eq6_next_lower_macro = vec![
602 macro_vector["eq_1"],
603 macro_vector["eq_2"],
604 macro_vector["eq_3"] + 1,
605 macro_vector["eq_4"],
606 macro_vector["eq_5"],
607 macro_vector["eq_6"],
608 ]
609 .iter()
610 .map(|x| x.to_string())
611 .collect::<Vec<String>>()
612 .join("");
613 } else if eq3 == 0 && eq6 == 1 {
614 eq3eq6_next_lower_macro = vec![
616 macro_vector["eq_1"],
617 macro_vector["eq_2"],
618 macro_vector["eq_3"] + 1,
619 macro_vector["eq_4"],
620 macro_vector["eq_5"],
621 macro_vector["eq_6"],
622 ]
623 .iter()
624 .map(|x| x.to_string())
625 .collect::<Vec<String>>()
626 .join("");
627 } else if eq3 == 1 && eq6 == 0 {
628 eq3eq6_next_lower_macro = vec![
630 macro_vector["eq_1"],
631 macro_vector["eq_2"],
632 macro_vector["eq_3"],
633 macro_vector["eq_4"],
634 macro_vector["eq_5"],
635 macro_vector["eq_6"] + 1,
636 ]
637 .iter()
638 .map(|x| x.to_string())
639 .collect::<Vec<String>>()
640 .join("");
641 } else if eq3 == 0 && eq6 == 0 {
642 eq3eq6_next_lower_macro_left = vec![
645 macro_vector["eq_1"],
646 macro_vector["eq_2"],
647 macro_vector["eq_3"],
648 macro_vector["eq_4"],
649 macro_vector["eq_5"],
650 macro_vector["eq_6"] + 1,
651 ]
652 .iter()
653 .map(|x| x.to_string())
654 .collect::<Vec<String>>()
655 .join("");
656 eq3eq6_next_lower_macro_right = vec![
657 macro_vector["eq_1"],
658 macro_vector["eq_2"],
659 macro_vector["eq_3"] + 1,
660 macro_vector["eq_4"],
661 macro_vector["eq_5"],
662 macro_vector["eq_6"],
663 ]
664 .iter()
665 .map(|x| x.to_string())
666 .collect::<Vec<String>>()
667 .join("");
668 } else {
669 eq3eq6_next_lower_macro = vec![
671 macro_vector["eq_1"],
672 macro_vector["eq_2"],
673 macro_vector["eq_3"] + 1,
674 macro_vector["eq_4"],
675 macro_vector["eq_5"],
676 macro_vector["eq_6"] + 1,
677 ]
678 .iter()
679 .map(|x| x.to_string())
680 .collect::<Vec<String>>()
681 .join("");
682 }
683
684 let eq4_next_lower_macro = vec![
685 macro_vector["eq_1"],
686 macro_vector["eq_2"],
687 macro_vector["eq_3"],
688 macro_vector["eq_4"] + 1,
689 macro_vector["eq_5"],
690 macro_vector["eq_6"],
691 ]
692 .iter()
693 .map(|x| x.to_string())
694 .collect::<Vec<String>>()
695 .join("");
696
697 let eq5_next_lower_macro = vec![
698 macro_vector["eq_1"],
699 macro_vector["eq_2"],
700 macro_vector["eq_3"],
701 macro_vector["eq_4"],
702 macro_vector["eq_5"] + 1,
703 macro_vector["eq_6"],
704 ]
705 .iter()
706 .map(|x| x.to_string())
707 .collect::<Vec<String>>()
708 .join("");
709
710 let score_eq1_next_lower_macro = match build_macro_vector_values().get(&eq_1_next_lower_macro) {
711 Some(macro_vector_value) => *macro_vector_value,
712 None => 0.0,
713 };
714
715 let score_eq2_next_lower_macro = match build_macro_vector_values().get(&eq_2_next_lower_macro) {
716 Some(macro_vector_value) => *macro_vector_value,
717 None => 0.0,
718 };
719
720 let mut score_eq3eq6_next_lower_macro: f64 = 0.0;
721 if eq3 == 0 && eq6 == 0 {
722 let score_eq3eq6_next_lower_macro_left =
724 match build_macro_vector_values().get(&eq3eq6_next_lower_macro_left) {
725 Some(value) => *value,
726 None => 0.0,
727 };
728
729 let score_eq3eq6_next_lower_macro_right =
730 match build_macro_vector_values().get(&eq3eq6_next_lower_macro_right) {
731 Some(value) => *value,
732 None => 0.0,
733 };
734
735 if score_eq3eq6_next_lower_macro_left > score_eq3eq6_next_lower_macro_right {
736 score_eq3eq6_next_lower_macro = score_eq3eq6_next_lower_macro_left
737 } else {
738 score_eq3eq6_next_lower_macro = score_eq3eq6_next_lower_macro_right
739 }
740 } else {
741 score_eq3eq6_next_lower_macro =
742 match build_macro_vector_values().get(&eq3eq6_next_lower_macro) {
743 Some(value) => *value,
744 None => 0.0,
745 };
746 }
747
748 let score_eq4_next_lower_macro = match build_macro_vector_values().get(&eq4_next_lower_macro) {
749 Some(value) => *value,
750 None => 0.0,
751 };
752
753 let score_eq5_next_lower_macro = match build_macro_vector_values().get(&eq5_next_lower_macro) {
754 Some(value) => *value,
755 None => 0.0,
756 };
757
758 let eq1_maxes = get_eq_maxes(macro_vector.clone(), 1);
761 let eq2_maxes = get_eq_maxes(macro_vector.clone(), 2);
762 let eq3_eq6_maxes = get_eq_3_6_maxes(macro_vector.clone());
763 let eq4_maxes = get_eq_maxes(macro_vector.clone(), 4);
764 let eq5_maxes = get_eq_maxes(macro_vector.clone(), 5);
765
766 let mut max_vectors: Vec<String> = vec![];
767 for eq1_max in &eq1_maxes {
768 for eq2_max in &eq2_maxes {
769 for eq3_eq6_max in &eq3_eq6_maxes {
770 for eq4_max in &eq4_maxes {
771 for eq5_max in &eq5_maxes {
772 max_vectors.push(format!(
773 "{}{}{}{}{}",
774 eq1_max, eq2_max, eq3_eq6_max, eq4_max, eq5_max
775 ));
776 }
777 }
778 }
779 }
780 }
781
782 let mut severity_distance_av: f64 = 0.0;
783 let mut severity_distance_pr: f64 = 0.0;
784 let mut severity_distance_ui: f64 = 0.0;
785 let mut severity_distance_ac: f64 = 0.0;
786 let mut severity_distance_at: f64 = 0.0;
787 let mut severity_distance_vc: f64 = 0.0;
788 let mut severity_distance_vi: f64 = 0.0;
789 let mut severity_distance_va: f64 = 0.0;
790 let mut severity_distance_sc: f64 = 0.0;
791 let mut severity_distance_si: f64 = 0.0;
792 let mut severity_distance_sa: f64 = 0.0;
793 let mut severity_distance_cr: f64 = 0.0;
794 let mut severity_distance_ir: f64 = 0.0;
795 let mut severity_distance_ar: f64 = 0.0;
796
797 for max_vector in &max_vectors {
798 let max_vector_preprocessed = preprocess_vector(max_vector.to_string());
799 severity_distance_av = av_levels[&get_m(vector.clone(), "AV".to_string())]
800 - av_levels[&get_value_by_key(max_vector_preprocessed.clone(), "AV".to_string())];
801 severity_distance_pr = pr_levels[&get_m(vector.clone(), "PR".to_string())]
802 - pr_levels[&get_value_by_key(max_vector_preprocessed.clone(), "PR".to_string())];
803 severity_distance_ui = ui_levels[&get_m(vector.clone(), "UI".to_string())]
804 - ui_levels[&get_value_by_key(max_vector_preprocessed.clone(), "UI".to_string())];
805 severity_distance_ac = ac_levels[&get_m(vector.clone(), "AC".to_string())]
806 - ac_levels[&get_value_by_key(max_vector_preprocessed.clone(), "AC".to_string())];
807 severity_distance_at = at_levels[&get_m(vector.clone(), "AT".to_string())]
808 - at_levels[&get_value_by_key(max_vector_preprocessed.clone(), "AT".to_string())];
809 severity_distance_vc = vc_levels[&get_m(vector.clone(), "VC".to_string())]
810 - vc_levels[&get_value_by_key(max_vector_preprocessed.clone(), "VC".to_string())];
811 severity_distance_vi = vi_levels[&get_m(vector.clone(), "VI".to_string())]
812 - vi_levels[&get_value_by_key(max_vector_preprocessed.clone(), "VI".to_string())];
813 severity_distance_va = va_levels[&get_m(vector.clone(), "VA".to_string())]
814 - va_levels[&get_value_by_key(max_vector_preprocessed.clone(), "VA".to_string())];
815 severity_distance_sc = sc_levels[&get_m(vector.clone(), "SC".to_string())]
816 - sc_levels[&get_value_by_key(max_vector_preprocessed.clone(), "SC".to_string())];
817 severity_distance_si = si_levels[&get_m(vector.clone(), "SI".to_string())]
818 - si_levels[&get_value_by_key(max_vector_preprocessed.clone(), "SI".to_string())];
819 severity_distance_sa = sa_levels[&get_m(vector.clone(), "SA".to_string())]
820 - sa_levels[&get_value_by_key(max_vector_preprocessed.clone(), "SA".to_string())];
821 severity_distance_cr = cr_levels[&get_m(vector.clone(), "CR".to_string())]
822 - cr_levels[&get_value_by_key(max_vector_preprocessed.clone(), "CR".to_string())];
823 severity_distance_ir = ir_levels[&get_m(vector.clone(), "IR".to_string())]
824 - ir_levels[&get_value_by_key(max_vector_preprocessed.clone(), "IR".to_string())];
825 severity_distance_ar = ar_levels[&get_m(vector.clone(), "AR".to_string())]
826 - ar_levels[&get_value_by_key(max_vector_preprocessed.clone(), "AR".to_string())];
827
828 if vec![
829 severity_distance_av,
830 severity_distance_pr,
831 severity_distance_ui,
832 severity_distance_ac,
833 severity_distance_at,
834 severity_distance_vc,
835 severity_distance_vi,
836 severity_distance_va,
837 severity_distance_sc,
838 severity_distance_si,
839 severity_distance_sa,
840 severity_distance_cr,
841 severity_distance_ir,
842 severity_distance_ar,
843 ]
844 .iter()
845 .any(|x| x < &0.0)
846 {
847 continue;
848 }
849
850 break;
851 }
852
853 let current_severity_distance_eq1: f64 =
854 severity_distance_av + severity_distance_pr + severity_distance_ui;
855 let current_severity_distance_eq2: f64 = severity_distance_ac + severity_distance_at;
856 let current_severity_distance_eq3eq6: f64 = severity_distance_vc
857 + severity_distance_vi
858 + severity_distance_va
859 + severity_distance_cr
860 + severity_distance_ir
861 + severity_distance_ar;
862 let current_severity_distance_eq4: f64 =
863 severity_distance_sc + severity_distance_si + severity_distance_sa;
864
865 let step: f64 = 0.1;
866
867 let mut available_distance_eq1: f64 = 0.0;
870 if score_eq1_next_lower_macro != 0.0 {
871 available_distance_eq1 = score - score_eq1_next_lower_macro;
872 }
873
874 let mut available_distance_eq2: f64 = 0.0;
875 if score_eq2_next_lower_macro != 0.0 {
876 available_distance_eq2 = score - score_eq2_next_lower_macro;
877 }
878
879 let mut available_distance_eq3eq6: f64 = 0.0;
880 if score_eq3eq6_next_lower_macro != 0.0 {
881 available_distance_eq3eq6 = score - score_eq3eq6_next_lower_macro;
882 }
883
884 let mut available_distance_eq4: f64 = 0.0;
885 if score_eq4_next_lower_macro != 0.0 {
886 available_distance_eq4 = score - score_eq4_next_lower_macro;
887 }
888
889 let mut available_distance_eq5: f64 = 0.0;
890 if score_eq5_next_lower_macro != 0.0 {
891 available_distance_eq5 = score - score_eq5_next_lower_macro;
892 }
893
894 let mut n_existing_lower: f64 = 0.0;
896
897 let mut normalized_severity_eq1: f64 = 0.0;
898 let mut normalized_severity_eq2: f64 = 0.0;
899 let mut normalized_severity_eq3eq6: f64 = 0.0;
900 let mut normalized_severity_eq4: f64 = 0.0;
901 let mut normalized_severity_eq5: f64 = 0.0;
902
903 let max_severity = build_max_severity();
904 let max_severity_eq1 = max_severity[&"eq1".to_string()][&(macro_vector["eq_1"] as i32)] * step;
905 let max_severity_eq2 = max_severity[&"eq2".to_string()][&(macro_vector["eq_2"] as i32)] * step;
906 let max_severity_eq3eq6 = build_eq3eq6_max_severity()[&(macro_vector["eq_3"] as i32)]
907 [&(macro_vector["eq_6"] as i32)]
908 * step;
909 let max_severity_eq4 = max_severity[&"eq4".to_string()][&(macro_vector["eq_4"] as i32)] * step;
910
911 if available_distance_eq1 != 0.0 {
912 n_existing_lower += 1.0;
913 let percent_to_next_eq1_severity = current_severity_distance_eq1 / max_severity_eq1;
914 normalized_severity_eq1 = available_distance_eq1 * percent_to_next_eq1_severity;
915 }
916
917 if available_distance_eq2 != 0.0 {
918 n_existing_lower += 1.0;
919 let percent_to_next_eq2_severity = current_severity_distance_eq2 / max_severity_eq2;
920 normalized_severity_eq2 = available_distance_eq2 * percent_to_next_eq2_severity;
921 }
922
923 if available_distance_eq3eq6 != 0.0 {
924 n_existing_lower += 1.0;
925 let percent_to_next_eq3eq6_severity =
926 current_severity_distance_eq3eq6 / max_severity_eq3eq6;
927 normalized_severity_eq3eq6 = available_distance_eq3eq6 * percent_to_next_eq3eq6_severity;
928 }
929
930 if available_distance_eq4 != 0.0 {
931 n_existing_lower += 1.0;
932 let percent_to_next_eq4_severity = (current_severity_distance_eq4) / max_severity_eq4;
933 normalized_severity_eq4 = available_distance_eq4 * percent_to_next_eq4_severity;
934 }
935
936 if available_distance_eq5 != 0.0 {
937 n_existing_lower += 1.0;
939 let percent_to_next_eq5_severity = 0.0;
940 normalized_severity_eq5 = available_distance_eq5 * percent_to_next_eq5_severity;
941 }
942
943 let mean_distance: f64 = match n_existing_lower {
945 value if value == 0.0 => 0.0,
946 _ => {
947 (normalized_severity_eq1
948 + normalized_severity_eq2
949 + normalized_severity_eq3eq6
950 + normalized_severity_eq4
951 + normalized_severity_eq5)
952 / n_existing_lower
953 }
954 };
955
956 score -= mean_distance;
957 if score < 0.0 {
958 score = 0.0;
959 }
960
961 if score > 10.0 {
962 score = 10.0;
963 }
964
965 (score * 10.0).round() / 10.0
966}
967
968pub fn calculate_severity_v_4(score: f64) -> String {
981 if score == 0.0 {
982 "None".to_string()
983 } else if score < 4.0 {
984 "Low".to_string()
985 } else if score < 7.0 {
986 "Medium".to_string()
987 } else if score < 9.0 {
988 "High".to_string()
989 } else {
990 "Critical".to_string()
991 }
992}
993
994pub fn calculate_base_score_v_2(vector: String) -> f64 {
1007 let vector: HashMap<String, String> = preprocess_vector(vector);
1008
1009 let access_vector: f64 = match vector["AV"].as_str() {
1010 "L" => 0.395,
1011 "A" => 0.646,
1012 "N" => 1.0,
1013 _ => panic!("Invalid value for AccessVector (AV)."),
1014 };
1015
1016 let access_complexity: f64 = match vector["AC"].as_str() {
1017 "H" => 0.35,
1018 "M" => 0.61,
1019 "L" => 0.71,
1020 _ => panic!("Invalid value for AccessComplexity (AC)."),
1021 };
1022
1023 let authentication: f64 = match vector["Au"].as_str() {
1024 "M" => 0.45,
1025 "S" => 0.56,
1026 "N" => 0.704,
1027 _ => panic!("Invalid value for Authentication (Au)."),
1028 };
1029
1030 let conf_impact: f64 = match vector["C"].as_str() {
1031 "N" => 0.0,
1032 "P" => 0.275,
1033 "C" => 0.660,
1034 _ => panic!("Invalid value for Confidence Impact (C)."),
1035 };
1036
1037 let integ_impact: f64 = match vector["I"].as_str() {
1038 "N" => 0.0,
1039 "P" => 0.275,
1040 "C" => 0.660,
1041 _ => panic!("Invalid value for IntegImpact (I)."),
1042 };
1043
1044 let avail_impact: f64 = match vector["A"].as_str() {
1045 "N" => 0.0,
1046 "P" => 0.275,
1047 "C" => 0.660,
1048 _ => panic!("Invalid value for IntegImpact (A)."),
1049 };
1050
1051 let impact: f64 =
1052 10.41 * (1.0 - (1.0 - conf_impact) * (1.0 - integ_impact) * (1.0 - avail_impact));
1053 let exploitability: f64 = 20.0 * access_vector * access_complexity * authentication;
1054 let f_impact: f64 = match impact {
1055 value if value == 0.0 => 0.0,
1056 _ => 1.176,
1057 };
1058 let score: f64 = (0.6 * impact + (0.4 * exploitability - 1.5)) * f_impact;
1059
1060 (score * 10.0).round() / 10.0
1061}
1062
1063pub fn calculate_temp_score_v_2(base_score: f64, temp_vector: String) -> f64 {
1076 let vector: HashMap<String, String> = preprocess_vector(temp_vector);
1077 let exploitability: f64 = match vector["E"].as_str() {
1078 "U" => 0.85,
1079 "POC" => 0.9,
1080 "F" => 0.95,
1081 "H" => 1.0,
1082 "ND" => 1.0,
1083 _ => panic!("Invalid value for Exploitability (E)."),
1084 };
1085 let remediation_level: f64 = match vector["RL"].as_str() {
1086 "OF" => 0.87,
1087 "TF" => 0.90,
1088 "W" => 0.95,
1089 "U" => 1.0,
1090 "ND" => 1.0,
1091 _ => panic!("Invalid value for RemediationLevel (RL)."),
1092 };
1093 let report_confidence: f64 = match vector["RC"].as_str() {
1094 "UC" => 0.90,
1095 "UR" => 0.95,
1096 "C" => 1.0,
1097 "ND" => 1.0,
1098 _ => panic!("Invalid value for ReportConfidence (RC)"),
1099 };
1100
1101 let score: f64 = base_score * exploitability * remediation_level * report_confidence;
1102
1103 (score * 10.0).round() / 10.0
1104}
1105
1106pub fn calculate_env_score_v_2(vector: String) -> f64 {
1119 let vector: HashMap<String, String> = preprocess_vector(vector);
1120
1121 let conf_impact: f64 = match vector["C"].as_str() {
1122 "N" => 0.0,
1123 "P" => 0.275,
1124 "C" => 0.660,
1125 _ => panic!("Invalid value for Confidence Impact (C)."),
1126 };
1127
1128 let integ_impact: f64 = match vector["I"].as_str() {
1129 "N" => 0.0,
1130 "P" => 0.275,
1131 "C" => 0.660,
1132 _ => panic!("Invalid value for IntegImpact (I)."),
1133 };
1134
1135 let collateral_damage_potential: f64 = match vector["CDP"].as_str() {
1136 "N" => 0.0,
1137 "L" => 0.1,
1138 "LM" => 0.3,
1139 "MH" => 0.4,
1140 "H" => 0.5,
1141 "ND" => 0.0,
1142 _ => panic!("Invalid value for CollateralDamagePotential (CDP)"),
1143 };
1144
1145 let target_distribution: f64 = match vector["TD"].as_str() {
1146 "N" => 0.0,
1147 "L" => 0.25,
1148 "M" => 0.75,
1149 "H" => 1.0,
1150 "ND" => 1.0,
1151 _ => panic!("Invalid value for TargetDistribution (TD)"),
1152 };
1153
1154 let conf_req: f64 = match vector["CR"].as_str() {
1155 "L" => 0.5,
1156 "M" => 1.0,
1157 "H" => 1.51,
1158 "ND" => 1.0,
1159 _ => panic!("Invalid value for ConfReq (CR)"),
1160 };
1161
1162 let integ_req: f64 = match vector["IR"].as_str() {
1163 "L" => 0.5,
1164 "M" => 1.0,
1165 "H" => 1.51,
1166 "ND" => 1.0,
1167 _ => panic!("Invalid value for IntegReq (IR)"),
1168 };
1169
1170 let avail_req: f64 = match vector["AR"].as_str() {
1171 "L" => 0.5,
1172 "M" => 1.0,
1173 "H" => 1.51,
1174 "ND" => 1.0,
1175 _ => panic!("Invalid value for AvailReq (AR)"),
1176 };
1177
1178 let avail_impact: f64 = match vector["A"].as_str() {
1179 "N" => 0.0,
1180 "P" => 0.275,
1181 "C" => 0.660,
1182 _ => panic!("Invalid value for IntegImpact (A)."),
1183 };
1184
1185 let access_vector: f64 = match vector["AV"].as_str() {
1186 "L" => 0.395,
1187 "A" => 0.646,
1188 "N" => 1.0,
1189 _ => panic!("Invalid value for AccessVector (AV)."),
1190 };
1191
1192 let access_complexity: f64 = match vector["AC"].as_str() {
1193 "H" => 0.35,
1194 "M" => 0.61,
1195 "L" => 0.71,
1196 _ => panic!("Invalid value for AccessComplexity (AC)."),
1197 };
1198
1199 let authentication: f64 = match vector["Au"].as_str() {
1200 "M" => 0.45,
1201 "S" => 0.56,
1202 "N" => 0.704,
1203 _ => panic!("Invalid value for Authentication (Au)."),
1204 };
1205
1206 let second_adjusted_impact: f64 = 10.41
1207 * (1.0
1208 - (1.0 - conf_impact * conf_req)
1209 * (1.0 - integ_impact * integ_req)
1210 * (1.0 - avail_impact * avail_req));
1211
1212 let adjusted_impact: f64 = match second_adjusted_impact {
1213 value if value < 10.0 => value,
1214 _ => 10.0,
1215 };
1216
1217 let impact: f64 =
1218 10.41 * (1.0 - (1.0 - conf_impact) * (1.0 - integ_impact) * (1.0 - avail_impact));
1219 let exploitability: f64 = 20.0 * access_vector * access_complexity * authentication;
1220 let f_impact: f64 = match impact {
1221 value if value == 0.0 => 0.0,
1222 _ => 1.176,
1223 };
1224 let adjusted_base: f64 = (0.6 * adjusted_impact + (0.4 * exploitability - 1.5)) * f_impact;
1225
1226 let exploitability: f64 = match vector["E"].as_str() {
1227 "U" => 0.85,
1228 "POC" => 0.9,
1229 "F" => 0.95,
1230 "H" => 1.0,
1231 "ND" => 1.0,
1232 _ => panic!("Invalid value for Exploitability (E)."),
1233 };
1234
1235 let remediation_level: f64 = match vector["RL"].as_str() {
1236 "OF" => 0.87,
1237 "TF" => 0.90,
1238 "W" => 0.95,
1239 "U" => 1.0,
1240 "ND" => 1.0,
1241 _ => panic!("Invalid value for RemediationLevel (RL)."),
1242 };
1243
1244 let report_confidence: f64 = match vector["RC"].as_str() {
1245 "UC" => 0.90,
1246 "UR" => 0.95,
1247 "C" => 1.0,
1248 "ND" => 1.0,
1249 _ => panic!("Invalid value for ReportConfidence (RC)"),
1250 };
1251
1252 let mut adjusted_temporal: f64 =
1253 adjusted_base * exploitability * remediation_level * report_confidence;
1254 adjusted_temporal = (adjusted_temporal * 10.0).round() / 10.0;
1255
1256 let score: f64 = (adjusted_temporal + (10.0 - adjusted_temporal) * collateral_damage_potential)
1257 * target_distribution;
1258
1259 (score * 10.0).round() / 10.0
1260}
1261
1262pub fn calculate_severity_v_2(base_score: f64) -> String {
1274 if base_score < 4.0 {
1275 "Low".to_string()
1276 } else if base_score < 7.0 {
1277 "Medium".to_string()
1278 } else {
1279 "High".to_string()
1280 }
1281}
1282
1283#[cfg(test)]
1284mod tests {
1285 use super::*;
1286
1287 #[test]
1288 fn calculate_base_score_v_2_positive() {
1289 assert_eq!(
1290 calculate_base_score_v_2("AV:N/AC:L/Au:N/C:N/I:N/A:C".to_string()),
1291 7.8
1292 );
1293 }
1294
1295 #[test]
1296 fn calculate_temp_score_v_2_positive() {
1297 assert_eq!(
1298 calculate_temp_score_v_2(7.8, "E:F/RL:OF/RC:C".to_string()),
1299 6.4
1300 );
1301 }
1302
1303 #[test]
1304 fn calculate_env_score_v_2_positive() {
1305 let vector: String =
1306 "AV:N/AC:L/Au:N/C:N/I:N/A:C/E:F/RL:OF/RC:C/CDP:H/TD:H/CR:M/IR:M/AR:H".to_string();
1307 assert_eq!(calculate_env_score_v_2(vector), 9.2);
1308 }
1309
1310 #[test]
1311 fn calculate_severity_v_2_low_positive() {
1312 for score in vec![0.0, 3.7, 3.9] {
1313 assert_eq!(calculate_severity_v_2(score), "Low".to_string());
1314 }
1315 }
1316
1317 #[test]
1318 fn calculate_severity_v_2_medium_positive() {
1319 for score in vec![4.0, 5.0, 6.9] {
1320 assert_eq!(calculate_severity_v_2(score), "Medium".to_string());
1321 }
1322 }
1323
1324 #[test]
1325 fn calculate_severity_v_2_high_positive() {
1326 for score in vec![7.0, 7.8, 10.0] {
1327 assert_eq!(calculate_severity_v_2(score), "High".to_string());
1328 }
1329 }
1330
1331 #[test]
1332 fn calculate_score_v_4_positive() {
1333 let file_obj: File = match File::open("src/test_data/base_score_cvss_v4_values.json") {
1334 Ok(value) => value,
1335 Err(_) => panic!("Can't read base_score_cvss_v4_values.json"),
1336 };
1337 let reader = BufReader::new(file_obj);
1338 let data: HashMap<String, f64> = match serde_json::from_reader(reader) {
1339 Ok(value) => value,
1340 Err(_) => panic!("Can't create HashMap<String, f64> from reader."),
1341 };
1342 for (vector, score) in data {
1343 assert_eq!(calculate_score_v_4(vector.clone()), score, "{}", vector);
1344 }
1345 }
1346
1347 #[test]
1348 fn calculate_severity_v_4_positive() {
1349 let file_obj: File = match File::open("src/test_data/base_severity_cvss_v4_values.json") {
1350 Ok(value) => value,
1351 Err(_) => panic!("Can't read base_severity_cvss_v4_values.json"),
1352 };
1353 let reader = BufReader::new(file_obj);
1354 let data: HashMap<String, String> = match serde_json::from_reader(reader) {
1355 Ok(value) => value,
1356 Err(_) => panic!("Can't create HashMap<String, String> from reader."),
1357 };
1358 for (vector, severity) in data {
1359 let score: f64 = calculate_score_v_4(vector);
1360 assert_eq!(calculate_severity_v_4(score), severity)
1361 }
1362 }
1363}