envseal 0.3.7

Write-only secret vault with process-level access control — post-agent secret management
Documentation
//! Execution pipeline tests — inject, multi-secret, collision, unicode,
//! policy enforcement, stdio isolation, and hardening smoke tests.

#![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);

    // Both map to SAME_ENV. Current behavior: last value wins, no error.
    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());
}