use proptest::prelude::*;
use waf_normalizer::url::normalize_path;
fn check_invariants(input: &str) {
let (out, _) = normalize_path(input);
assert!(out.starts_with('/'), "(i) not root-anchored: {out:?} from {input:?}");
for seg in out.split('/') {
assert_ne!(seg, "..", "(ii) residual traversal segment: {out:?} from {input:?}");
assert_ne!(seg, ".", "(iii) residual dot segment: {out:?} from {input:?}");
}
assert!(!out.contains("//"), "(iv) double slash: {out:?} from {input:?}");
assert!(!out.contains('\0'), "(v) NUL survived: {out:?} from {input:?}");
assert!(
!out.chars().any(|c| c.is_ascii_uppercase()),
"(vi) ASCII uppercase survived: {out:?} from {input:?}"
);
}
proptest! {
#[test]
fn prop_path_invariants_structured(
s in "(/|\\.\\.|\\.|%[0-9a-fA-F]{0,2}|[a-zA-Z0-9_]){0,40}",
) {
check_invariants(&s);
}
}
proptest! {
#[test]
fn prop_path_invariants_arbitrary(s in ".{0,64}") {
check_invariants(&s);
}
}
#[test]
fn known_traversal_is_neutralized() {
for (input, expect) in [
("/a/b/../c", "/a/c"),
("/../../../etc/passwd", "/etc/passwd"),
("/a/./b", "/a/b"),
("//a//b", "/a/b"),
("/%2e%2e/x", "/x"), ] {
let (out, _) = normalize_path(input);
assert_eq!(out, expect, "input {input:?}");
assert!(!out.split('/').any(|s| s == ".."), "residual .. in {out:?}");
}
}