From e6cfd61307920e6e0b7938b45a24b2d40b27084e Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Sat, 9 May 2026 06:02:42 +0200 Subject: [PATCH] validate slug --- backend/src/handlers/posts.rs | 56 ++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/backend/src/handlers/posts.rs b/backend/src/handlers/posts.rs index 27f7d7b..d1110c5 100644 --- a/backend/src/handlers/posts.rs +++ b/backend/src/handlers/posts.rs @@ -16,14 +16,56 @@ use crate::{ 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> { - if s.is_empty() - || s.contains('/') - || s.contains('\\') - || s.contains("..") - || s.contains('\0') - { - return Err(AppError::BadRequest("Invalid slug".to_string())); + if s.is_empty() { + return Err(AppError::BadRequest("Slug is empty".to_string())); + } + if s.len() > MAX_SLUG_LEN { + return Err(AppError::BadRequest(format!( + "Slug exceeds {} characters", + 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(()) }