ruso_runtime/runtime/
report.rs1use crate::contract::Severity;
2use crate::runtime::spec::CheckMetadata;
3
4#[derive(Debug, Clone)]
5pub struct Finding {
6 pub name: String,
7 pub description: Option<String>,
8 pub impact: Option<String>,
9 pub severity: Severity,
10 pub author: Option<String>,
11 pub cve: Vec<String>,
12 pub cwe: Vec<String>,
13 pub references: Vec<String>,
14 pub cvss: Vec<String>,
15 pub cvss_score: Vec<String>,
16 pub mitigation: Option<String>,
17 pub evidence: Vec<String>,
18}
19
20impl Finding {
21 pub fn from_metadata(metadata: &CheckMetadata, evidence: Vec<String>) -> Option<Self> {
23 let name = metadata
24 .name
25 .clone()
26 .or_else(|| metadata.report_title.clone())?;
27 Some(Self {
28 name,
29 description: metadata.description.clone(),
30 impact: metadata.impact.clone(),
31 severity: metadata.severity.clone().unwrap_or(Severity::Info),
32 author: metadata.author.clone(),
33 cve: metadata.cve.clone(),
34 cwe: metadata.cwe.clone(),
35 references: metadata.references.clone(),
36 cvss: metadata.cvss.clone(),
37 cvss_score: metadata.cvss_score.clone(),
38 mitigation: metadata.mitigation.clone(),
39 evidence,
40 })
41 }
42}
43
44#[derive(Debug, Clone, Default)]
45pub struct Report {
46 pub findings: Vec<Finding>,
47}
48
49impl Report {
50 pub fn set_finding(&mut self, finding: Finding) {
52 self.findings.clear();
53 self.findings.push(finding);
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use crate::contract::Severity;
60 use crate::runtime::spec::CheckMetadata;
61
62 use super::Finding;
63
64 #[test]
65 fn from_metadata_requires_name_or_report_title() {
66 let meta = CheckMetadata {
67 name: Some("Check".into()),
68 description: Some("desc".into()),
69 severity: Some(Severity::Medium),
70 ..Default::default()
71 };
72 let finding = Finding::from_metadata(&meta, vec!["proof".into()]).expect("finding");
73 assert_eq!(finding.name, "Check");
74 assert_eq!(finding.severity, Severity::Medium);
75 assert_eq!(finding.evidence, vec!["proof"]);
76 }
77
78 #[test]
79 fn from_metadata_uses_report_title_fallback() {
80 let meta = CheckMetadata {
81 report_title: Some("Legacy title".into()),
82 ..Default::default()
83 };
84 let finding = Finding::from_metadata(&meta, vec![]).expect("finding");
85 assert_eq!(finding.name, "Legacy title");
86 assert_eq!(finding.severity, Severity::Info);
87 }
88}