From f296bb68df0ec99d62d2bea07094c07df024bb8f Mon Sep 17 00:00:00 2001 From: Jack Platten Date: Sun, 23 Aug 2026 16:36:02 -0700 Subject: [PATCH] fix: allow path-empty URIs and drop authority/path charset checks RFC 3986 lets hier-part be path-empty, so "urn:" alone is a valid URI; treat it as one. --- crates/tranquil-lexicon/src/formats.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/tranquil-lexicon/src/formats.rs b/crates/tranquil-lexicon/src/formats.rs index cb8feb5..66c3a12 100644 --- a/crates/tranquil-lexicon/src/formats.rs +++ b/crates/tranquil-lexicon/src/formats.rs @@ -34,6 +34,12 @@ pub fn is_valid_datetime(s: &str) -> bool { chrono::DateTime::parse_from_rfc3339(s).is_ok() } +/// Checks the scheme only, not the character set or structure of what +/// follows. The aim is to accept at least all valid URIs; we can always +/// tighten this later. It does not parse the authority, because at-uris +/// put colons in the authority (at://did:plc:abc123/collection/rkey) and +/// any 3986 authority parser reads that as a non-numeric port and rejects +/// it. pub fn is_valid_uri(s: &str) -> bool { let Some((scheme, rest)) = s.split_once(':') else { return false; @@ -48,7 +54,7 @@ pub fn is_valid_uri(s: &str) -> bool { } match rest.strip_prefix("//") { Some(authority_and_path) => !authority_and_path.is_empty(), - None => !rest.is_empty(), + None => true, } } @@ -167,14 +173,23 @@ mod tests { assert!(is_valid_uri( "has_an_underscore:70766a5a-3f95-4b19-96c8-a2c9c4a5e6e5" )); + assert!(is_valid_uri("urn:")); } #[test] fn test_invalid_uris_without_authority() { - assert!(!is_valid_uri("urn:")); assert!(!is_valid_uri(":no-scheme")); } + #[test] + fn test_valid_uris_dont_reject_at_uri_authority_colons() { + // at-uri authorities contain colons (did:plc:...); is_valid_uri must not + // reject them the way a strict RFC 3986 authority parser would. + assert!(is_valid_uri( + "at://did:plc:cwdkf4xxjpznceembuuspt3d/sh.tangled.repo.pull/3mtjn7zouwn22" + )); + } + #[test] fn test_valid_cids() { assert!(is_valid_cid("bafyreiabcdef123456"));