use super::traits::{Operator, OperatorResult};
pub struct ValidateUrlEncodingOperator;
impl Operator for ValidateUrlEncodingOperator {
fn execute(&self, value: &str) -> OperatorResult {
if is_valid_url_encoding(value) {
OperatorResult::no_match() } else {
OperatorResult::matched("invalid URL encoding".to_string())
}
}
fn name(&self) -> &'static str {
"validateUrlEncoding"
}
}
pub struct ValidateUtf8EncodingOperator;
impl Operator for ValidateUtf8EncodingOperator {
fn execute(&self, value: &str) -> OperatorResult {
if is_valid_utf8_sequence(value) {
OperatorResult::no_match()
} else {
OperatorResult::matched("invalid UTF-8 encoding".to_string())
}
}
fn name(&self) -> &'static str {
"validateUtf8Encoding"
}
}
fn is_valid_url_encoding(s: &str) -> bool {
let mut chars = s.chars().peekable();
while let Some(c) = chars.next() {
if c == '%' {
let hex1 = chars.next();
let hex2 = chars.next();
match (hex1, hex2) {
(Some(h1), Some(h2)) => {
if !h1.is_ascii_hexdigit() || !h2.is_ascii_hexdigit() {
return false;
}
}
_ => return false,
}
}
}
true
}
fn is_valid_utf8_sequence(s: &str) -> bool {
if s.contains('\0') {
return false;
}
let bytes = s.as_bytes();
for i in 0..bytes.len() {
if bytes[i] == 0xC0 || bytes[i] == 0xC1 {
return false;
}
if bytes[i] >= 0xF5 {
return false;
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_url_encoding() {
assert!(is_valid_url_encoding("hello%20world"));
assert!(is_valid_url_encoding("test%2Fpath"));
assert!(!is_valid_url_encoding("hello%2"));
assert!(!is_valid_url_encoding("hello%GG"));
}
#[test]
fn test_validate_url_encoding_operator() {
let op = ValidateUrlEncodingOperator;
assert!(!op.execute("hello%20world").matched); assert!(op.execute("hello%2").matched); }
}