From 0b6b0de03de531e34d60bc738aabf6cfde0f2f83 Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 24 Jun 2026 15:11:46 +0000 Subject: [PATCH 1/7] feat(vessel): implement global taskbar flash prevention --- Cargo.lock | 7 --- crates/vessel/src/main.rs | 17 +++++++ crates/vessel/src/sanitation.rs | 78 +++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 322ebd1..e1572fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1998,13 +1998,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "win_test" -version = "0.1.0" -dependencies = [ - "windows", -] - [[package]] name = "winapi" version = "0.3.9" diff --git a/crates/vessel/src/main.rs b/crates/vessel/src/main.rs index c74af88..a08a888 100644 --- a/crates/vessel/src/main.rs +++ b/crates/vessel/src/main.rs @@ -104,6 +104,22 @@ async fn main() -> Result<()> { Commands::Start => { log::info!("Starting Vessel agent..."); + tokio::spawn(async move { + if let Ok(_) = tokio::signal::ctrl_c().await { + log::info!("Received Ctrl-C, initiating shutdown sequence..."); + sanitation::restore_system_defaults(); + std::process::exit(0); + } + }); + + struct ShutdownGuard; + impl Drop for ShutdownGuard { + fn drop(&mut self) { + sanitation::restore_system_defaults(); + } + } + let _shutdown_guard = ShutdownGuard; + sanitation::clean_high_risk_directories(); // Create secondary desktop before security initialization so that @@ -122,6 +138,7 @@ async fn main() -> Result<()> { Commands::Stop => { log::info!("Stopping Vessel agent..."); // TODO: Clean up isolated desktop sessions and guest applications + sanitation::restore_system_defaults(); } Commands::Credentials(args) => { match args.command { diff --git a/crates/vessel/src/sanitation.rs b/crates/vessel/src/sanitation.rs index 4c879e3..da85f24 100644 --- a/crates/vessel/src/sanitation.rs +++ b/crates/vessel/src/sanitation.rs @@ -9,7 +9,13 @@ mod windows_impl { type DWORD = u32; type WCHAR = u16; + type PVOID = *mut std::ffi::c_void; + const MAXIMUM_ALLOWED: DWORD = 0x02000000; + const SPI_GETFOREGROUNDLOCKTIMEOUT: DWORD = 0x2000; + const SPI_SETFOREGROUNDLOCKTIMEOUT: DWORD = 0x2001; + const SPIF_UPDATEINIFILE: DWORD = 0x01; + const SPIF_SENDCHANGE: DWORD = 0x02; type DESKTOPENUMPROCW = Option BOOL>; @@ -34,6 +40,19 @@ mod windows_impl { dwDesiredAccess: DWORD, ) -> HDESK; fn CloseDesktop(hDesktop: HDESK) -> BOOL; + fn SystemParametersInfoW( + uiAction: DWORD, + uiParam: DWORD, + pvParam: PVOID, + fWinIni: DWORD, + ) -> BOOL; + } + + static ORIGINAL_FOREGROUND_LOCK_TIMEOUT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); + static TIMEOUT_CAPTURED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); + + fn get_timeout_backup_path() -> std::path::PathBuf { + std::env::temp_dir().join("vessel_foreground_timeout.txt") } unsafe extern "system" fn enum_desktops_callback( @@ -90,6 +109,30 @@ mod windows_impl { let count = AtomicUsize::new(0); unsafe { + let mut timeout: DWORD = 0; + let res = SystemParametersInfoW( + SPI_GETFOREGROUNDLOCKTIMEOUT, + 0, + &mut timeout as *mut _ as PVOID, + 0, + ); + if res != 0 { + ORIGINAL_FOREGROUND_LOCK_TIMEOUT.store(timeout as usize, Ordering::SeqCst); + TIMEOUT_CAPTURED.store(true, Ordering::SeqCst); + let _ = std::fs::write(get_timeout_backup_path(), timeout.to_string()); + log::info!("Sanitation: Captured original ForegroundLockTimeout: {} ms", timeout); + + let new_timeout: DWORD = u32::MAX; + SystemParametersInfoW( + SPI_SETFOREGROUNDLOCKTIMEOUT, + 0, + new_timeout as usize as PVOID, + SPIF_UPDATEINIFILE | SPIF_SENDCHANGE, + ); + } else { + log::warn!("Sanitation: Failed to capture original ForegroundLockTimeout"); + } + EnumWindowStationsW( Some(enum_window_stations_callback), &count as *const _ as LPARAM, @@ -103,6 +146,41 @@ mod windows_impl { Ok(()) } + + pub fn restore_timeout() { + let mut timeout_to_restore = None; + + if TIMEOUT_CAPTURED.load(Ordering::SeqCst) { + timeout_to_restore = Some(ORIGINAL_FOREGROUND_LOCK_TIMEOUT.load(Ordering::SeqCst) as DWORD); + } else { + // Try to read from backup file in case this is a separate process (e.g. vessel stop) + if let Ok(content) = std::fs::read_to_string(get_timeout_backup_path()) { + if let Ok(parsed) = content.trim().parse::() { + timeout_to_restore = Some(parsed); + } + } + } + + if let Some(timeout) = timeout_to_restore { + unsafe { + SystemParametersInfoW( + SPI_SETFOREGROUNDLOCKTIMEOUT, + 0, + timeout as usize as PVOID, + SPIF_UPDATEINIFILE | SPIF_SENDCHANGE, + ); + } + let _ = std::fs::remove_file(get_timeout_backup_path()); + log::info!("Sanitation: Restored original ForegroundLockTimeout: {} ms", timeout); + } + } +} + +pub fn restore_system_defaults() { + #[cfg(windows)] + { + windows_impl::restore_timeout(); + } } pub fn sanitize_vessel_desktops() -> anyhow::Result<()> { From 704c700b21b06330cf90981316769612e3293b07 Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 24 Jun 2026 15:54:08 +0000 Subject: [PATCH 2/7] test(vessel): add tests for taskbar flash prevention logic --- crates/vessel/src/sanitation.rs | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/crates/vessel/src/sanitation.rs b/crates/vessel/src/sanitation.rs index da85f24..1b9286b 100644 --- a/crates/vessel/src/sanitation.rs +++ b/crates/vessel/src/sanitation.rs @@ -174,6 +174,25 @@ mod windows_impl { log::info!("Sanitation: Restored original ForegroundLockTimeout: {} ms", timeout); } } + + #[cfg(test)] + mod tests { + use super::*; + use std::sync::atomic::Ordering; + + #[test] + fn test_get_timeout_backup_path() { + let path = get_timeout_backup_path(); + assert!(path.to_string_lossy().contains("vessel_foreground_timeout.txt")); + } + + #[test] + fn test_timeout_captured_initial_state() { + // Because this is global state, we can't assume it's strictly false if other tests run, + // but we can test we can read it. + let _ = TIMEOUT_CAPTURED.load(Ordering::SeqCst); + } + } } pub fn restore_system_defaults() { @@ -285,3 +304,21 @@ pub fn clean_high_risk_directories() { wipe_dir(&target); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_restore_system_defaults_does_not_panic() { + // This should run smoothly and not panic on any platform. + restore_system_defaults(); + } + + #[test] + fn test_sanitize_vessel_desktops_does_not_panic() { + // This should not panic + let res = sanitize_vessel_desktops(); + assert!(res.is_ok()); + } +} From d8246475d4df289c321a2e57f08b97d2168fa0b9 Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 24 Jun 2026 16:13:11 +0000 Subject: [PATCH 3/7] fix(sanitation): pass valid pointers to SystemParametersInfoW for SPI_SETFOREGROUNDLOCKTIMEOUT --- crates/vessel/src/sanitation.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/vessel/src/sanitation.rs b/crates/vessel/src/sanitation.rs index 1b9286b..9b6633d 100644 --- a/crates/vessel/src/sanitation.rs +++ b/crates/vessel/src/sanitation.rs @@ -122,11 +122,11 @@ mod windows_impl { let _ = std::fs::write(get_timeout_backup_path(), timeout.to_string()); log::info!("Sanitation: Captured original ForegroundLockTimeout: {} ms", timeout); - let new_timeout: DWORD = u32::MAX; + let new_timeout: DWORD = 0; // 0 allows focus stealing, preventing taskbar flash SystemParametersInfoW( SPI_SETFOREGROUNDLOCKTIMEOUT, - 0, - new_timeout as usize as PVOID, + new_timeout, + 0 as PVOID, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE, ); } else { @@ -165,8 +165,8 @@ mod windows_impl { unsafe { SystemParametersInfoW( SPI_SETFOREGROUNDLOCKTIMEOUT, - 0, - timeout as usize as PVOID, + timeout, + 0 as PVOID, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE, ); } From bbfdfab7629486a5b857cedebaffc73f399aace0 Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 24 Jun 2026 16:35:56 +0000 Subject: [PATCH 4/7] chore: remove unused import in tests --- crates/security-engine/tests/windows_tests.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/security-engine/tests/windows_tests.rs b/crates/security-engine/tests/windows_tests.rs index 26d830e..343f9d2 100644 --- a/crates/security-engine/tests/windows_tests.rs +++ b/crates/security-engine/tests/windows_tests.rs @@ -1,5 +1,3 @@ -use security_engine; - #[cfg(windows)] #[test] fn test_get_vessel_credentials_consistency() { From d60d4eefafc152b39500f0fff3659a064ccd8d61 Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 24 Jun 2026 16:46:03 +0000 Subject: [PATCH 5/7] fix(windows): resolve Zeroizing type mismatch in get_vessel_credentials --- crates/security-engine/src/windows.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/security-engine/src/windows.rs b/crates/security-engine/src/windows.rs index 2345459..6f0d7f4 100644 --- a/crates/security-engine/src/windows.rs +++ b/crates/security-engine/src/windows.rs @@ -67,7 +67,7 @@ pub fn get_vessel_credentials() -> (String, String) { CREDENTIALS.get_or_init(|| { let store = PlatformCredentialStore {}; let user = match store.get_credential_by_target("Antigravity/VesselUser") { - Ok(Some(u)) => u, + Ok(Some(u)) => u.to_string(), _ => { let mut rng = rand::thread_rng(); let random_suffix: String = (&mut rng).sample_iter(&Alphanumeric).take(8).map(char::from).collect(); @@ -78,7 +78,7 @@ pub fn get_vessel_credentials() -> (String, String) { }; let pass = match store.get_credential_by_target("Antigravity/VesselPassword") { - Ok(Some(p)) => p, + Ok(Some(p)) => p.to_string(), _ => { let mut rng = rand::thread_rng(); let new_pass: String = (&mut rng).sample_iter(&Alphanumeric).take(20).map(char::from).collect(); From 5bf70ab2685f77093d359409f5c63edf1e00dfda Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 24 Jun 2026 16:50:41 +0000 Subject: [PATCH 6/7] Fix dead code and snake_case warnings in sanitation module --- crates/vessel/src/sanitation.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/vessel/src/sanitation.rs b/crates/vessel/src/sanitation.rs index 9504d8f..d341c7a 100644 --- a/crates/vessel/src/sanitation.rs +++ b/crates/vessel/src/sanitation.rs @@ -18,13 +18,12 @@ mod windows_impl { const SPIF_SENDCHANGE: DWORD = 0x02; type DESKTOPENUMPROCW = - Option BOOL>; + Option BOOL>; type WINSTAENUMPROCW = - Option BOOL>; + Option BOOL>; #[link(name = "user32")] extern "system" { - fn GetProcessWindowStation() -> HWINSTA; fn EnumWindowStationsW(lpEnumFunc: WINSTAENUMPROCW, lParam: LPARAM) -> BOOL; fn EnumDesktopsW(hwinsta: HWINSTA, lpEnumFunc: DESKTOPENUMPROCW, lParam: LPARAM) -> BOOL; fn OpenWindowStationW( From ba8c81cba2fe590f28d39d65e567dfd9311bcadf Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 24 Jun 2026 16:58:10 +0000 Subject: [PATCH 7/7] fix(sanitation): pass valid timeout as pvParam for SPI_SETFOREGROUNDLOCKTIMEOUT --- crates/vessel/src/sanitation.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/vessel/src/sanitation.rs b/crates/vessel/src/sanitation.rs index d341c7a..e9b6ff9 100644 --- a/crates/vessel/src/sanitation.rs +++ b/crates/vessel/src/sanitation.rs @@ -124,8 +124,8 @@ mod windows_impl { let new_timeout: DWORD = 0; // 0 allows focus stealing, preventing taskbar flash SystemParametersInfoW( SPI_SETFOREGROUNDLOCKTIMEOUT, - new_timeout, - 0 as PVOID, + 0, + new_timeout as usize as PVOID, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE, ); } else { @@ -164,8 +164,8 @@ mod windows_impl { unsafe { SystemParametersInfoW( SPI_SETFOREGROUNDLOCKTIMEOUT, - timeout, - 0 as PVOID, + 0, + timeout as usize as PVOID, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE, ); }