From 14dd5e3184e3ec1a78f87ab2176fc1f04c998ee4 Mon Sep 17 00:00:00 2001 From: shikhar Date: Fri, 20 Feb 2026 10:52:30 -0500 Subject: [PATCH 1/4] fix(types): reject control characters in bucket names --- src/types.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/types.rs b/src/types.rs index f5ca030..7251085 100644 --- a/src/types.rs +++ b/src/types.rs @@ -26,6 +26,9 @@ impl BucketName { if name.len() > Self::MAX_LEN { return Err("Bucket name too long"); } + if name.chars().any(char::is_control) { + return Err("Bucket name cannot contain control characters"); + } Ok(Self(name)) } } @@ -184,6 +187,12 @@ impl IntoIterator for BucketNameSet { mod tests { use super::*; + #[test] + fn bucket_name_rejects_control_characters() { + let result = BucketName::new("bucket\nname"); + assert_eq!(result, Err("Bucket name cannot contain control characters")); + } + #[test] fn object_kind_deserialize_rejects_empty() { let result: Result = serde_json::from_str(r#""""#); From c1fd614fe1de3ce29b19cb89ca07161bb2dfe0fb Mon Sep 17 00:00:00 2001 From: shikhar Date: Fri, 20 Feb 2026 11:07:48 -0500 Subject: [PATCH 2/4] fix(types): reject control characters in object kinds --- src/types.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/types.rs b/src/types.rs index 7251085..a521d2e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -67,6 +67,9 @@ impl ObjectKind { if key.len() > Self::MAX_LEN { return Err("Object kind too long"); } + if key.chars().any(char::is_control) { + return Err("Object kind cannot contain control characters"); + } Ok(Self(key)) } } @@ -193,6 +196,12 @@ mod tests { assert_eq!(result, Err("Bucket name cannot contain control characters")); } + #[test] + fn object_kind_rejects_control_characters() { + let result = ObjectKind::new("kind\nname"); + assert_eq!(result, Err("Object kind cannot contain control characters")); + } + #[test] fn object_kind_deserialize_rejects_empty() { let result: Result = serde_json::from_str(r#""""#); From 8c7c870c19c8a2aaad1838abed733ab3320d5200 Mon Sep 17 00:00:00 2001 From: shikhar Date: Fri, 20 Feb 2026 11:09:45 -0500 Subject: [PATCH 3/4] refactor(types): deduplicate bucket and kind validation --- src/types.rs | 51 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/types.rs b/src/types.rs index a521d2e..b9c7603 100644 --- a/src/types.rs +++ b/src/types.rs @@ -6,6 +6,25 @@ use serde::{Deserialize, Deserializer, Serialize, de::Error}; pub type PageId = u16; +fn validate_bucket_or_kind_name( + value: &str, + max_len: usize, + empty_error: &'static str, + too_long_error: &'static str, + control_chars_error: &'static str, +) -> Result<(), &'static str> { + if value.is_empty() { + return Err(empty_error); + } + if value.len() > max_len { + return Err(too_long_error); + } + if value.chars().any(char::is_control) { + return Err(control_chars_error); + } + Ok(()) +} + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct BucketName(CompactString); @@ -20,15 +39,13 @@ impl BucketName { pub fn new(name: impl Into) -> Result { let name = name.into(); - if name.is_empty() { - return Err("Bucket name cannot be empty"); - } - if name.len() > Self::MAX_LEN { - return Err("Bucket name too long"); - } - if name.chars().any(char::is_control) { - return Err("Bucket name cannot contain control characters"); - } + validate_bucket_or_kind_name( + name.as_str(), + Self::MAX_LEN, + "Bucket name cannot be empty", + "Bucket name too long", + "Bucket name cannot contain control characters", + )?; Ok(Self(name)) } } @@ -61,15 +78,13 @@ impl ObjectKind { pub fn new(key: impl Into) -> Result { let key = key.into(); - if key.is_empty() { - return Err("Object kind cannot be empty"); - } - if key.len() > Self::MAX_LEN { - return Err("Object kind too long"); - } - if key.chars().any(char::is_control) { - return Err("Object kind cannot contain control characters"); - } + validate_bucket_or_kind_name( + key.as_str(), + Self::MAX_LEN, + "Object kind cannot be empty", + "Object kind too long", + "Object kind cannot contain control characters", + )?; Ok(Self(key)) } } From d5ece656f35e208bf9c50a153560eb6385f3376e Mon Sep 17 00:00:00 2001 From: shikhar Date: Fri, 20 Feb 2026 11:10:40 -0500 Subject: [PATCH 4/4] Revert "refactor(types): deduplicate bucket and kind validation" This reverts commit 8c7c870c19c8a2aaad1838abed733ab3320d5200. --- src/types.rs | 51 ++++++++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/src/types.rs b/src/types.rs index b9c7603..a521d2e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -6,25 +6,6 @@ use serde::{Deserialize, Deserializer, Serialize, de::Error}; pub type PageId = u16; -fn validate_bucket_or_kind_name( - value: &str, - max_len: usize, - empty_error: &'static str, - too_long_error: &'static str, - control_chars_error: &'static str, -) -> Result<(), &'static str> { - if value.is_empty() { - return Err(empty_error); - } - if value.len() > max_len { - return Err(too_long_error); - } - if value.chars().any(char::is_control) { - return Err(control_chars_error); - } - Ok(()) -} - #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct BucketName(CompactString); @@ -39,13 +20,15 @@ impl BucketName { pub fn new(name: impl Into) -> Result { let name = name.into(); - validate_bucket_or_kind_name( - name.as_str(), - Self::MAX_LEN, - "Bucket name cannot be empty", - "Bucket name too long", - "Bucket name cannot contain control characters", - )?; + if name.is_empty() { + return Err("Bucket name cannot be empty"); + } + if name.len() > Self::MAX_LEN { + return Err("Bucket name too long"); + } + if name.chars().any(char::is_control) { + return Err("Bucket name cannot contain control characters"); + } Ok(Self(name)) } } @@ -78,13 +61,15 @@ impl ObjectKind { pub fn new(key: impl Into) -> Result { let key = key.into(); - validate_bucket_or_kind_name( - key.as_str(), - Self::MAX_LEN, - "Object kind cannot be empty", - "Object kind too long", - "Object kind cannot contain control characters", - )?; + if key.is_empty() { + return Err("Object kind cannot be empty"); + } + if key.len() > Self::MAX_LEN { + return Err("Object kind too long"); + } + if key.chars().any(char::is_control) { + return Err("Object kind cannot contain control characters"); + } Ok(Self(key)) } }