use zenith_api::normalize::{
percent_decode_bytes_into, percent_decode_with_policy, InvalidSequencePolicy,
};
fn waf_decode_path(input: &str) -> Option<String> {
let mut buf = [0u8; 4096];
let decoded = percent_decode_bytes_into(input.as_bytes(), &mut buf, true)?;
std::str::from_utf8(decoded).ok().map(str::to_string)
}
fn extractor_decode_path(input: &str) -> Option<String> {
percent_decode_with_policy(input, true, InvalidSequencePolicy::Preserve)
}
const ADVERSARIAL_CASES: &[&str] = &[
"%ZZ",
"test%ZZdata",
"%",
"%2",
"100%+pure",
"%2Z%41",
"a+b",
"a+b+c",
"name=John+Doe",
"%FF",
"%FF%FE",
"bad=%FF&good=ok",
"%41%42%43",
"%2f%2F",
"%E4%BD%A0%E5%A5%BD",
"hello%20world",
"%2e%2e%2f",
"%252e%252e%252f",
"..%2f..%2fetc%2fpasswd",
"%2E%2E%2F",
"%2e%2F%2E",
"%41+%42",
"space%20%20here",
"%2525",
"id=%27+OR+%271%27%3D%271",
"id=1'+OR+'1'='1+%ZZ",
"",
"plain",
"%20",
];
#[test]
fn test_waf_extractor_decode_consistency() {
for case in ADVERSARIAL_CASES {
let waf = waf_decode_path(case);
let extractor = extractor_decode_path(case);
assert_eq!(
waf, extractor,
"WAF 与 extractor 解码结果不一致(协议差分): {:?}",
case
);
}
}
#[test]
fn test_consistency_spot_values() {
assert_eq!(waf_decode_path("%ZZ"), Some("%ZZ".to_string()));
assert_eq!(extractor_decode_path("%ZZ"), Some("%ZZ".to_string()));
assert_eq!(waf_decode_path("a+b"), Some("a b".to_string()));
assert_eq!(extractor_decode_path("a+b"), Some("a b".to_string()));
assert_eq!(waf_decode_path("%FF"), None);
assert_eq!(extractor_decode_path("%FF"), None);
assert_eq!(waf_decode_path("%2e%2e%2f"), Some("../".to_string()));
assert_eq!(extractor_decode_path("%2e%2e%2f"), Some("../".to_string()));
assert_eq!(
waf_decode_path("%252e%252e%252f"),
Some("%2e%2e%2f".to_string())
);
assert_eq!(
extractor_decode_path("%252e%252e%252f"),
Some("%2e%2e%2f".to_string())
);
}