For any security-sensitive policy/matching logic (authZ/exec policies, allow/deny rules, pattern matching), always canonicalize the relevant inputs before matching, and ensure edge cases like absolute-path .. are handled safely to prevent bypass.
Apply:
./..) before applying glob/prefix/pattern checks... consistently (e.g., /../foo should normalize to /foo, not escape the root).Example pattern (self-contained):
fn normalize_path(value: &str) -> String {
let raw = value.trim().replace('\\', "/");
let absolute = raw.starts_with('/');
let mut segs: Vec<&str> = Vec::new();
for s in raw.split('/') {
match s {
"" | "." => {}
".." => {
if !absolute {
// For relative paths, preserve leading ..
if segs.is_empty() || *segs.last().unwrap() == ".." {
segs.push(s);
} else {
segs.pop();
}
} else {
// For absolute paths, clamp: `/../foo` -> `/foo`
if !segs.is_empty() && *segs.last().unwrap() != ".." {
segs.pop();
}
}
}
_ => segs.push(s),
}
}
let mut out = String::new();
if absolute { out.push('/'); }
out.push_str(&segs.join("/"));
out
}
// Security regression test idea:
// assert!(!matches_policy("/src/**", normalize_path("/src/../secret.txt")));
This prevents attackers from exploiting normalization gaps to make /src/../secret.txt incorrectly match a /src/**-style rule.
Enter the URL of a public GitHub repository