ploit_malper/engine/
parser.rs1use crate::error::Result;
2use crate::models::ScanRecord;
3use serde_json::Value;
4
5pub fn parse_vulnmalper_json(json_input: &str) -> Result<Vec<ScanRecord>> {
6 let value: Value = serde_json::from_str(json_input)?;
7 let findings = match value {
8 Value::Array(arr) => parse_array(&arr),
9 Value::Object(obj) => {
10 if let Some(arr) = obj.get("results").and_then(Value::as_array) {
11 parse_array(arr)
12 } else if let Some(arr) = obj.get("findings").and_then(Value::as_array) {
13 parse_array(arr)
14 } else {
15 Vec::new()
16 }
17 }
18 _ => Vec::new(),
19 };
20
21 Ok(findings)
22}
23
24fn parse_array(arr: &[Value]) -> Vec<ScanRecord> {
25 arr.iter()
26 .filter_map(|v| {
27 let target = v.get("target")?.as_str()?.to_string();
28 let tool = v.get("tool")?.as_str()?.to_string();
29 let title = v.get("title")?.as_str()?.to_string();
30 let severity = v
31 .get("severity")
32 .and_then(Value::as_str)
33 .unwrap_or("unknown")
34 .to_string();
35 let port = v
36 .get("port")
37 .and_then(Value::as_u64)
38 .and_then(|port| u16::try_from(port).ok());
39 let cve = v.get("cve").and_then(Value::as_str).map(ToOwned::to_owned);
40 let service = v
41 .get("service")
42 .and_then(Value::as_str)
43 .map(ToOwned::to_owned);
44
45 Some(ScanRecord {
46 target,
47 tool,
48 title,
49 severity,
50 port,
51 cve,
52 service,
53 raw: v.clone(),
54 ..Default::default()
55 })
56 })
57 .collect()
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn parses_results_array() {
66 let input = r#"{"results":[{"target":"10.0.0.1","tool":"nmap","title":"OpenSSH","severity":"high"}]}"#;
67 let records = parse_vulnmalper_json(input).unwrap();
68 assert_eq!(records.len(), 1);
69 assert_eq!(records[0].target, "10.0.0.1");
70 }
71}