Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/security-engine/src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
Expand Down
1 change: 0 additions & 1 deletion crates/security-engine/tests/windows_tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use security_engine;

#[cfg(windows)]
#[test]
Expand Down
90 changes: 75 additions & 15 deletions crates/vm-orch/src/guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,11 @@ impl CommandExt for Command {
fn spawn_guarded(&mut self) -> io::Result<(GuardedChild, Option<ResourceGuard>)> {
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();
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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;
}
Expand Down Expand Up @@ -351,10 +369,18 @@ impl CommandExt for Command {
let mut cmd_line_u16: Vec<u16> =
cmd_line.encode_utf16().chain(std::iter::once(0)).collect();

let mut desktop_name: Vec<u16> = "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::<STARTUPINFOW>() as u32;
Expand All @@ -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();
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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),
}
}
}
Loading