#![allow(clippy::duplicate_mod)]
#[allow(unused_imports)]
use envseal::error::Error;
#[cfg(unix)]
use envseal::execution::prepare_execution;
use envseal::execution::{
execute_multi_with_options, harden_child_process_inner, InjectExecOptions,
};
use envseal::gui::mock::{clear_mock, set_mock_approval, MockApprovalResponse};
use envseal::vault::Vault;
#[path = "../common/mod.rs"]
mod common;
fn setup_vault_with_secrets(secrets: &[(&str, &[u8])]) -> (tempfile::TempDir, Vault) {
let (dir, vault) = common::temp_vault();
for (name, value) in secrets {
vault.store(name, value, false).unwrap();
}
(dir, vault)
}
#[cfg(unix)]
#[test]
fn execute_multi_with_options_multiple_secrets() {
let (_dir, vault) =
setup_vault_with_secrets(&[("api-key", b"secret1"), ("db-pass", b"secret2")]);
set_mock_approval(MockApprovalResponse::AllowOnce);
let result = execute_multi_with_options(
&vault,
&[("api-key", "API_KEY"), ("db-pass", "DB_PASS")],
&["/usr/bin/true".to_string()],
InjectExecOptions::default(),
);
clear_mock();
assert!(
result.is_ok(),
"multi-secret inject should succeed: {result:?}"
);
}
#[cfg(unix)]
#[test]
fn env_var_name_collision_last_wins() {
let (_dir, vault) = setup_vault_with_secrets(&[("first", b"value1"), ("second", b"value2")]);
set_mock_approval(MockApprovalResponse::AllowOnce);
let result = execute_multi_with_options(
&vault,
&[("first", "SAME_ENV"), ("second", "SAME_ENV")],
&["/usr/bin/true".to_string()],
InjectExecOptions::default(),
);
clear_mock();
assert!(
result.is_ok(),
"collision should not error, last value wins: {result:?}"
);
}
#[test]
fn unicode_env_var_name_rejected() {
let (_dir, vault) = setup_vault_with_secrets(&[("secret", b"value")]);
set_mock_approval(MockApprovalResponse::AllowOnce);
let result = execute_multi_with_options(
&vault,
&[("secret", "SÉCRÈT")],
&["/usr/bin/true".to_string()],
InjectExecOptions::default(),
);
clear_mock();
assert!(
matches!(result, Err(Error::BinaryResolution(_))),
"unicode env var name should be rejected: {result:?}"
);
}
#[cfg(unix)]
#[test]
fn prepare_execution_denies_unauthorized_binary() {
let (_dir, vault) = setup_vault_with_secrets(&[("secret", b"value")]);
set_mock_approval(MockApprovalResponse::Deny);
let result = prepare_execution(
&vault,
&[("secret", "SECRET")],
&["/usr/bin/true".to_string()],
);
clear_mock();
assert!(
matches!(result, Err(Error::UserDenied)),
"unauthorized binary should be denied with UserDenied"
);
}
#[cfg(unix)]
#[test]
fn stdio_isolation_does_not_panic() {
let (_dir, vault) = setup_vault_with_secrets(&[("secret", b"value")]);
set_mock_approval(MockApprovalResponse::AllowOnce);
let result = execute_multi_with_options(
&vault,
&[("secret", "SECRET")],
&[
"/usr/bin/dash".to_string(),
"-c".to_string(),
"echo leaked".to_string(),
],
InjectExecOptions {
isolate_stdio: true,
},
);
clear_mock();
assert!(
result.is_ok(),
"stdio isolation should not panic: {result:?}"
);
}
#[cfg(target_os = "linux")]
#[test]
fn harden_child_process_inner_smoke() {
use std::os::unix::process::CommandExt;
use std::process::Command;
let mut cmd = Command::new("/usr/bin/true");
unsafe {
cmd.pre_exec(|| harden_child_process_inner());
}
let status = cmd.status().expect("spawn hardened child");
assert!(status.success());
}
#[cfg(not(target_os = "linux"))]
#[test]
fn harden_child_process_inner_noop() {
assert!(harden_child_process_inner().is_ok());
}