Skip to main content

max / makenotwork

validation: reject control characters in bio and sync device name (ultra-fuzz NOTE tail) Closes the Run 2 NOTE that validate_bio and validate_sync_device_name accepted control characters, while display-name and sync-app-name already rejected them. Bio allows newlines/tabs (multi-line) but not other control chars; device name rejects all control chars, mirroring validate_sync_app_name. Tests cover NUL and escape. (Bidi/format chars are a separate Trojan-source concern, out of scope for this NOTE; is_control() matches the existing validators' policy.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-06-23 20:43 UTC
Commit: ccf8532a5b4ff192b7a3a631fa1a94d45b7ffbb6
Parent: bf7d7c6
2 files changed, +21 insertions, -0 deletions
@@ -133,6 +133,12 @@ pub fn validate_sync_device_name(name: &str) -> Result<(), AppError> {
133 133 limits::SYNC_DEVICE_NAME_MAX
134 134 )));
135 135 }
136 + // Reject control characters, mirroring `validate_sync_app_name`.
137 + if name.chars().any(|c| c.is_control()) {
138 + return Err(AppError::validation(
139 + "Device name must not contain control characters".to_string(),
140 + ));
141 + }
136 142 Ok(())
137 143 }
138 144
@@ -234,6 +240,9 @@ mod tests {
234 240 assert!(validate_sync_device_name("Max's MacBook").is_ok());
235 241 assert!(validate_sync_device_name("").is_err());
236 242 assert!(validate_sync_device_name(&"a".repeat(101)).is_err());
243 + // Control characters (NUL, escape) are rejected.
244 + assert!(validate_sync_device_name("laptop\u{0}").is_err());
245 + assert!(validate_sync_device_name("laptop\u{1b}evil").is_err());
237 246 }
238 247
239 248 #[test]
@@ -29,6 +29,14 @@ pub fn validate_bio(bio: &str) -> Result<(), AppError> {
29 29 limits::BIO_MAX
30 30 )));
31 31 }
32 + // Reject control characters (NUL, escape, etc.) while allowing normal
33 + // whitespace (newlines/tabs) so multi-line bios still work. Mirrors the
34 + // display-name and sync-app-name validators.
35 + if bio.chars().any(|c| c.is_control() && !matches!(c, '\n' | '\r' | '\t')) {
36 + return Err(AppError::validation(
37 + "Bio must not contain control characters".to_string(),
38 + ));
39 + }
32 40 Ok(())
33 41 }
34 42
@@ -233,6 +241,10 @@ mod tests {
233 241 assert!(validate_bio("I make music").is_ok());
234 242 assert!(validate_bio("").is_ok());
235 243 assert!(validate_bio(&"a".repeat(2001)).is_err());
244 + // Newlines/tabs are fine (multi-line bios); other control chars aren't.
245 + assert!(validate_bio("line one\nline two\ttabbed").is_ok());
246 + assert!(validate_bio("evil\u{0}nul").is_err());
247 + assert!(validate_bio("esc\u{1b}ape").is_err());
236 248 }
237 249
238 250 #[test]