From 4448895a6b2c7163ae01e155c6add92b0c7f5840 Mon Sep 17 00:00:00 2001 From: NimBold Date: Mon, 29 Jun 2026 17:36:43 +0330 Subject: [PATCH] fix: pass capture cookies and headers securely to metadata fetcher - Pass captured cookies and headers from UI state into the backend `fetch_metadata` command. - Implement secure domain-matching redirect policy in the backend to prevent leaking cookies, headers, and Basic Auth credentials to third-party domains upon redirect. - Ensure metadata fetches correctly identify authenticated files (e.g. from Gofile) without being redirected to login/error pages. --- src-tauri/src/lib.rs | 52 ++++++++++++++++++++++++---- src/components/AddDownloadsModal.tsx | 6 ++-- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 171f0a5..c257b02 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -876,8 +876,11 @@ async fn fetch_metadata( user_agent: Option, username: Option, password: Option, + headers: Option, + cookies: Option, ) -> Result { let mut current_url = url.clone(); + let original_host = reqwest::Url::parse(&url).ok().and_then(|u| u.host_str().map(|s| s.to_string())); let mut redirects = 0; let res; @@ -904,21 +907,58 @@ async fn fetch_metadata( builder = builder.resolve(&host, addr); } + let current_host = reqwest::Url::parse(¤t_url).ok().and_then(|u| u.host_str().map(|s| s.to_string())); + let mut should_send_auth = redirects == 0; + if !should_send_auth { + if let (Some(orig), Some(curr)) = (&original_host, ¤t_host) { + if curr == orig || curr.ends_with(&format!(".{}", orig)) { + should_send_auth = true; + } + } + } + + let mut header_map = reqwest::header::HeaderMap::new(); + if should_send_auth { + if let Some(ref h_str) = headers { + for line in h_str.lines() { + if let Some((k, v)) = line.split_once(':') { + if let Ok(name) = reqwest::header::HeaderName::from_bytes(k.trim().as_bytes()) { + if let Ok(value) = reqwest::header::HeaderValue::from_str(v.trim()) { + header_map.insert(name, value); + } + } + } + } + } + if let Some(ref c_str) = cookies { + if !c_str.trim().is_empty() { + if let Ok(value) = reqwest::header::HeaderValue::from_str(c_str.trim()) { + header_map.insert(reqwest::header::COOKIE, value); + } + } + } + } + builder = builder.default_headers(header_map); + let client = builder.build().map_err(|e| e.to_string())?; let mut head_req = client.head(¤t_url); - if let Some(ref user) = username { - if !user.is_empty() { - head_req = head_req.basic_auth(user, password.as_deref()); + if should_send_auth { + if let Some(ref user) = username { + if !user.is_empty() { + head_req = head_req.basic_auth(user, password.as_deref()); + } } } let mut current_res = head_req.send().await.map_err(|e| e.to_string())?; if !current_res.status().is_success() && !current_res.status().is_redirection() { let mut get_req = client.get(¤t_url); - if let Some(ref user) = username { - if !user.is_empty() { - get_req = get_req.basic_auth(user, password.as_deref()); + if should_send_auth { + if let Some(ref user) = username { + if !user.is_empty() { + get_req = get_req.basic_auth(user, password.as_deref()); + } } } current_res = get_req.send().await.map_err(|e| e.to_string())?; diff --git a/src/components/AddDownloadsModal.tsx b/src/components/AddDownloadsModal.tsx index 2ff1751..7d5d1e6 100644 --- a/src/components/AddDownloadsModal.tsx +++ b/src/components/AddDownloadsModal.tsx @@ -247,7 +247,9 @@ export const AddDownloadsModal = () => { url: row.sourceUrl, userAgent: settingsStore.customUserAgent || null, username: useAuth ? username.trim() || null : login?.username || null, - password: useAuth ? password || null : keychainPassword + password: useAuth ? password || null : keychainPassword, + headers: headers?.trim() || null, + cookies: cookies?.trim() || null }); setParsedItems(current => updateRowIfCurrent( current, @@ -291,7 +293,7 @@ export const AddDownloadsModal = () => { } })(); } - }, [parsedItems, pendingAddFilename, password, useAuth, username]); + }, [parsedItems, pendingAddFilename, password, useAuth, username, headers, cookies]); useEffect(() => { if (parsedItems.length === 0) {