diff --git a/src/parseable/mod.rs b/src/parseable/mod.rs index a46f92984..680aa8a3b 100644 --- a/src/parseable/mod.rs +++ b/src/parseable/mod.rs @@ -22,7 +22,7 @@ use std::{ num::NonZeroU32, path::PathBuf, str::FromStr, - sync::{Arc, RwLock}, + sync::{Arc, LazyLock, RwLock}, }; use actix_web::http::{ @@ -34,6 +34,7 @@ use bytes::Bytes; use chrono::Utc; use clap::{Parser, error::ErrorKind}; use once_cell::sync::{Lazy, OnceCell}; +use regex::Regex; use relative_path::RelativePathBuf; pub use staging::StagingError; use streams::StreamRef; @@ -102,6 +103,23 @@ pub const STREAM_EXISTS: &str = "Stream exists"; /// OnceCell to return metastore pub static METASTORE: OnceCell> = OnceCell::new(); +/// LazyLock for tenant id regex +pub static TENANT_ID_REGEX: LazyLock = LazyLock::new(|| { + regex::RegexBuilder::new(r"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,35}$") + .build() + .expect("Unable to create tenant id validation regex") +}); + +pub fn validate_tenant_id(tenant_id: &str) -> Result<(), String> { + if !TENANT_ID_REGEX.is_match(&tenant_id) { + return Err("tenant ID should follow regex- ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,35}$".into()); + } + if tenant_id.eq(DEFAULT_TENANT) { + return Err(format!("tenant ID can't be {DEFAULT_TENANT}")); + } + Ok(()) +} + /// For OTel log sources the telemetry_type is fully determined by the log_source. /// When the user explicitly sets x-p-telemetry-type and it disagrees with the /// implied type for an otel-* source, reject the request. When they don't set it, @@ -1260,6 +1278,8 @@ impl Parseable { // validate the possible presence of tenant storage metadata for tenant_id in dirs.into_iter() { + // check if tenantID is valid + validate_tenant_id(&tenant_id).map_err(|e| anyhow::anyhow!(e))?; if let Some(meta) = PARSEABLE .metastore .get_parseable_metadata(&Some(tenant_id.clone()))