use crate::capability_ext::CapabilitySetExt;
use crate::cli::{SandboxArgs, WhyArgs, WhyOp};
use crate::{policy, profile, query_ext, sandbox_state};
use nono::{AccessMode, CapabilitySet, NonoError, Result};
pub(crate) fn run_why(args: WhyArgs) -> Result<()> {
use query_ext::{print_result, query_network, query_path, QueryResult};
use sandbox_state::load_sandbox_state;
let (caps, overridden_paths): (CapabilitySet, Vec<std::path::PathBuf>) = if args.self_query {
match load_sandbox_state() {
Some(state) => {
let paths = state.override_deny_as_paths();
(state.to_caps()?, paths)
}
None => {
let result = QueryResult::NotSandboxed {
message: "Not running inside a nono sandbox".to_string(),
};
if args.json {
let json = serde_json::to_string_pretty(&result).map_err(|e| {
NonoError::ConfigParse(format!("JSON serialization failed: {}", e))
})?;
println!("{}", json);
} else {
print_result(&result);
}
return Ok(());
}
}
} else if let Some(ref profile_name) = args.profile {
let profile = profile::load_profile(profile_name)?;
let workdir = args
.workdir
.clone()
.or_else(|| std::env::current_dir().ok())
.unwrap_or_else(|| std::path::PathBuf::from("."));
let sandbox_args = SandboxArgs {
allow: args.allow.clone(),
read: args.read.clone(),
write: args.write.clone(),
allow_file: args.allow_file.clone(),
read_file: args.read_file.clone(),
write_file: args.write_file.clone(),
block_net: args.block_net,
workdir: args.workdir.clone(),
..SandboxArgs::default()
};
let mut override_paths = Vec::new();
for tmpl in &profile.policy.override_deny {
let expanded = profile::expand_vars(tmpl, &workdir)?;
if expanded.exists() {
if let Ok(canonical) = expanded.canonicalize() {
override_paths.push(canonical);
}
} else {
override_paths.push(expanded);
}
}
let (mut caps, needs_unlink) =
CapabilitySet::from_profile(&profile, &workdir, &sandbox_args)?;
if needs_unlink {
policy::apply_unlink_overrides(&mut caps);
}
(caps, override_paths)
} else {
let sandbox_args = SandboxArgs {
allow: args.allow.clone(),
read: args.read.clone(),
write: args.write.clone(),
allow_file: args.allow_file.clone(),
read_file: args.read_file.clone(),
write_file: args.write_file.clone(),
block_net: args.block_net,
workdir: args.workdir.clone(),
..SandboxArgs::default()
};
let (mut caps, needs_unlink) = CapabilitySet::from_args(&sandbox_args)?;
if needs_unlink {
policy::apply_unlink_overrides(&mut caps);
}
(caps, vec![])
};
let result = if let Some(ref path) = args.path {
let op = match args.op {
Some(WhyOp::Read) => AccessMode::Read,
Some(WhyOp::Write) => AccessMode::Write,
Some(WhyOp::ReadWrite) => AccessMode::ReadWrite,
None => AccessMode::Read,
};
query_path(path, op, &caps, &overridden_paths)?
} else if let Some(ref host) = args.host {
query_network(host, args.port, &caps)
} else {
return Err(NonoError::ConfigParse(
"--path or --host is required".to_string(),
));
};
if args.json {
let json = serde_json::to_string_pretty(&result)
.map_err(|e| NonoError::ConfigParse(format!("JSON serialization failed: {}", e)))?;
println!("{}", json);
} else {
print_result(&result);
}
Ok(())
}