Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/cortex-execpolicy/src/detection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ impl<'a> DetectionHelper<'a> {

/// Normalize a path for comparison.
pub fn normalize_path(path: &str) -> String {
let mut normalized = path.replace("//", "/");
let mut normalized = path.replace('\\', "/");

// Handle trailing slashes
while normalized.len() > 1 && normalized.ends_with('/') {
normalized.pop();
}

// Expand ~ to /home or user directory indicator
if normalized.starts_with('~') {
normalized = normalized.replacen('~', "/home", 1);
}

// Handle Windows paths
normalized = normalized.replace('\\', "/");
while normalized.contains("//") {
normalized = normalized.replace("//", "/");
}

// Handle trailing slashes
while normalized.len() > 1 && normalized.ends_with('/') {
normalized.pop();
}

normalized
}
Expand Down
38 changes: 38 additions & 0 deletions src/cortex-execpolicy/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,22 @@ mod decision_tests {
mod parsed_command_tests {
use super::*;

#[test]
fn test_normalize_path_collapses_repeated_slashes() {
assert_eq!(
crate::detection::DetectionHelper::normalize_path("///etc/shadow"),
"/etc/shadow"
);
assert_eq!(
crate::detection::DetectionHelper::normalize_path("////etc//passwd///"),
"/etc/passwd"
);
assert_eq!(
crate::detection::DetectionHelper::normalize_path(r"C:\\Users\\secrets\\"),
"C:/Users/secrets"
);
}

#[test]
fn test_parse_simple_command() {
let cmd = ParsedCommand::from_args(&["ls".to_string(), "-la".to_string()]).unwrap();
Expand Down Expand Up @@ -254,6 +270,28 @@ mod destructive_file_ops_tests {
}
}

#[test]
fn test_multi_slash_sensitive_paths_denied() {
let policy = ExecPolicy::new();

let bypass_attempts = vec![
vec!["rm", "-rf", "///etc"],
vec!["rm", "-rf", "////etc/passwd"],
vec!["shred", "///etc/shadow"],
vec!["chmod", "777", "///etc/passwd"],
];

for cmd in bypass_attempts {
let cmd_strings: Vec<String> = cmd.iter().map(|s| s.to_string()).collect();
assert_eq!(
policy.evaluate(&cmd_strings),
Decision::Deny,
"Failed for: {:?}",
cmd
);
}
}

#[test]
fn test_rm_on_safe_path_allowed() {
let policy = ExecPolicy::new();
Expand Down