validate slug

This commit is contained in:
2026-05-09 06:02:42 +02:00
parent 801df057e0
commit e6cfd61307
+49 -7
View File
@@ -16,14 +16,56 @@ use crate::{
const WORDS_PER_MINUTE: u32 = 200; const WORDS_PER_MINUTE: u32 = 200;
const MAX_SLUG_LEN: usize = 100;
const WINDOWS_RESERVED: &[&str] = &[
"CON", "PRN", "AUX", "NUL",
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
];
fn validate_slug(s: &str) -> Result<(), AppError> { fn validate_slug(s: &str) -> Result<(), AppError> {
if s.is_empty() if s.is_empty() {
|| s.contains('/') return Err(AppError::BadRequest("Slug is empty".to_string()));
|| s.contains('\\') }
|| s.contains("..") if s.len() > MAX_SLUG_LEN {
|| s.contains('\0') return Err(AppError::BadRequest(format!(
{ "Slug exceeds {} characters",
return Err(AppError::BadRequest("Invalid slug".to_string())); MAX_SLUG_LEN
)));
}
if s.starts_with('.') {
return Err(AppError::BadRequest(
"Slug cannot start with '.'".to_string(),
));
}
if s.ends_with('.') || s.ends_with(' ') {
return Err(AppError::BadRequest(
"Slug cannot end with '.' or space".to_string(),
));
}
if s.contains("..") {
return Err(AppError::BadRequest(
"Slug cannot contain '..'".to_string(),
));
}
for c in s.chars() {
if c.is_control() {
return Err(AppError::BadRequest(
"Slug contains control characters".to_string(),
));
}
if matches!(c, '/' | '\\' | '<' | '>' | ':' | '"' | '|' | '?' | '*') {
return Err(AppError::BadRequest(format!(
"Slug contains invalid character '{}'",
c
)));
}
}
let stem = s.split('.').next().unwrap_or("").to_ascii_uppercase();
if WINDOWS_RESERVED.iter().any(|r| *r == stem) {
return Err(AppError::BadRequest(
"Slug is a reserved name".to_string(),
));
} }
Ok(()) Ok(())
} }