sdl_parser/
training_learning_objective.rs

1use std::collections::HashMap;
2
3use anyhow::{anyhow, Result};
4use serde::{Deserialize, Serialize};
5
6use crate::{evaluation::Evaluation, helpers::Connection};
7
8#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
9pub struct TrainingLearningObjective {
10    #[serde(alias = "Name", alias = "NAME")]
11    pub name: Option<String>,
12    #[serde(alias = "Description", alias = "DESCRIPTION")]
13    pub description: Option<String>,
14    #[serde(alias = "Evaluation", alias = "EVALUATION")]
15    pub evaluation: String,
16}
17
18pub type TrainingLearningObjectives = HashMap<String, TrainingLearningObjective>;
19
20impl Connection<Evaluation> for (&String, &TrainingLearningObjective) {
21    fn validate_connections(&self, potential_evaluation_names: &Option<Vec<String>>) -> Result<()> {
22        if let Some(existing_evaluation_names) = potential_evaluation_names {
23            if !existing_evaluation_names.contains(&self.1.evaluation) {
24                return Err(anyhow!(
25                    "TLO \"{tlo_name}\" Evaluation \"{evaluation_name}\" not found under Scenario Evaluations",
26                    tlo_name = self.0,
27                    evaluation_name =self.1.evaluation
28                ));
29            }
30        } else {
31            return Err(anyhow!(
32                "TLO \"{tlo_name}\" requires an Evaluation but none found under Scenario",
33                tlo_name = self.0
34            ));
35        }
36
37        Ok(())
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use crate::parse_sdl;
45
46    #[test]
47    fn parses_sdl_with_tlos() {
48        let sdl = r#"
49            name: test-scenario
50            description: some description
51            conditions:
52                condition-1:
53                    command: executable/path.sh
54                    interval: 30
55            metrics:
56                metric-1:
57                    type: MANUAL
58                    artifact: true
59                    max-score: 10
60                metric-2:
61                    type: CONDITIONAL
62                    max-score: 10
63                    condition: condition-1
64            vulnerabilities:
65                vulnerability-1:
66                    name: Some other vulnerability
67                    description: some-description
68                    technical: false
69                    class: CWE-1343
70                vulnerability-2:
71                    name: Some vulnerability
72                    description: some-description
73                    technical: false
74                    class: CWE-1341
75            evaluations:
76                evaluation-1:
77                    description: some description
78                    metrics:
79                        - metric-1
80                        - metric-2
81                    min-score: 50
82            tlos:
83                tlo-1:
84                    description: some description
85                    evaluation: evaluation-1
86        "#;
87        let tlos = parse_sdl(sdl).unwrap().tlos;
88        insta::with_settings!({sort_maps => true}, {
89                insta::assert_yaml_snapshot!(tlos);
90        });
91    }
92
93    #[test]
94    fn parses_training_learning_objective() {
95        let tlo_string = r#"
96          name: test-training-learning-objective
97          description: some description
98          evaluation: evaluation-2
99        "#;
100        serde_yaml::from_str::<TrainingLearningObjective>(tlo_string).unwrap();
101    }
102}