use crate::policy::ir::PolicyDecision;
use crate::policy::sandbox_types::SandboxPolicy;
use crate::style;
pub fn format_tool_header(
title: &str,
tool_name: &str,
arguments: &serde_json::Value,
) -> Vec<String> {
vec![
style::bold(title).to_string(),
format!(" {} {}", style::cyan("tool:"), tool_name),
format!(" {} {}", style::cyan("arguments:"), arguments),
]
}
pub fn format_decision(decision: &PolicyDecision) -> Vec<String> {
let mut lines = Vec::new();
lines.push(format!(
"{} {}",
style::bold("Decision:"),
style::effect(&decision.effect.to_string())
));
if let Some(ref reason) = decision.reason {
lines.push(format!("{} {}", style::bold("Reason: "), reason));
}
lines.push(String::new());
if !decision.trace.matched_rules.is_empty() {
lines.push(style::header("Matched rules:").to_string());
for m in &decision.trace.matched_rules {
let eff = style::effect(&m.effect.to_string());
lines.push(format!(" [{}] {} -> {}", m.rule_index, m.description, eff));
}
lines.push(String::new());
}
if !decision.trace.skipped_rules.is_empty() {
lines.push(style::dim("Skipped rules:").to_string());
for s in &decision.trace.skipped_rules {
lines.push(format!(
" {} {} {}",
style::dim(&format!("[{}]", s.rule_index)),
style::dim(&s.description),
style::dim(&format!("({})", s.reason))
));
}
lines.push(String::new());
}
lines.push(format!(
"{} {}",
style::bold("Resolution:"),
style::effect(&decision.trace.final_resolution)
));
lines
}
pub fn format_sandbox_summary(sandbox: &SandboxPolicy) -> Vec<String> {
let mut lines = Vec::new();
lines.push(format!(
" {}: {}",
style::cyan("default"),
sandbox.default.short()
));
lines.push(format!(
" {}: {:?}",
style::cyan("network"),
sandbox.network
));
for rule in &sandbox.rules {
lines.push(format!(
" {:?} {} in {}",
rule.effect,
rule.caps.short(),
rule.path
));
}
lines
}
pub fn decision_to_json(decision: &PolicyDecision) -> serde_json::Value {
serde_json::json!({
"effect": format!("{}", decision.effect),
"reason": decision.reason,
"matched_rules": decision.trace.matched_rules.iter().map(|m| {
serde_json::json!({
"rule_index": m.rule_index,
"description": m.description,
"effect": format!("{}", m.effect),
})
}).collect::<Vec<_>>(),
"skipped_rules": decision.trace.skipped_rules.iter().map(|s| {
serde_json::json!({
"rule_index": s.rule_index,
"description": s.description,
"reason": s.reason,
})
}).collect::<Vec<_>>(),
"resolution": decision.trace.final_resolution,
"sandbox": decision.sandbox.as_ref().map(|s| serde_json::to_value(s).ok()),
})
}
pub fn colorize_effect_prefix(text: &str) -> String {
if let Some(rest) = text.strip_prefix("allow") {
format!("{}{}", style::green("allow"), rest)
} else if let Some(rest) = text.strip_prefix("deny") {
format!("{}{}", style::red("deny"), rest)
} else if let Some(rest) = text.strip_prefix("ask") {
format!("{}{}", style::yellow("ask"), rest)
} else {
text.to_string()
}
}