sdl_parser/
vulnerability.rs1use anyhow::{anyhow, Result};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5use crate::{constants::CWE_REGEX, Formalize};
6
7#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
8pub struct Vulnerability {
9 #[serde(alias = "Name", alias = "NAME")]
10 pub name: String,
11 #[serde(alias = "Description", alias = "DESCRIPTION")]
12 pub description: String,
13 #[serde(default, alias = "Technical", alias = "TECHNICAL")]
14 pub technical: bool,
15 #[serde(alias = "Class", alias = "CLASS")]
16 pub class: String,
17}
18
19impl Formalize for Vulnerability {
20 fn formalize(&mut self) -> Result<()> {
21 if !CWE_REGEX.is_match(&self.class) {
22 return Err(anyhow!("Vulnerability class must match /CWE-\\d+/"));
23 }
24
25 Ok(())
26 }
27}
28
29pub type Vulnerabilities = HashMap<String, Vulnerability>;
30
31#[cfg(test)]
32mod tests {
33 use super::*;
34 use crate::parse_sdl;
35
36 #[test]
37 fn vulnerability_is_parsed() {
38 let sdl = r#"
39 name: Some vulnerability
40 description: some-description
41 class: CWE-123
42 "#;
43 serde_yaml::from_str::<Vulnerability>(sdl).unwrap();
44 }
45
46 #[test]
47 fn default_technical_field_is_false() {
48 let sdl = r#"
49 name: Some vulnerability
50 description: some-description
51 class: CWE-123
52 "#;
53 let vulnerability = serde_yaml::from_str::<Vulnerability>(sdl).unwrap();
54 assert!(!vulnerability.technical);
55 }
56
57 #[test]
58 fn vulnerability_is_parsed_in_scenario() {
59 let sdl = r#"
60 name: test-scenario
61 description: some-description
62 vulnerabilities:
63 vuln-1:
64 name: Some vulnerability
65 description: some-description
66 class: CWE-1341
67 vuln-2:
68 name: Some other vulnerability
69 description: some-description
70 technical: false
71 class: CWE-1343
72 "#;
73 let vulnerabilities = parse_sdl(sdl).unwrap().vulnerabilities;
74 insta::with_settings!({sort_maps => true}, {
75 insta::assert_yaml_snapshot!(vulnerabilities);
76 });
77 }
78
79 #[test]
80 fn parses_scenario_with_vulnerability_in_node() {
81 let sdl = r#"
82 name: test-scenario
83 description: some-description
84 vulnerabilities:
85 vuln-1:
86 name: Some vulnerability
87 description: some-description
88 technical: true
89 class: CWE-1341
90 vuln-2:
91 name: Some other vulnerability
92 description: some-description
93 technical: false
94 class: CWE-1343
95 nodes:
96 win-10:
97 type: VM
98 resources:
99 ram: 2 gib
100 cpu: 2
101 source: windows10
102 vulnerabilities:
103 - vuln-2
104 - vuln-1
105 "#;
106 parse_sdl(sdl).unwrap();
107 }
108
109 #[test]
110 #[should_panic(expected = "Vulnerability \"vuln-4\" not found under Scenario Vulnerabilities")]
111 fn missing_vulnerability_for_node() {
112 let sdl = r#"
113 name: test-scenario
114 description: some-description
115 vulnerabilities:
116 vuln-1:
117 name: Some vulnerability
118 description: some-description
119 technical: true
120 class: CWE-1343
121 vuln-2:
122 name: Some other vulnerability
123 description: some-description
124 technical: false
125 class: CWE-1343
126 nodes:
127 win-10:
128 type: VM
129 resources:
130 ram: 2 gib
131 cpu: 2
132 source: windows10
133 vulnerabilities:
134 - vuln-4
135 "#;
136 parse_sdl(sdl).unwrap();
137 }
138
139 #[test]
140 fn parses_vulnerability_for_feature() {
141 let sdl = r#"
142 name: test-scenario
143 description: some-description
144 features:
145 my-less-cool-feature:
146 type: Configuration
147 source:
148 name: cool-config
149 version: 1.0.0
150 vulnerabilities:
151 - vuln-1
152 - vuln-2
153 vulnerabilities:
154 vuln-1:
155 name: Some vulnerability
156 description: some-description
157 technical: true
158 class: CWE-1341
159 vuln-2:
160 name: Some other vulnerability
161 description: some-description
162 technical: false
163 class: CWE-1343
164 "#;
165 parse_sdl(sdl).unwrap();
166 }
167
168 #[test]
169 #[should_panic(expected = "Vulnerability \"vuln-4\" not found under Scenario Vulnerabilities")]
170 fn missing_vulnerability_for_feature() {
171 let sdl = r#"
172 name: test-scenario
173 description: some-description
174 features:
175 my-less-cool-feature:
176 type: configuration
177 source:
178 name: cool-config
179 version: 1.0.0
180 vulnerabilities:
181 - vuln-4
182 vulnerabilities:
183 vuln-1:
184 name: Some vulnerability
185 description: some-description
186 technical: true
187 class: CWE-1341
188 vuln-2:
189 name: Some other vulnerability
190 description: some-description
191 technical: false
192 class: CWE-1343
193 nodes:
194 win-10:
195 type: VM
196 resources:
197 ram: 2 gib
198 cpu: 2
199 source: windows10
200 vulnerabilities:
201 - vuln-4
202 "#;
203 parse_sdl(sdl).unwrap();
204 }
205}