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(); diff --git a/crates/security-engine/tests/windows_tests.rs b/crates/security-engine/tests/windows_tests.rs index 26d830e..74c70c7 100644 --- a/crates/security-engine/tests/windows_tests.rs +++ b/crates/security-engine/tests/windows_tests.rs @@ -1,4 +1,3 @@ -use security_engine; #[cfg(windows)] #[test] diff --git a/crates/vm-orch/src/guard.rs b/crates/vm-orch/src/guard.rs index aa2f8f8..f041c04 100644 --- a/crates/vm-orch/src/guard.rs +++ b/crates/vm-orch/src/guard.rs @@ -179,7 +179,11 @@ impl CommandExt for Command { fn spawn_guarded(&mut self) -> io::Result<(GuardedChild, Option)> { let program = self.get_program().to_string_lossy().to_string(); let program_path = std::path::Path::new(&program); - let mut program_name = program_path.file_name().unwrap_or_default().to_string_lossy().to_string(); + let mut program_name = program_path + .file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string(); if program_name.to_lowercase().ends_with(".exe") { program_name = program_name[..program_name.len() - 4].to_string(); } @@ -217,7 +221,7 @@ impl CommandExt for Command { let mut has_author_email = false; let mut has_committer_name = false; let mut has_committer_email = false; - + for (k, _) in self.get_envs() { if let Some(key_str) = k.to_str() { match key_str { @@ -233,14 +237,22 @@ impl CommandExt for Command { if !has_author_name || !has_author_email || !has_committer_name || !has_committer_email { let host_name = get_host_git_config("user.name"); let host_email = get_host_git_config("user.email"); - + if let Some(name) = &host_name { - if !has_author_name { self.env("GIT_AUTHOR_NAME", name); } - if !has_committer_name { self.env("GIT_COMMITTER_NAME", name); } + if !has_author_name { + self.env("GIT_AUTHOR_NAME", name); + } + if !has_committer_name { + self.env("GIT_COMMITTER_NAME", name); + } } if let Some(email) = &host_email { - if !has_author_email { self.env("GIT_AUTHOR_EMAIL", email); } - if !has_committer_email { self.env("GIT_COMMITTER_EMAIL", email); } + if !has_author_email { + self.env("GIT_AUTHOR_EMAIL", email); + } + if !has_committer_email { + self.env("GIT_COMMITTER_EMAIL", email); + } } // Also map them via GIT_CONFIG_* so `git config user.name` works @@ -249,12 +261,18 @@ impl CommandExt for Command { let has_git_config_count = self.get_envs().any(|(k, _)| k == "GIT_CONFIG_COUNT"); if !has_git_config_count { if let Some(name) = &host_name { - self.env(format!("GIT_CONFIG_KEY_{}", current_config_count), "user.name"); + self.env( + format!("GIT_CONFIG_KEY_{}", current_config_count), + "user.name", + ); self.env(format!("GIT_CONFIG_VALUE_{}", current_config_count), name); current_config_count += 1; } if let Some(email) = &host_email { - self.env(format!("GIT_CONFIG_KEY_{}", current_config_count), "user.email"); + self.env( + format!("GIT_CONFIG_KEY_{}", current_config_count), + "user.email", + ); self.env(format!("GIT_CONFIG_VALUE_{}", current_config_count), email); current_config_count += 1; } @@ -351,10 +369,18 @@ impl CommandExt for Command { let mut cmd_line_u16: Vec = cmd_line.encode_utf16().chain(std::iter::once(0)).collect(); - let mut desktop_name: Vec = "VesselDesktop" - .encode_utf16() - .chain(std::iter::once(0)) - .collect(); + let mut desktop_name = Vec::new(); + if desktop_name.try_reserve("VesselDesktop".len() + 1).is_err() { + log::error!( + "Process spawn failure: failed to allocate memory for desktop string" + ); + let _ = CloseHandle(job_handle); + return Err(io::Error::new( + io::ErrorKind::OutOfMemory, + "Failed to allocate desktop string", + )); + } + desktop_name.extend("VesselDesktop".encode_utf16().chain(std::iter::once(0))); let mut startup_info = STARTUPINFOW::default(); startup_info.cb = std::mem::size_of::() as u32; @@ -372,7 +398,7 @@ impl CommandExt for Command { env_map.remove(k); } } - + let mut env_block = Vec::new(); for (k, v) in env_map { let k_str = k.to_string_lossy(); @@ -386,7 +412,8 @@ impl CommandExt for Command { } env_block.push(0); // double null termination - let creation_flags = CREATE_SUSPENDED | windows::Win32::System::Threading::CREATE_UNICODE_ENVIRONMENT; + let creation_flags = CREATE_SUSPENDED + | windows::Win32::System::Threading::CREATE_UNICODE_ENVIRONMENT; let create_res = CreateProcessAsUserW( token, @@ -422,6 +449,10 @@ impl CommandExt for Command { if let Err(e) = security_engine::audit_process_desktop(process_info.dwThreadId, "VesselDesktop") { + log::error!( + "Process spawn failure: post-spawn desktop validation failed: {}", + e + ); let _ = windows::Win32::System::Threading::TerminateProcess(process_handle, 1); let _ = CloseHandle(process_handle); let _ = CloseHandle(process_info.hThread); @@ -659,4 +690,33 @@ mod tests { } } } + + #[test] + fn test_git_env_injection() { + let mut cmd = Command::new("git"); + cmd.arg("status"); + + let mut has_author_name = false; + let mut has_author_email = false; + for (k, _) in cmd.get_envs() { + if let Some(key_str) = k.to_str() { + if key_str == "GIT_AUTHOR_NAME" { has_author_name = true; } + if key_str == "GIT_AUTHOR_EMAIL" { has_author_email = true; } + } + } + + // Before spawn_guarded, no envs injected by default + assert!(!has_author_name); + assert!(!has_author_email); + } + + #[test] + fn test_unauthorized_command_blocked() { + let mut cmd = Command::new("malicious_hacker_tool"); + let res = cmd.spawn_guarded(); + match res { + Ok(_) => panic!("Expected permission denied error"), + Err(err) => assert_eq!(err.kind(), io::ErrorKind::PermissionDenied), + } + } }