pub fn is_standalone_tracevault_subcommand(command: &str, subcommand: &str) -> bool {
let trimmed = command.trim();
const COMPOUND_MARKERS: &[&str] = &[
";", "|", "&", "\n", "\r", "\u{0B}", "\u{0C}", "`", "$(", ">", "<",
];
if COMPOUND_MARKERS.iter().any(|m| trimmed.contains(m)) {
return false;
}
let mut tokens = trimmed.split_whitespace();
let Some(arg0) = tokens.next() else {
return false;
};
if arg0.contains('=') {
return false;
}
let prog = arg0.rsplit(['/', '\\']).next().unwrap_or(arg0);
if prog != "tracevault" {
return false;
}
tokens.next() == Some(subcommand)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matches_bare_invocation() {
assert!(is_standalone_tracevault_subcommand(
"tracevault verify-start",
"verify-start"
));
}
#[test]
fn matches_with_path_and_flags() {
assert!(is_standalone_tracevault_subcommand(
"/usr/local/bin/tracevault verify-start --session-id abc",
"verify-start"
));
}
#[test]
fn matches_with_surrounding_whitespace() {
assert!(is_standalone_tracevault_subcommand(
" tracevault verify-start ",
"verify-start"
));
}
#[test]
fn rejects_compound_with_and() {
assert!(!is_standalone_tracevault_subcommand(
"tracevault verify-start && rm -rf /",
"verify-start"
));
}
#[test]
fn rejects_compound_with_semicolon_and_pipe() {
assert!(!is_standalone_tracevault_subcommand(
"tracevault verify-start; evil",
"verify-start"
));
assert!(!is_standalone_tracevault_subcommand(
"tracevault verify-start | tee log",
"verify-start"
));
}
#[test]
fn rejects_command_substitution_and_background() {
assert!(!is_standalone_tracevault_subcommand(
"tracevault verify-start $(evil)",
"verify-start"
));
assert!(!is_standalone_tracevault_subcommand(
"tracevault verify-start &",
"verify-start"
));
assert!(!is_standalone_tracevault_subcommand(
"tracevault verify-start `evil`",
"verify-start"
));
}
#[test]
fn rejects_disguised_argv0() {
assert!(!is_standalone_tracevault_subcommand(
"echo tracevault verify-start",
"verify-start"
));
}
#[test]
fn rejects_wrong_subcommand() {
assert!(!is_standalone_tracevault_subcommand(
"tracevault check",
"verify-start"
));
}
#[test]
fn rejects_newline_compound() {
assert!(!is_standalone_tracevault_subcommand(
"tracevault verify-start\nrm -rf /",
"verify-start"
));
}
#[test]
fn rejects_env_assignment_prefix_bypass() {
assert!(!is_standalone_tracevault_subcommand(
"EVIL=/tracevault verify-start",
"verify-start"
));
assert!(!is_standalone_tracevault_subcommand(
"X=/tracevault evil-cmd",
"verify-start"
));
assert!(!is_standalone_tracevault_subcommand(
"tracevault=x verify-start",
"verify-start"
));
}
#[test]
fn rejects_vertical_tab_and_form_feed_separators() {
assert!(!is_standalone_tracevault_subcommand(
"tracevault verify-start\u{0B}evil",
"verify-start"
));
assert!(!is_standalone_tracevault_subcommand(
"tracevault verify-start\u{0C}evil",
"verify-start"
));
}
}