//! Validation of the opaque GUI payload and server key. The payload is a //! base64 string produced by the mod (header + up to 8 cursor samples); the //! service never decodes it, it only bounds what it stores and relays. /// Header (4 bytes) + 8 samples x 4 bytes = 36 bytes = 48 base64 characters. pub const MAX_PAYLOAD_CHARS: usize = 48; pub const MAX_SERVER_CHARS: usize = 64; pub const MAX_WANT: usize = 40; pub fn valid_payload(s: &str) -> bool { !s.is_empty() && s.len() <= MAX_PAYLOAD_CHARS && s.bytes() .all(|b| b.is_ascii_alphanumeric() || matches!(b, b'+' | b'/' | b'=')) } /// Normalised server key: trimmed, lower-case, no control characters, bounded. pub fn normalize_server(s: &str) -> Option { let s = s.trim().to_ascii_lowercase(); if s.is_empty() || s.chars().count() > MAX_SERVER_CHARS || s.chars().any(|c| c.is_control()) { return None; } Some(s) } #[cfg(test)] mod tests { use super::*; #[test] fn payload_charset_and_length() { assert!(valid_payload("AQEAAAA=")); assert!(!valid_payload("")); assert!(!valid_payload("has space")); assert!(!valid_payload("