pub(crate) use crate::commands::navigator::{DOMAIN, to_pretty_json};
use crate::commands::navigator::{Gradient, Layer, NavTechnique, Versions};
use super::analysis::{VisibilityAnalysis, level_name};
const MAX_SCORE: u64 = 4;
pub(crate) fn build_layer(analysis: &VisibilityAnalysis, name: &str) -> Layer {
let techniques: Vec<NavTechnique> = analysis
.techniques
.iter()
.map(|t| NavTechnique {
technique_id: t.technique_id.clone(),
score: u64::from(t.score),
comment: format!(
"{} visibility via {}",
level_name(t.score),
t.data_sources.join(", ")
),
enabled: true,
show_subtechniques: false,
})
.collect();
Layer {
name: name.to_string(),
versions: Versions::current(),
domain: DOMAIN,
description: format!(
"Telemetry visibility generated by rsigma; score = DeTT&CT visibility level 0-4 per technique ({} techniques).",
techniques.len()
),
sorting: 3, hide_disabled: false,
gradient: Gradient {
colors: vec!["#ff6666", "#ffe766", "#8ec843"],
min_value: 0,
max_value: MAX_SCORE,
},
techniques,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::commands::visibility::analysis::{TechniqueVisibility, VisibilityAnalysis};
fn analysis_with(techniques: Vec<TechniqueVisibility>) -> VisibilityAnalysis {
VisibilityAnalysis {
data_sources: vec![],
techniques,
untapped: vec![],
unmapped_logsources: vec![],
rules_total: 0,
logsources_total: 0,
events_observed: 0,
observed_unique_keys: 0,
has_observed: true,
}
}
#[test]
fn layer_scores_by_visibility_level_and_pins_format() {
let a = analysis_with(vec![TechniqueVisibility {
technique_id: "T1059".into(),
score: 3,
data_sources: vec!["Process".into(), "Script".into()],
}]);
let layer = build_layer(&a, "test");
let json = to_pretty_json(&layer);
assert!(json.contains("\"layer\": \"4.5\""));
assert!(json.contains("\"techniqueID\": \"T1059\""));
assert!(json.contains("\"score\": 3"));
assert_eq!(layer.gradient.max_value, 4);
}
}