use crate::behavior::{BehaviorMode, BehaviorPack};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BehaviorPackDiff {
pub metadata: DiffMetadata,
pub capability_changes: CapabilityChanges,
pub parameter_changes: Vec<ParameterChange>,
pub guard_changes: GuardChanges,
pub risk_analysis: RiskAnalysis,
pub summary: DiffSummary,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiffMetadata {
pub from_pack: String,
pub to_pack: String,
pub from_mode: BehaviorMode,
pub to_mode: BehaviorMode,
pub timestamp: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityChanges {
pub atoms_enabled: Vec<String>,
pub atoms_disabled: Vec<String>,
pub macros_enabled: Vec<String>,
pub macros_disabled: Vec<String>,
pub playbooks_enabled: Vec<String>,
pub playbooks_disabled: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParameterChange {
pub capability: String,
pub key: String,
pub old_value: Option<serde_json::Value>,
pub new_value: Option<serde_json::Value>,
pub change_type: ParameterChangeType,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ParameterChangeType {
Added,
Removed,
Modified,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GuardChanges {
pub atom_guards: Vec<GuardChange>,
pub macro_guards: Vec<GuardChange>,
pub playbook_guards: Vec<GuardChange>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GuardChange {
pub setting: String,
pub old_value: serde_json::Value,
pub new_value: serde_json::Value,
pub impact: GuardImpact,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum GuardImpact {
Restrictive,
Permissive,
Neutral,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskAnalysis {
pub overall_risk: RiskLevel,
pub security_scope: ScopeRiskAnalysis,
pub resource_limits: ResourceRiskAnalysis,
pub mode_risk: ModeRiskAnalysis,
pub scope_expansion_detected: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub enum RiskLevel {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScopeRiskAnalysis {
pub filesystem_changes: Vec<String>,
pub network_changes: Vec<String>,
pub risk_level: RiskLevel,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceRiskAnalysis {
pub memory_limit_changes: Vec<String>,
pub time_limit_changes: Vec<String>,
pub risk_level: RiskLevel,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModeRiskAnalysis {
pub risk_level: RiskLevel,
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiffSummary {
pub total_capability_changes: usize,
pub total_parameter_changes: usize,
pub total_guard_changes: usize,
pub requires_review: bool,
pub description: String,
}
impl BehaviorPackDiff {
pub fn compare(from: &BehaviorPack, to: &BehaviorPack) -> Result<Self> {
let metadata = DiffMetadata {
from_pack: from.name.clone(),
to_pack: to.name.clone(),
from_mode: from.mode.clone(),
to_mode: to.mode.clone(),
timestamp: chrono::Utc::now().to_rfc3339(),
};
let capability_changes = Self::analyze_capability_changes(from, to);
let parameter_changes = Self::analyze_parameter_changes(from, to);
let guard_changes = Self::analyze_guard_changes(from, to);
let risk_analysis = Self::analyze_risk(
&capability_changes,
¶meter_changes,
&guard_changes,
&metadata,
);
let summary = Self::generate_summary(
&capability_changes,
¶meter_changes,
&guard_changes,
&risk_analysis,
);
Ok(Self {
metadata,
capability_changes,
parameter_changes,
guard_changes,
risk_analysis,
summary,
})
}
fn analyze_capability_changes(from: &BehaviorPack, to: &BehaviorPack) -> CapabilityChanges {
let from_atoms: HashSet<_> = from.enable.atoms.iter().cloned().collect();
let to_atoms: HashSet<_> = to.enable.atoms.iter().cloned().collect();
let from_macros: HashSet<_> = from.enable.macros.iter().cloned().collect();
let to_macros: HashSet<_> = to.enable.macros.iter().cloned().collect();
let from_playbooks: HashSet<_> = from.enable.playbooks.iter().cloned().collect();
let to_playbooks: HashSet<_> = to.enable.playbooks.iter().cloned().collect();
CapabilityChanges {
atoms_enabled: to_atoms.difference(&from_atoms).cloned().collect(),
atoms_disabled: from_atoms.difference(&to_atoms).cloned().collect(),
macros_enabled: to_macros.difference(&from_macros).cloned().collect(),
macros_disabled: from_macros.difference(&to_macros).cloned().collect(),
playbooks_enabled: to_playbooks.difference(&from_playbooks).cloned().collect(),
playbooks_disabled: from_playbooks.difference(&to_playbooks).cloned().collect(),
}
}
fn analyze_parameter_changes(from: &BehaviorPack, to: &BehaviorPack) -> Vec<ParameterChange> {
let mut changes = Vec::new();
let mut all_capabilities = HashSet::new();
all_capabilities.extend(from.params.keys());
all_capabilities.extend(to.params.keys());
for capability in all_capabilities {
let from_params = from.params.get(capability);
let to_params = to.params.get(capability);
match (from_params, to_params) {
(None, Some(to_val)) => {
if let Some(obj) = to_val.as_object() {
for (key, value) in obj {
changes.push(ParameterChange {
capability: capability.clone(),
key: key.clone(),
old_value: None,
new_value: Some(value.clone()),
change_type: ParameterChangeType::Added,
});
}
}
}
(Some(_from_val), None) => {
changes.push(ParameterChange {
capability: capability.clone(),
key: "*".to_string(), old_value: from_params.cloned(),
new_value: None,
change_type: ParameterChangeType::Removed,
});
}
(Some(from_val), Some(to_val)) => {
Self::compare_param_objects(capability, from_val, to_val, &mut changes);
}
(None, None) => {
}
}
}
changes
}
fn compare_param_objects(
capability: &str,
from_val: &serde_json::Value,
to_val: &serde_json::Value,
changes: &mut Vec<ParameterChange>,
) {
if let (Some(from_obj), Some(to_obj)) = (from_val.as_object(), to_val.as_object()) {
let mut all_keys = HashSet::new();
all_keys.extend(from_obj.keys());
all_keys.extend(to_obj.keys());
for key in all_keys {
let from_key_val = from_obj.get(key);
let to_key_val = to_obj.get(key);
match (from_key_val, to_key_val) {
(None, Some(new_val)) => {
changes.push(ParameterChange {
capability: capability.to_string(),
key: key.clone(),
old_value: None,
new_value: Some(new_val.clone()),
change_type: ParameterChangeType::Added,
});
}
(Some(old_val), None) => {
changes.push(ParameterChange {
capability: capability.to_string(),
key: key.clone(),
old_value: Some(old_val.clone()),
new_value: None,
change_type: ParameterChangeType::Removed,
});
}
(Some(old_val), Some(new_val)) => {
if old_val != new_val {
changes.push(ParameterChange {
capability: capability.to_string(),
key: key.clone(),
old_value: Some(old_val.clone()),
new_value: Some(new_val.clone()),
change_type: ParameterChangeType::Modified,
});
}
}
(None, None) => {
}
}
}
}
}
fn analyze_guard_changes(from: &BehaviorPack, to: &BehaviorPack) -> GuardChanges {
let mut guard_changes = GuardChanges {
atom_guards: Vec::new(),
macro_guards: Vec::new(),
playbook_guards: Vec::new(),
};
if let (Some(from_atom_guards), Some(to_atom_guards)) =
(&from.guards.atoms, &to.guards.atoms)
{
if from_atom_guards.default_max_bytes != to_atom_guards.default_max_bytes {
guard_changes.atom_guards.push(GuardChange {
setting: "default_max_bytes".to_string(),
old_value: serde_json::json!(from_atom_guards.default_max_bytes),
new_value: serde_json::json!(to_atom_guards.default_max_bytes),
impact: if to_atom_guards.default_max_bytes > from_atom_guards.default_max_bytes
{
GuardImpact::Permissive
} else {
GuardImpact::Restrictive
},
});
}
if from_atom_guards.require_justification != to_atom_guards.require_justification {
guard_changes.atom_guards.push(GuardChange {
setting: "require_justification".to_string(),
old_value: serde_json::json!(from_atom_guards.require_justification),
new_value: serde_json::json!(to_atom_guards.require_justification),
impact: if to_atom_guards.require_justification {
GuardImpact::Restrictive
} else {
GuardImpact::Permissive
},
});
}
}
if let (Some(from_pb_guards), Some(to_pb_guards)) =
(&from.guards.playbooks, &to.guards.playbooks)
{
if from_pb_guards.max_steps != to_pb_guards.max_steps {
guard_changes.playbook_guards.push(GuardChange {
setting: "max_steps".to_string(),
old_value: serde_json::json!(from_pb_guards.max_steps),
new_value: serde_json::json!(to_pb_guards.max_steps),
impact: if to_pb_guards.max_steps > from_pb_guards.max_steps {
GuardImpact::Permissive
} else {
GuardImpact::Restrictive
},
});
}
}
guard_changes
}
fn analyze_risk(
cap_changes: &CapabilityChanges,
param_changes: &[ParameterChange],
guard_changes: &GuardChanges,
metadata: &DiffMetadata,
) -> RiskAnalysis {
let mut risk_factors = Vec::new();
let mode_risk = Self::assess_mode_risk(&metadata.from_mode, &metadata.to_mode);
risk_factors.push(mode_risk.risk_level.clone());
let total_new_capabilities = cap_changes.atoms_enabled.len()
+ cap_changes.macros_enabled.len()
+ cap_changes.playbooks_enabled.len();
if total_new_capabilities > 0 {
risk_factors.push(match total_new_capabilities {
1..=2 => RiskLevel::Low,
3..=5 => RiskLevel::Medium,
_ => RiskLevel::High,
});
}
let scope_expansion_detected = Self::detect_silent_scope_expansion(param_changes);
if scope_expansion_detected {
risk_factors.push(RiskLevel::Critical);
}
let permissive_guard_changes = guard_changes
.atom_guards
.iter()
.chain(guard_changes.macro_guards.iter())
.chain(guard_changes.playbook_guards.iter())
.filter(|change| matches!(change.impact, GuardImpact::Permissive))
.count();
if permissive_guard_changes > 0 {
risk_factors.push(RiskLevel::Medium);
}
let overall_risk = risk_factors.into_iter().max().unwrap_or(RiskLevel::Low);
RiskAnalysis {
overall_risk,
security_scope: ScopeRiskAnalysis {
filesystem_changes: Self::extract_filesystem_changes(param_changes),
network_changes: Self::extract_network_changes(param_changes),
risk_level: if scope_expansion_detected {
RiskLevel::High
} else {
RiskLevel::Low
},
},
resource_limits: ResourceRiskAnalysis {
memory_limit_changes: Self::extract_memory_changes(param_changes, guard_changes),
time_limit_changes: Self::extract_time_changes(param_changes),
risk_level: RiskLevel::Low, },
mode_risk,
scope_expansion_detected,
}
}
fn assess_mode_risk(from_mode: &BehaviorMode, to_mode: &BehaviorMode) -> ModeRiskAnalysis {
use BehaviorMode::*;
match (from_mode, to_mode) {
(Strict, Explore) => ModeRiskAnalysis {
risk_level: RiskLevel::Medium,
description: "Transition from strict to explore mode enables direct atom usage"
.to_string(),
},
(Strict, Shadow) => ModeRiskAnalysis {
risk_level: RiskLevel::Low,
description: "Transition to shadow mode - no actual execution".to_string(),
},
(Explore, Strict) => ModeRiskAnalysis {
risk_level: RiskLevel::Low,
description: "Transition to strict mode - more restrictive".to_string(),
},
(Explore, Shadow) => ModeRiskAnalysis {
risk_level: RiskLevel::Low,
description: "Transition to shadow mode - no actual execution".to_string(),
},
(Shadow, Strict) => ModeRiskAnalysis {
risk_level: RiskLevel::Low,
description: "Transition from shadow to strict mode".to_string(),
},
(Shadow, Explore) => ModeRiskAnalysis {
risk_level: RiskLevel::Medium,
description: "Transition from shadow to explore mode - enables execution"
.to_string(),
},
(Strict, Strict) | (Explore, Explore) | (Shadow, Shadow) => ModeRiskAnalysis {
risk_level: RiskLevel::Low,
description: "No mode change".to_string(),
},
}
}
fn detect_silent_scope_expansion(param_changes: &[ParameterChange]) -> bool {
for change in param_changes {
match &change.key as &str {
"hosts" => {
if let (Some(old_val), Some(new_val)) = (&change.old_value, &change.new_value) {
if Self::array_expanded(old_val, new_val) {
return true;
}
}
}
"paths" | "allowed_paths" => {
if let (Some(old_val), Some(new_val)) = (&change.old_value, &change.new_value) {
if Self::array_expanded(old_val, new_val) {
return true;
}
}
}
"max_bytes" | "timeout_ms" => {
if let (Some(old_val), Some(new_val)) = (&change.old_value, &change.new_value) {
if Self::numeric_increased(old_val, new_val) {
return true;
}
}
}
_ => {}
}
}
false
}
fn array_expanded(old_val: &serde_json::Value, new_val: &serde_json::Value) -> bool {
if let (Some(old_arr), Some(new_arr)) = (old_val.as_array(), new_val.as_array()) {
new_arr.len() > old_arr.len()
} else {
false
}
}
fn numeric_increased(old_val: &serde_json::Value, new_val: &serde_json::Value) -> bool {
if let (Some(old_num), Some(new_num)) = (old_val.as_f64(), new_val.as_f64()) {
new_num > old_num
} else {
false
}
}
fn extract_filesystem_changes(param_changes: &[ParameterChange]) -> Vec<String> {
param_changes
.iter()
.filter(|change| matches!(change.key.as_str(), "paths" | "allowed_paths" | "max_bytes"))
.map(|change| {
format!(
"{}.{}: {:?} → {:?}",
change.capability, change.key, change.old_value, change.new_value
)
})
.collect()
}
fn extract_network_changes(param_changes: &[ParameterChange]) -> Vec<String> {
param_changes
.iter()
.filter(|change| matches!(change.key.as_str(), "hosts" | "timeout_ms"))
.map(|change| {
format!(
"{}.{}: {:?} → {:?}",
change.capability, change.key, change.old_value, change.new_value
)
})
.collect()
}
fn extract_memory_changes(
param_changes: &[ParameterChange],
guard_changes: &GuardChanges,
) -> Vec<String> {
let mut changes = Vec::new();
for change in param_changes {
if change.key.contains("memory") || change.key.contains("max_bytes") {
changes.push(format!(
"{}.{}: {:?} → {:?}",
change.capability, change.key, change.old_value, change.new_value
));
}
}
for guard_change in &guard_changes.atom_guards {
if guard_change.setting == "default_max_bytes" {
changes.push(format!(
"guards.atoms.{}: {:?} → {:?}",
guard_change.setting, guard_change.old_value, guard_change.new_value
));
}
}
changes
}
fn extract_time_changes(param_changes: &[ParameterChange]) -> Vec<String> {
param_changes
.iter()
.filter(|change| change.key.contains("timeout") || change.key.contains("duration"))
.map(|change| {
format!(
"{}.{}: {:?} → {:?}",
change.capability, change.key, change.old_value, change.new_value
)
})
.collect()
}
fn generate_summary(
cap_changes: &CapabilityChanges,
param_changes: &[ParameterChange],
guard_changes: &GuardChanges,
risk_analysis: &RiskAnalysis,
) -> DiffSummary {
let total_capability_changes = cap_changes.atoms_enabled.len()
+ cap_changes.atoms_disabled.len()
+ cap_changes.macros_enabled.len()
+ cap_changes.macros_disabled.len()
+ cap_changes.playbooks_enabled.len()
+ cap_changes.playbooks_disabled.len();
let total_guard_changes = guard_changes.atom_guards.len()
+ guard_changes.macro_guards.len()
+ guard_changes.playbook_guards.len();
let requires_review = matches!(
risk_analysis.overall_risk,
RiskLevel::High | RiskLevel::Critical
) || risk_analysis.scope_expansion_detected
|| total_capability_changes > 5;
let description = Self::generate_description(
total_capability_changes,
param_changes.len(),
total_guard_changes,
&risk_analysis.overall_risk,
);
DiffSummary {
total_capability_changes,
total_parameter_changes: param_changes.len(),
total_guard_changes,
requires_review,
description,
}
}
fn generate_description(
cap_changes: usize,
param_changes: usize,
guard_changes: usize,
risk_level: &RiskLevel,
) -> String {
let mut parts = Vec::new();
if cap_changes > 0 {
parts.push(format!("{} capability changes", cap_changes));
}
if param_changes > 0 {
parts.push(format!("{} parameter changes", param_changes));
}
if guard_changes > 0 {
parts.push(format!("{} guard changes", guard_changes));
}
let changes_desc = if parts.is_empty() {
"No changes detected".to_string()
} else {
parts.join(", ")
};
let risk_desc = match risk_level {
RiskLevel::Low => "low risk",
RiskLevel::Medium => "medium risk",
RiskLevel::High => "high risk",
RiskLevel::Critical => "CRITICAL risk",
};
format!("{} ({})", changes_desc, risk_desc)
}
pub fn to_report(&self) -> String {
let mut report = String::new();
report.push_str("# Behavior Pack Diff Report\n\n");
report.push_str(&format!(
"**From:** {} ({})\n",
self.metadata.from_pack,
format!("{:?}", self.metadata.from_mode).to_lowercase()
));
report.push_str(&format!(
"**To:** {} ({})\n",
self.metadata.to_pack,
format!("{:?}", self.metadata.to_mode).to_lowercase()
));
report.push_str(&format!("**Timestamp:** {}\n\n", self.metadata.timestamp));
report.push_str("## Summary\n\n");
report.push_str(&format!("{}\n\n", self.summary.description));
report.push_str(&format!(
"**Total Changes:** {} capabilities, {} parameters, {} guards\n\n",
self.summary.total_capability_changes,
self.summary.total_parameter_changes,
self.summary.total_guard_changes
));
if self.summary.requires_review {
report.push_str("⚠️ **Manual review required**\n\n");
}
report.push_str("## Risk Analysis\n\n");
report.push_str(&format!(
"**Risk Level:** {:?}\n",
self.risk_analysis.overall_risk
));
if self.risk_analysis.scope_expansion_detected {
report.push_str("🚨 **Silent scope expansion detected**\n");
}
report.push_str(&format!(
"**Mode Change Risk:** {:?} - {}\n\n",
self.risk_analysis.mode_risk.risk_level, self.risk_analysis.mode_risk.description
));
if self.summary.total_capability_changes > 0 {
report.push_str("## Capability Changes\n\n");
if !self.capability_changes.atoms_enabled.is_empty() {
report.push_str(&format!(
"**Atoms Enabled:** {}\n",
self.capability_changes.atoms_enabled.join(", ")
));
}
if !self.capability_changes.atoms_disabled.is_empty() {
report.push_str(&format!(
"**Atoms Disabled:** {}\n",
self.capability_changes.atoms_disabled.join(", ")
));
}
if !self.capability_changes.macros_enabled.is_empty() {
report.push_str(&format!(
"**Macros Enabled:** {}\n",
self.capability_changes.macros_enabled.join(", ")
));
}
if !self.capability_changes.macros_disabled.is_empty() {
report.push_str(&format!(
"**Macros Disabled:** {}\n",
self.capability_changes.macros_disabled.join(", ")
));
}
if !self.capability_changes.playbooks_enabled.is_empty() {
report.push_str(&format!(
"**Playbooks Enabled:** {}\n",
self.capability_changes.playbooks_enabled.join(", ")
));
}
if !self.capability_changes.playbooks_disabled.is_empty() {
report.push_str(&format!(
"**Playbooks Disabled:** {}\n",
self.capability_changes.playbooks_disabled.join(", ")
));
}
report.push('\n');
}
if !self.parameter_changes.is_empty() {
report.push_str("## Parameter Changes\n\n");
for change in &self.parameter_changes {
let change_symbol = match change.change_type {
ParameterChangeType::Added => "➕",
ParameterChangeType::Removed => "➖",
ParameterChangeType::Modified => "🔄",
};
report.push_str(&format!(
"{} **{}.{}:** {:?} → {:?}\n",
change_symbol,
change.capability,
change.key,
change.old_value,
change.new_value
));
}
report.push('\n');
}
if self.summary.total_guard_changes > 0 {
report.push_str("## Guard Changes\n\n");
for change in &self.guard_changes.atom_guards {
let impact_symbol = match change.impact {
GuardImpact::Restrictive => "🔒",
GuardImpact::Permissive => "🔓",
GuardImpact::Neutral => "⚖️",
};
report.push_str(&format!(
"{} **atoms.{}:** {:?} → {:?}\n",
impact_symbol, change.setting, change.old_value, change.new_value
));
}
report.push('\n');
}
report
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::behavior::{EnabledCapabilities, GuardConfig};
use std::collections::HashMap;
#[test]
fn test_behavior_pack_diff_no_changes() {
let pack1 = BehaviorPack {
name: "test-pack".to_string(),
mode: BehaviorMode::Strict,
enable: EnabledCapabilities::default(),
params: HashMap::new(),
guards: GuardConfig::default(),
};
let pack2 = pack1.clone();
let diff = BehaviorPackDiff::compare(&pack1, &pack2).unwrap();
assert_eq!(diff.summary.total_capability_changes, 0);
assert_eq!(diff.summary.total_parameter_changes, 0);
assert!(matches!(diff.risk_analysis.overall_risk, RiskLevel::Low));
}
#[test]
fn test_capability_changes_detection() {
let pack1 = BehaviorPack {
name: "pack1".to_string(),
mode: BehaviorMode::Strict,
enable: EnabledCapabilities {
atoms: vec!["atom1".to_string()],
macros: vec!["macro1".to_string()],
playbooks: vec!["playbook1".to_string()],
},
params: HashMap::new(),
guards: GuardConfig::default(),
};
let pack2 = BehaviorPack {
name: "pack2".to_string(),
mode: BehaviorMode::Strict,
enable: EnabledCapabilities {
atoms: vec!["atom1".to_string(), "atom2".to_string()],
macros: vec!["macro2".to_string()],
playbooks: vec!["playbook1".to_string()],
},
params: HashMap::new(),
guards: GuardConfig::default(),
};
let diff = BehaviorPackDiff::compare(&pack1, &pack2).unwrap();
assert_eq!(diff.capability_changes.atoms_enabled, vec!["atom2"]);
assert_eq!(diff.capability_changes.macros_enabled, vec!["macro2"]);
assert_eq!(diff.capability_changes.macros_disabled, vec!["macro1"]);
assert!(diff.summary.total_capability_changes > 0);
}
#[test]
fn test_scope_expansion_detection() {
let mut params1 = HashMap::new();
params1.insert(
"http.fetch.v1".to_string(),
serde_json::json!({
"hosts": ["api.example.com"]
}),
);
let mut params2 = HashMap::new();
params2.insert(
"http.fetch.v1".to_string(),
serde_json::json!({
"hosts": ["api.example.com", "untrusted.com"]
}),
);
let pack1 = BehaviorPack {
name: "pack1".to_string(),
mode: BehaviorMode::Strict,
enable: EnabledCapabilities::default(),
params: params1,
guards: GuardConfig::default(),
};
let pack2 = BehaviorPack {
name: "pack2".to_string(),
mode: BehaviorMode::Strict,
enable: EnabledCapabilities::default(),
params: params2,
guards: GuardConfig::default(),
};
let diff = BehaviorPackDiff::compare(&pack1, &pack2).unwrap();
assert!(diff.risk_analysis.scope_expansion_detected);
assert!(matches!(
diff.risk_analysis.overall_risk,
RiskLevel::Critical
));
}
}