mirror of
https://github.com/marcoallegretti/WEFT_OS.git
synced 2026-03-27 01:13:09 +00:00
fix(file-portal): block dotdot path-traversal in is_allowed
Path::starts_with is component-aware but does not resolve .., so /allowed/../etc/passwd would pass the check. Add normalize_path() that lexically resolves . and .. components without touching the filesystem so the check works on non-existent paths too. Add regression test.
This commit is contained in:
parent
a18f5c7604
commit
8eace960c2
1 changed files with 26 additions and 1 deletions
|
|
@ -74,11 +74,27 @@ fn parse_allowed(args: &[String]) -> Vec<PathBuf> {
|
||||||
allowed
|
allowed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn normalize_path(path: &Path) -> PathBuf {
|
||||||
|
use std::path::Component;
|
||||||
|
let mut out = PathBuf::new();
|
||||||
|
for c in path.components() {
|
||||||
|
match c {
|
||||||
|
Component::ParentDir => {
|
||||||
|
out.pop();
|
||||||
|
}
|
||||||
|
Component::CurDir => {}
|
||||||
|
other => out.push(other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
fn is_allowed(path: &Path, allowed: &[PathBuf]) -> bool {
|
fn is_allowed(path: &Path, allowed: &[PathBuf]) -> bool {
|
||||||
if allowed.is_empty() {
|
if allowed.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
allowed.iter().any(|a| path.starts_with(a))
|
let norm = normalize_path(path);
|
||||||
|
allowed.iter().any(|a| norm.starts_with(a))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_connection(stream: UnixStream, allowed: &[PathBuf]) {
|
fn handle_connection(stream: UnixStream, allowed: &[PathBuf]) {
|
||||||
|
|
@ -191,6 +207,15 @@ mod tests {
|
||||||
assert!(!is_allowed(Path::new("/etc/passwd"), &allowed));
|
assert!(!is_allowed(Path::new("/etc/passwd"), &allowed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dotdot_traversal_blocked() {
|
||||||
|
let allowed = vec![PathBuf::from("/tmp/weft-test-allowed")];
|
||||||
|
assert!(!is_allowed(
|
||||||
|
Path::new("/tmp/weft-test-allowed/../etc/passwd"),
|
||||||
|
&allowed
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_allowlist_rejects_all() {
|
fn empty_allowlist_rejects_all() {
|
||||||
assert!(!is_allowed(Path::new("/tmp/anything"), &[]));
|
assert!(!is_allowed(Path::new("/tmp/anything"), &[]));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue