pub(super) const MAX_PATTERN_LENGTH: usize = 1024;
pub(super) const MAX_PATH_SEGMENTS: usize = 32;
pub(super) const MAX_REGEX_SIZE: usize = 1 << 20;
pub(super) fn type_spec_to_regex(type_spec: &str) -> &'static str {
match type_spec {
"int" => r"[0-9]+",
"str" => r"[^/]+",
"uuid" => r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}",
"slug" => r"[a-z0-9]+(?:-[a-z0-9]+)*",
"path" => r".+",
"i8" | "i16" | "i32" | "i64" => r"-?[0-9]+",
"u8" | "u16" | "u32" | "u64" => r"[0-9]+",
"f32" | "f64" => r"-?[0-9]+(?:\.[0-9]+)?",
"bool" => r"true|false|1|0",
"email" => r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
"date" => r"[0-9]{4}-[0-9]{2}-[0-9]{2}",
_ => r"[^/]+",
}
}
pub(super) fn validate_path_param(value: &str) -> bool {
if value.contains('\0') {
return false;
}
let lower = value.to_ascii_lowercase();
if lower.contains("%2e")
|| lower.contains("%2f")
|| lower.contains("%5c")
|| lower.contains("%00")
{
return false;
}
if value.starts_with('/') || value.starts_with('\\') {
return false;
}
for segment in value.split('/') {
if segment == ".." {
return false;
}
}
for segment in value.split('\\') {
if segment == ".." {
return false;
}
}
true
}
pub(crate) fn validate_reverse_param(value: &str) -> bool {
if value.contains('\0') {
return false;
}
if value.contains('/') || value.contains('\\') || value.contains('?') || value.contains('#') {
return false;
}
if value == ".." || value.starts_with("../") || value.ends_with("/..") || value.contains("/../")
{
return false;
}
let lower = value.to_ascii_lowercase();
if lower.contains("%2f")
|| lower.contains("%2e")
|| lower.contains("%5c")
|| lower.contains("%3f")
|| lower.contains("%23")
|| lower.contains("%00")
{
return false;
}
true
}