Skip to content

Commit f736e7e

Browse files
committed
fix(execpolicy): normalize repeated path separators
1 parent 7954d02 commit f736e7e

2 files changed

Lines changed: 47 additions & 9 deletions

File tree

src/cortex-execpolicy/src/detection.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,20 @@ impl<'a> DetectionHelper<'a> {
1717

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

22-
// Handle trailing slashes
23-
while normalized.len() > 1 && normalized.ends_with('/') {
24-
normalized.pop();
25-
}
26-
27-
// Expand ~ to /home or user directory indicator
2822
if normalized.starts_with('~') {
2923
normalized = normalized.replacen('~', "/home", 1);
3024
}
3125

32-
// Handle Windows paths
33-
normalized = normalized.replace('\\', "/");
26+
while normalized.contains("//") {
27+
normalized = normalized.replace("//", "/");
28+
}
29+
30+
// Handle trailing slashes
31+
while normalized.len() > 1 && normalized.ends_with('/') {
32+
normalized.pop();
33+
}
3434

3535
normalized
3636
}

src/cortex-execpolicy/src/tests.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,22 @@ mod decision_tests {
7272
mod parsed_command_tests {
7373
use super::*;
7474

75+
#[test]
76+
fn test_normalize_path_collapses_repeated_slashes() {
77+
assert_eq!(
78+
crate::detection::DetectionHelper::normalize_path("///etc/shadow"),
79+
"/etc/shadow"
80+
);
81+
assert_eq!(
82+
crate::detection::DetectionHelper::normalize_path("////etc//passwd///"),
83+
"/etc/passwd"
84+
);
85+
assert_eq!(
86+
crate::detection::DetectionHelper::normalize_path(r"C:\\Users\\secrets\\"),
87+
"C:/Users/secrets"
88+
);
89+
}
90+
7591
#[test]
7692
fn test_parse_simple_command() {
7793
let cmd = ParsedCommand::from_args(&["ls".to_string(), "-la".to_string()]).unwrap();
@@ -254,6 +270,28 @@ mod destructive_file_ops_tests {
254270
}
255271
}
256272

273+
#[test]
274+
fn test_multi_slash_sensitive_paths_denied() {
275+
let policy = ExecPolicy::new();
276+
277+
let bypass_attempts = vec![
278+
vec!["rm", "-rf", "///etc"],
279+
vec!["rm", "-rf", "////etc/passwd"],
280+
vec!["shred", "///etc/shadow"],
281+
vec!["chmod", "777", "///etc/passwd"],
282+
];
283+
284+
for cmd in bypass_attempts {
285+
let cmd_strings: Vec<String> = cmd.iter().map(|s| s.to_string()).collect();
286+
assert_eq!(
287+
policy.evaluate(&cmd_strings),
288+
Decision::Deny,
289+
"Failed for: {:?}",
290+
cmd
291+
);
292+
}
293+
}
294+
257295
#[test]
258296
fn test_rm_on_safe_path_allowed() {
259297
let policy = ExecPolicy::new();

0 commit comments

Comments
 (0)