Files
crank/crates/crank-config/src/validation.rs
T

112 lines
3.2 KiB
Rust

use std::net::IpAddr;
pub(crate) struct ParsedList<T> {
pub(crate) items: Vec<T>,
pub(crate) invalid: bool,
pub(crate) out_of_range: bool,
}
pub(crate) fn valid_percent_encoding(value: &str) -> bool {
let bytes = value.as_bytes();
let mut index = 0;
while index < bytes.len() {
if bytes[index] == b'%' {
if index + 2 >= bytes.len()
|| !bytes[index + 1].is_ascii_hexdigit()
|| !bytes[index + 2].is_ascii_hexdigit()
{
return false;
}
index += 3;
} else {
index += 1;
}
}
true
}
pub(crate) fn valid_database_host(value: &str) -> bool {
!value.is_empty()
&& value.len() <= 253
&& !value.chars().any(char::is_whitespace)
&& (value.parse::<std::net::IpAddr>().is_ok()
|| value.split('.').all(|label| {
!label.is_empty()
&& label.len() <= 63
&& label
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || byte == b'-')
&& !label.starts_with('-')
&& !label.ends_with('-')
}))
}
pub(crate) fn valid_database_identifier(value: &str) -> bool {
!value.is_empty()
&& value.len() <= 128
&& value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-' | b'.'))
}
pub(crate) fn parse_host_list(raw: &str) -> ParsedList<String> {
let mut result = ParsedList {
items: Vec::new(),
invalid: false,
out_of_range: raw.len() > 16_384,
};
if result.out_of_range {
return result;
}
for item in raw.split(',') {
let item = item.trim().to_ascii_lowercase();
let base = item.strip_prefix("*.").unwrap_or(&item);
let valid_ip = !item.starts_with("*.") && base.parse::<IpAddr>().is_ok();
if item.is_empty()
|| item.len() > 253
|| item.contains('/')
|| item.contains(char::is_whitespace)
|| base.is_empty()
|| (!valid_ip && base.contains(':'))
|| (item.starts_with("*.") && base.parse::<IpAddr>().is_ok())
{
result.invalid = true;
continue;
}
if !result.items.contains(&item) {
result.items.push(item);
}
}
if result.items.len() > 256 {
result.out_of_range = true;
result.items.truncate(256);
}
result
}
pub(crate) fn parse_ip_list(raw: &str) -> ParsedList<IpAddr> {
let mut result = ParsedList {
items: Vec::new(),
invalid: false,
out_of_range: raw.len() > 4096,
};
if result.out_of_range {
return result;
}
for item in raw.split(',').map(str::trim) {
match item.parse::<IpAddr>() {
Ok(value) if !result.items.contains(&value) => result.items.push(value),
Ok(_) => {}
Err(_) => result.invalid = true,
}
}
if result.items.is_empty() {
result.invalid = true;
}
if result.items.len() > 64 {
result.out_of_range = true;
result.items.truncate(64);
}
result
}