From 27e2a8630ec703ac81da1d14634da8876f360f48 Mon Sep 17 00:00:00 2001 From: Denys Fedoryshchenko Date: Thu, 18 Dec 2025 15:25:05 +0200 Subject: [PATCH 1/2] hotfix: Add error details so we can troubleshoot problem Signed-off-by: Denys Fedoryshchenko --- src/azure.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/azure.rs b/src/azure.rs index 0ee741c..e60268c 100644 --- a/src/azure.rs +++ b/src/azure.rs @@ -440,7 +440,36 @@ async fn get_file_from_blob(filename: String) -> ReceivedFile { // is status anything else than 200? // TODO: Do we need to return headers as well or it is data leakage? if response.status() != 200 { - eprintln!("Error getting blob: {:?}", response.status()); + let status = response.status(); + let headers = response.headers().clone(); + let ms_error_code = headers + .get("x-ms-error-code") + .and_then(|v| v.to_str().ok()) + .unwrap_or(""); + let ms_request_id = headers + .get("x-ms-request-id") + .and_then(|v| v.to_str().ok()) + .unwrap_or(""); + let content_type = headers + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or(""); + let body_preview = match response.bytes().await { + Ok(bytes) => { + let mut preview = String::from_utf8_lossy(&bytes).into_owned(); + const MAX_LEN: usize = 4096; + if preview.len() > MAX_LEN { + preview.truncate(MAX_LEN); + preview.push_str("…"); + } + preview + } + Err(e) => format!(""), + }; + + eprintln!( + "Error getting blob: {status} (x-ms-error-code={ms_error_code}, x-ms-request-id={ms_request_id}, content-type={content_type}) body={body_preview}" + ); return received_file; } received_file.headers = response.headers().clone(); From 5abc2bf2cec2c9c5881e17507d0a73cb41c1d640 Mon Sep 17 00:00:00 2001 From: Denys Fedoryshchenko Date: Thu, 18 Dec 2025 15:31:12 +0200 Subject: [PATCH 2/2] Add sas token format validation Signed-off-by: Denys Fedoryshchenko --- src/azure.rs | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/azure.rs b/src/azure.rs index e60268c..5e1f030 100644 --- a/src/azure.rs +++ b/src/azure.rs @@ -53,6 +53,18 @@ fn sanitize_tag_component(input: &str) -> String { .collect() } +fn normalize_sas_token(input: &str) -> String { + let trimmed = input.trim(); + if trimmed.is_empty() { + return String::new(); + } + if trimmed.starts_with('?') { + trimmed.to_string() + } else { + format!("?{}", trimmed) + } +} + /// Get Azure credentials from config.toml fn get_azure_credentials(name: &str) -> AzureConfig { let cfg_content = get_config_content(); @@ -67,7 +79,30 @@ fn get_azure_credentials(name: &str) -> AzureConfig { account: account.to_string(), key: key.to_string(), container: container.to_string(), - sastoken: sastoken.to_string(), + sastoken: normalize_sas_token(sastoken), + } +} + +#[cfg(test)] +mod tests { + use super::normalize_sas_token; + + #[test] + fn sas_token_is_left_empty() { + assert_eq!(normalize_sas_token(""), ""); + assert_eq!(normalize_sas_token(" "), ""); + } + + #[test] + fn sas_token_is_left_intact_when_prefixed() { + assert_eq!(normalize_sas_token("?sv=1"), "?sv=1"); + assert_eq!(normalize_sas_token(" ?sv=1 "), "?sv=1"); + } + + #[test] + fn sas_token_is_prefixed_when_missing_question_mark() { + assert_eq!(normalize_sas_token("sv=1"), "?sv=1"); + assert_eq!(normalize_sas_token(" sv=1 "), "?sv=1"); } }