#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ShellSecurity {
#[default]
Enforce,
Warn,
Off,
}
impl ShellSecurity {
pub fn parse(value: &str) -> Option<Self> {
match value.trim().to_ascii_lowercase().as_str() {
"enforce" | "block" | "strict" | "on" => Some(Self::Enforce),
"warn" | "warn-only" | "warn_only" => Some(Self::Warn),
"off" | "disabled" | "none" | "yolo" => Some(Self::Off),
_ => None,
}
}
pub fn as_str(self) -> &'static str {
match self {
Self::Enforce => "enforce",
Self::Warn => "warn",
Self::Off => "off",
}
}
pub fn resolve() -> Self {
if let Ok(raw) = std::env::var("LEAN_CTX_SHELL_SECURITY")
&& let Some(mode) = Self::parse(&raw)
{
return mode;
}
crate::core::config::Config::load()
.shell_security
.as_deref()
.and_then(Self::parse)
.unwrap_or_default()
}
}
impl std::fmt::Display for ShellSecurity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_accepts_canonical_and_aliases() {
assert_eq!(
ShellSecurity::parse("enforce"),
Some(ShellSecurity::Enforce)
);
assert_eq!(ShellSecurity::parse("ON"), Some(ShellSecurity::Enforce));
assert_eq!(ShellSecurity::parse(" Warn "), Some(ShellSecurity::Warn));
assert_eq!(ShellSecurity::parse("off"), Some(ShellSecurity::Off));
assert_eq!(ShellSecurity::parse("yolo"), Some(ShellSecurity::Off));
}
#[test]
fn parse_rejects_unknown_so_caller_can_default() {
assert_eq!(ShellSecurity::parse("loose"), None);
assert_eq!(ShellSecurity::parse(""), None);
}
#[test]
fn default_is_enforce() {
assert_eq!(ShellSecurity::default(), ShellSecurity::Enforce);
}
#[test]
fn as_str_roundtrips_through_parse() {
for mode in [
ShellSecurity::Enforce,
ShellSecurity::Warn,
ShellSecurity::Off,
] {
assert_eq!(ShellSecurity::parse(mode.as_str()), Some(mode));
}
}
#[test]
fn env_override_takes_precedence_over_config() {
let _lock = crate::core::data_dir::test_env_lock();
crate::test_env::set_var("LEAN_CTX_SHELL_SECURITY", "off");
assert_eq!(ShellSecurity::resolve(), ShellSecurity::Off);
crate::test_env::set_var("LEAN_CTX_SHELL_SECURITY", "garbage");
let resolved = ShellSecurity::resolve();
assert!(matches!(
resolved,
ShellSecurity::Enforce | ShellSecurity::Warn | ShellSecurity::Off
));
crate::test_env::remove_var("LEAN_CTX_SHELL_SECURITY");
}
}