From 539af12d21567a39a074d3a73c893d98275c70d4 Mon Sep 17 00:00:00 2001 From: trinity-1686a Date: Mon, 19 May 2025 18:07:04 +0200 Subject: [PATCH] allow punnycode in bucket name --- src/api/admin/bucket.rs | 4 ++-- src/api/s3/bucket.rs | 2 +- src/garage/admin/bucket.rs | 2 +- src/model/bucket_alias_table.rs | 18 ++++++++---------- src/model/helper/locked.rs | 7 +++---- src/util/config.rs | 4 ++++ 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs index 2537bfc9..6cc21938 100644 --- a/src/api/admin/bucket.rs +++ b/src/api/admin/bucket.rs @@ -277,7 +277,7 @@ pub async fn handle_create_bucket( let helper = garage.locked_helper().await; if let Some(ga) = &req.global_alias { - if !is_valid_bucket_name(ga) { + if !is_valid_bucket_name(ga, garage.config.allow_punnycode) { return Err(Error::bad_request(format!( "{}: {}", ga, INVALID_BUCKET_NAME_MESSAGE @@ -292,7 +292,7 @@ pub async fn handle_create_bucket( } if let Some(la) = &req.local_alias { - if !is_valid_bucket_name(&la.alias) { + if !is_valid_bucket_name(&la.alias, garage.config.allow_punnycode) { return Err(Error::bad_request(format!( "{}: {}", la.alias, INVALID_BUCKET_NAME_MESSAGE diff --git a/src/api/s3/bucket.rs b/src/api/s3/bucket.rs index 3a09e769..d2a36c18 100644 --- a/src/api/s3/bucket.rs +++ b/src/api/s3/bucket.rs @@ -172,7 +172,7 @@ pub async fn handle_create_bucket( } // Create the bucket! - if !is_valid_bucket_name(&bucket_name) { + if !is_valid_bucket_name(&bucket_name, garage.config.allow_punnycode) { return Err(Error::bad_request(format!( "{}: {}", bucket_name, INVALID_BUCKET_NAME_MESSAGE diff --git a/src/garage/admin/bucket.rs b/src/garage/admin/bucket.rs index 1bdc6086..1ed0ebd8 100644 --- a/src/garage/admin/bucket.rs +++ b/src/garage/admin/bucket.rs @@ -126,7 +126,7 @@ impl AdminRpcHandler { #[allow(clippy::ptr_arg)] async fn handle_create_bucket(&self, name: &String) -> Result { - if !is_valid_bucket_name(name) { + if !is_valid_bucket_name(name, self.garage.config.allow_punnycode) { return Err(Error::BadRequest(format!( "{}: {}", name, INVALID_BUCKET_NAME_MESSAGE diff --git a/src/model/bucket_alias_table.rs b/src/model/bucket_alias_table.rs index 8bbe4118..04d808e8 100644 --- a/src/model/bucket_alias_table.rs +++ b/src/model/bucket_alias_table.rs @@ -22,14 +22,10 @@ mod v08 { pub use v08::*; impl BucketAlias { - pub fn new(name: String, ts: u64, bucket_id: Option) -> Option { - if !is_valid_bucket_name(&name) { - None - } else { - Some(BucketAlias { - name, - state: crdt::Lww::raw(ts, bucket_id), - }) + pub fn new(name: String, ts: u64, bucket_id: Option) -> Self { + BucketAlias { + name, + state: crdt::Lww::raw(ts, bucket_id), } } @@ -80,7 +76,7 @@ impl TableSchema for BucketAliasTable { /// In the case of Garage, bucket names must not be hex-encoded /// 32 byte string, which is excluded thanks to the /// maximum length of 63 bytes given in the spec. -pub fn is_valid_bucket_name(n: &str) -> bool { +pub fn is_valid_bucket_name(n: &str, punny: bool) -> bool { // Bucket names must be between 3 and 63 characters n.len() >= 3 && n.len() <= 63 // Bucket names must be composed of lowercase letters, numbers, @@ -92,7 +88,9 @@ pub fn is_valid_bucket_name(n: &str) -> bool { // Bucket names must not be formatted as an IP address && n.parse::().is_err() // Bucket names must not start with "xn--" - && !n.starts_with("xn--") + && (!n.starts_with("xn--") || punny) + // We are a bit stricter, to properly restrict punnycode in all labels + && (!n.contains(".xn--") || punny) // Bucket names must not end with "-s3alias" && !n.ends_with("-s3alias") } diff --git a/src/model/helper/locked.rs b/src/model/helper/locked.rs index 482e91b0..16b0bafc 100644 --- a/src/model/helper/locked.rs +++ b/src/model/helper/locked.rs @@ -57,7 +57,7 @@ impl<'a> LockedHelper<'a> { bucket_id: Uuid, alias_name: &String, ) -> Result<(), Error> { - if !is_valid_bucket_name(alias_name) { + if !is_valid_bucket_name(alias_name, self.0.config.allow_punnycode) { return Err(Error::InvalidBucketName(alias_name.to_string())); } @@ -88,8 +88,7 @@ impl<'a> LockedHelper<'a> { // writes are now done and all writes use timestamp alias_ts let alias = match alias { - None => BucketAlias::new(alias_name.clone(), alias_ts, Some(bucket_id)) - .ok_or_else(|| Error::InvalidBucketName(alias_name.clone()))?, + None => BucketAlias::new(alias_name.clone(), alias_ts, Some(bucket_id)), Some(mut a) => { a.state = Lww::raw(alias_ts, Some(bucket_id)); a @@ -218,7 +217,7 @@ impl<'a> LockedHelper<'a> { ) -> Result<(), Error> { let key_helper = KeyHelper(self.0); - if !is_valid_bucket_name(alias_name) { + if !is_valid_bucket_name(alias_name, self.0.config.allow_punnycode) { return Err(Error::InvalidBucketName(alias_name.to_string())); } diff --git a/src/util/config.rs b/src/util/config.rs index 73fc4ff4..f128177b 100644 --- a/src/util/config.rs +++ b/src/util/config.rs @@ -135,6 +135,10 @@ pub struct Config { /// Configuration for the admin API endpoint #[serde(default = "Default::default")] pub admin: AdminConfig, + + /// Allow punnycode in bucket names + #[serde(default)] + pub allow_punnycode: bool, } /// Value for data_dir: either a single directory or a list of dirs with attributes