#[doc(hidden)]
pub mod case_construct;
#[doc(hidden)]
pub mod compound;
#[doc(hidden)]
pub mod heredoc;
#[doc(hidden)]
pub mod lexer;
#[doc(hidden)]
pub mod mode;
#[doc(hidden)]
pub mod powershell;
#[doc(hidden)]
pub mod ps_statements;
#[doc(hidden)]
pub mod tokenizer;
use std::sync::OnceLock;
use crate::core::error::ShellError;
pub use case_construct::{contains_double_semicolon, find_shell_word, rewrite_case_constructs};
pub use compound::expand_to_leaf_segments;
pub use heredoc::{
heredoc_delims, read_heredoc_delim, strip_all_heredoc_bodies, strip_for_analysis,
};
pub use mode::ShellSecurity;
pub use ps_statements::{Dialect, with_dialect};
pub use tokenizer::*;
pub const MAX_VALIDATED_COMMAND_BYTES: usize = 64 * 1024;
#[derive(Clone, Copy)]
pub struct ShellGate {
pub check: fn(&str) -> Result<(), ShellError>,
pub warn_only_reason: fn(&str) -> String,
}
static GATE: OnceLock<ShellGate> = OnceLock::new();
#[must_use]
pub fn register_gate(gate: ShellGate) -> bool {
GATE.set(gate).is_ok()
}
#[cfg(any(test, feature = "test-support"))]
#[doc(hidden)]
pub fn install_test_gate() {
fn check(command: &str) -> Result<(), ShellError> {
let Ok(list) = std::env::var("LEAN_CTX_SHELL_ALLOWLIST_OVERRIDE") else {
return Ok(());
};
let first = command.split_whitespace().next().unwrap_or_default();
if list.split(',').any(|entry| entry.trim() == first) {
Ok(())
} else {
Err(ShellError::from(format!(
"test gate: '{first}' is not in the override allowlist"
)))
}
}
let _ = GATE.set(ShellGate {
check,
warn_only_reason: str::to_string,
});
}
pub fn gate_registered() -> bool {
GATE.get().is_some()
}
pub fn check_registered(command: &str) -> Result<(), ShellError> {
match GATE.get() {
Some(gate) => (gate.check)(command),
None => Err(ShellError::from(
"lean-ctx: no shell-allowlist policy is registered in this build; \
refusing to run the command (fail closed)",
)),
}
}
pub fn warn_only_reason_registered(message: &str) -> String {
match GATE.get() {
Some(gate) => (gate.warn_only_reason)(message),
None => message.to_string(),
}
}