fix: save_state captures cross-domain cookies and localStorage (#1064)

The Rust rewrite of save_state only captured cookies and localStorage
for the current page's origin, silently dropping cross-domain data
(e.g. SSO/CAS auth cookies). This was a regression from the JS version.

Cookies: replace Network.getCookies with Network.getAllCookies to
return cookies from all domains the browser has visited.

localStorage: track visited origins in BrowserManager during navigation,
then collect their localStorage via a temporary CDP target with Fetch
interception (serves blank HTML to avoid real network requests).

Co-authored-by: hyunjinee <leehj0110@kakao.com>
This commit is contained in:
jin.2
2026-03-28 13:41:53 -07:00
committed by GitHub
co-authored by hyunjinee
parent 747a3772e1
commit dc26ff7667
5 changed files with 414 additions and 42 deletions
+17
View File
@@ -199,6 +199,8 @@ pub struct BrowserManager {
default_timeout_ms: u64,
/// Stored download path from launch options, re-applied to new contexts (e.g., recording)
pub download_path: Option<String>,
/// Origins visited during this session, used by save_state to collect cross-origin localStorage.
visited_origins: HashSet<String>,
}
const LIGHTPANDA_CDP_CONNECT_TIMEOUT: Duration = Duration::from_secs(5);
@@ -268,6 +270,7 @@ impl BrowserManager {
active_page_index: 0,
default_timeout_ms: 25_000,
download_path: download_path.clone(),
visited_origins: HashSet::new(),
};
manager.discover_and_attach_targets().await?;
manager
@@ -333,6 +336,7 @@ impl BrowserManager {
active_page_index: 0,
default_timeout_ms: 10_000,
download_path: None, // CDP connections don't have a launch-time download path
visited_origins: HashSet::new(),
};
manager.discover_and_attach_targets().await?;
@@ -494,6 +498,14 @@ impl BrowserManager {
let page_url = self.get_url().await.unwrap_or_else(|_| url.to_string());
let title = self.get_title().await.unwrap_or_default();
// Track visited origin for cross-origin localStorage collection in save_state
if let Ok(parsed) = url::Url::parse(&page_url) {
let origin = parsed.origin().ascii_serialization();
if origin != "null" {
self.visited_origins.insert(origin);
}
}
if let Some(page) = self.pages.get_mut(self.active_page_index) {
page.url = page_url.clone();
page.title = title.clone();
@@ -1129,6 +1141,10 @@ impl BrowserManager {
self.pages.clone()
}
pub fn visited_origins(&self) -> &HashSet<String> {
&self.visited_origins
}
pub async fn set_download_behavior(&self, download_path: &str) -> Result<(), String> {
let session_id = self.active_session_id()?;
self.client
@@ -1276,6 +1292,7 @@ async fn initialize_lightpanda_manager(
active_page_index: 0,
default_timeout_ms: 25_000,
download_path: None,
visited_origins: HashSet::new(),
};
match discover_and_attach_lightpanda_targets(&mut manager, deadline).await {