From 82ad52b169ff5ce2f81440f79e2325afae39c1ab Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Tue, 3 Feb 2026 11:35:01 +0000 Subject: [PATCH] cob: further restrict TypeName Domain Names have restrictions on their total length, and the length of individual labels[^0]. Since `TypeName`s are expected to be reverse domain name strings, the total length of the internal string should not exceed 255 and each component should not exceed 63. [^0]: https://www.rfc-editor.org/rfc/rfc1035#section-2.3.4 --- CHANGELOG.md | 3 +++ crates/radicle-cob/src/type_name.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71296aa3..1f04e634 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Callers should no longer expect the `fetching` inside that JSON result. - The `rad debug` information for ongoing fetches contained the number of subscribers awaiting for results, this was removed. +- The `TypeName` strings defined in `radicle-cob` are restricted to reflect the + size limits on domain names as specified in + [RFC-1035](https://www.rfc-editor.org/rfc/rfc1035#section-2.3.4). ## 1.6.1 diff --git a/crates/radicle-cob/src/type_name.rs b/crates/radicle-cob/src/type_name.rs index 3a09a057..60f86235 100644 --- a/crates/radicle-cob/src/type_name.rs +++ b/crates/radicle-cob/src/type_name.rs @@ -10,6 +10,9 @@ use thiserror::Error; /// alphanumeric characters or hyphens separated by a period. Each /// component must start and end with an alphanumeric character. /// +/// The total length of a typename MUST NOT exceed 255, and each component +/// length MUST NOT exceed 63. +/// /// # Examples /// /// * `abc.def` @@ -19,6 +22,9 @@ use thiserror::Error; pub struct TypeName(String); impl TypeName { + const MAX_LENGTH: usize = 255; + const MAX_COMPONENT: usize = 63; + pub fn as_str(&self) -> &str { &self.0 } @@ -40,8 +46,18 @@ impl FromStr for TypeName { type Err = TypeNameParse; fn from_str(s: &str) -> Result { + if s.len() > Self::MAX_LENGTH { + return Err(TypeNameParse { + invalid: s.to_string(), + }); + } let split = s.split('.'); for component in split { + if component.len() > Self::MAX_COMPONENT { + return Err(TypeNameParse { + invalid: s.to_string(), + }); + } if component.is_empty() { return Err(TypeNameParse { invalid: s.to_string(), @@ -100,5 +116,15 @@ mod test { assert!(TypeName::from_str("abc..ghi").is_err()); assert!(TypeName::from_str("abc.-123.ghi").is_err()); assert!(TypeName::from_str("abc.123-.ghi").is_err()); + assert!(TypeName::from_str(&format!( + "a.very.long.name.that.exceeds.the.two-hundred-and-fifty-five.length.limit.{}", + "a".repeat(255) + )) + .is_err()); + assert!(TypeName::from_str(&format!( + "component.exceeds.sixty-three.limit.{}", + "a".repeat(64) + )) + .is_err()); } }