use assert_cmd::Command;
use predicates::prelude::*;
use tempfile::tempdir;
fn tsafe() -> Command {
Command::cargo_bin("tsafe").unwrap()
}
fn vault_dir(dir: &tempfile::TempDir) -> std::path::PathBuf {
let vaults = dir.path().join("vaults");
std::fs::create_dir_all(&vaults).unwrap();
vaults
}
fn audit_log_path(vaults: &std::path::Path, profile: &str) -> std::path::PathBuf {
vaults
.parent()
.unwrap()
.join("state")
.join("audit")
.join(format!("{profile}.audit.jsonl"))
}
fn exec_args(prefix: &[&str], child_cmd: Vec<String>) -> Vec<String> {
let mut args = prefix.iter().map(|s| s.to_string()).collect::<Vec<_>>();
args.push("--".to_string());
args.extend(child_cmd);
args
}
fn env_dump_child_cmd() -> Vec<String> {
#[cfg(not(target_os = "windows"))]
{
vec!["printenv".to_string()]
}
#[cfg(target_os = "windows")]
{
vec!["cmd".to_string(), "/c".to_string(), "set".to_string()]
}
}
fn env_value_child_cmd(name: &str) -> Vec<String> {
#[cfg(not(target_os = "windows"))]
{
vec!["printenv".to_string(), name.to_string()]
}
#[cfg(target_os = "windows")]
{
vec![
"cmd".to_string(),
"/c".to_string(),
"echo".to_string(),
format!("%{name}%"),
]
}
}
fn success_probe_child_cmd() -> Vec<String> {
env_value_child_cmd("PATH")
}
fn success_probe_target() -> &'static str {
#[cfg(not(target_os = "windows"))]
{
"printenv"
}
#[cfg(target_os = "windows")]
{
"cmd"
}
}
#[test]
fn exec_injects_secrets_into_child_env() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "INJECTED_VAR", "hello-from-vault"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let child_cmd = exec_args(&["exec"], env_value_child_cmd("INJECTED_VAR"));
tsafe()
.args(child_cmd)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("hello-from-vault"));
}
#[test]
fn exec_default_injects_all_vault_secrets_with_no_flags() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
for (key, value) in [
("BROAD_DEFAULT_FOO", "foo-from-vault"),
("BROAD_DEFAULT_BAR", "bar-from-vault"),
] {
tsafe()
.args(["set", key, value])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
}
let assert = tsafe()
.args(["exec", "--dry-run"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let stdout = String::from_utf8(assert.get_output().stdout.clone()).unwrap();
assert!(
stdout.contains("BROAD_DEFAULT_FOO"),
"no-flag exec must include FOO key in dry-run; stdout was {stdout:?}"
);
assert!(
stdout.contains("BROAD_DEFAULT_BAR"),
"no-flag exec must include BAR key in dry-run; stdout was {stdout:?}"
);
}
#[test]
fn exec_strips_tsafe_password_from_child() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let env_dump = exec_args(&["exec"], env_dump_child_cmd());
tsafe()
.args(env_dump)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("TSAFE_PASSWORD").not());
}
#[test]
fn exec_empty_command_fails() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["exec"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure();
}
#[test]
fn exec_dry_run_lists_keys_sorted() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "ZEBRA", "z"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "APPLE", "a"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let assert = tsafe()
.args(["exec", "--dry-run"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let out = String::from_utf8(assert.get_output().stdout.clone()).unwrap();
assert_eq!(out, "APPLE\nZEBRA\n");
}
#[test]
fn exec_require_fails_when_key_missing() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "ONLY", "x"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(
&["exec", "--require", "ONLY,MISSING"],
success_probe_child_cmd(),
))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure()
.stderr(predicates::str::contains(
"required secret 'MISSING' not found",
));
}
#[test]
fn exec_require_succeeds_when_keys_present() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "REQ_A", "a"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "REQ_B", "b"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let child_cmd = exec_args(
&["exec", "--require", "REQ_A", "--require", "REQ_B"],
env_value_child_cmd("REQ_A"),
);
tsafe()
.args(child_cmd)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("a"));
}
#[test]
fn exec_aborts_on_dangerous_env_name_by_default() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "NODE_OPTIONS", "--", "--inspect"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(&["exec"], success_probe_child_cmd()))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure()
.stderr(predicates::str::contains("DANGEROUS_ENV_VARIABLE"));
}
#[test]
fn exec_allow_dangerous_env_injects_with_warning() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "NODE_OPTIONS", "--", "--inspect"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let child_cmd = exec_args(
&["exec", "--allow-dangerous-env"],
env_value_child_cmd("NODE_OPTIONS"),
);
tsafe()
.args(child_cmd)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("--inspect"))
.stderr(predicates::str::contains("NODE_OPTIONS"));
}
#[test]
fn exec_deny_dangerous_env_aborts() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "LD_PRELOAD", "/tmp/evil.so"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(
&["exec", "--deny-dangerous-env"],
success_probe_child_cmd(),
))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure()
.stderr(predicates::str::contains("LD_PRELOAD"));
}
#[test]
fn exec_missing_required_secret_writes_failure_audit_context() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(
&["exec", "--require", "MISSING_KEY"],
success_probe_child_cmd(),
))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure()
.stderr(predicates::str::contains(
"required secret 'MISSING_KEY' not found",
));
let audit_path = audit_log_path(&vaults, "default");
let content = std::fs::read_to_string(audit_path).unwrap();
let last = content.lines().last().unwrap();
let entry: serde_json::Value = serde_json::from_str(last).unwrap();
assert_eq!(entry["operation"], "exec");
assert_eq!(entry["status"], "failure");
assert_eq!(
entry["context"]["exec"]["missing_required_secrets"],
serde_json::json!(["MISSING_KEY"])
);
assert_eq!(
entry["context"]["exec"]["deny_reason"],
"REQUIRED_SECRET_NOT_FOUND"
);
}
#[test]
fn exec_dangerous_env_denial_writes_failure_audit_context() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "NODE_OPTIONS", "--", "--inspect"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(&["exec"], success_probe_child_cmd()))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure()
.stderr(predicates::str::contains("DANGEROUS_ENV_VARIABLE"));
let audit_path = audit_log_path(&vaults, "default");
let content = std::fs::read_to_string(audit_path).unwrap();
let last = content.lines().last().unwrap();
let entry: serde_json::Value = serde_json::from_str(last).unwrap();
assert_eq!(entry["operation"], "exec");
assert_eq!(entry["status"], "failure");
assert_eq!(
entry["context"]["exec"]["deny_reason"],
"DANGEROUS_ENV_VARIABLE"
);
}
#[test]
fn exec_plan_lists_injected_keys_and_run_line() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "PLAN_KEY_A", "a"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "PLAN_KEY_B", "b"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let assert = tsafe()
.args(["exec", "--plan", "--", "npm", "start"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let out = String::from_utf8(assert.get_output().stdout.clone()).unwrap();
assert!(out.contains("PLAN_KEY_A"), "expected PLAN_KEY_A in plan");
assert!(out.contains("PLAN_KEY_B"), "expected PLAN_KEY_B in plan");
assert!(!out.contains('"'), "values must not be quoted in plan");
assert!(
out.contains("exec -- npm start"),
"expected run line with command"
);
}
#[test]
fn exec_plan_shows_require_satisfaction() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "PRESENT_KEY", "v"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let assert = tsafe()
.args(exec_args(
&["exec", "--plan", "--require", "PRESENT_KEY,ABSENT_KEY"],
success_probe_child_cmd(),
))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let out = String::from_utf8(assert.get_output().stdout.clone()).unwrap();
assert!(out.contains("PRESENT_KEY"), "expected PRESENT_KEY");
assert!(out.contains("ABSENT_KEY"), "expected ABSENT_KEY");
assert!(
out.contains('✓') || out.contains("present"),
"expected present marker"
);
assert!(
out.contains('✗') || out.contains("not in vault"),
"expected missing marker"
);
}
#[test]
fn exec_plan_works_with_no_command() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["exec", "--plan"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
}
#[test]
fn exec_minimal_keeps_path_and_excludes_tokens() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "VAULT_VAL", "from-vault"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let env_dump = exec_args(&["exec", "--minimal"], env_dump_child_cmd());
{
let out = tsafe()
.args(env_dump)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.env("ARBITRARY_TOKEN", "should-not-leak")
.env("MY_SECRET_KEY", "also-not-leak")
.assert()
.success()
.get_output()
.stdout
.clone();
let out = String::from_utf8(out).unwrap();
assert!(
out.to_ascii_lowercase().contains("path="),
"PATH should be kept"
);
assert!(out.contains("VAULT_VAL"), "vault key must be present");
assert!(
!out.contains("ARBITRARY_TOKEN"),
"arbitrary parent token must not leak"
);
assert!(
!out.contains("MY_SECRET_KEY"),
"arbitrary parent key must not leak"
);
}
}
#[test]
fn exec_no_inherit_excludes_parent_env() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "CLEAN_KEY", "fromvault"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let env_dump = exec_args(&["exec", "--no-inherit"], env_dump_child_cmd());
tsafe()
.args(env_dump)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.env("PARENT_ONLY_VAR", "should-not-appear")
.assert()
.success()
.stdout(predicates::str::contains("PARENT_ONLY_VAR").not())
.stdout(predicates::str::contains("CLEAN_KEY")); }
#[test]
fn exec_only_limits_inherited_env() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "VAULT_KEY", "vval"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let env_dump = exec_args(&["exec", "--only", "ALLOWED_VAR"], env_dump_child_cmd());
tsafe()
.args(env_dump)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.env("ALLOWED_VAR", "yes")
.env("BLOCKED_VAR", "no")
.assert()
.success()
.stdout(predicates::str::contains("ALLOWED_VAR"))
.stdout(predicates::str::contains("BLOCKED_VAR").not())
.stdout(predicates::str::contains("VAULT_KEY")); }
#[test]
fn exec_strips_github_token_from_child_when_not_in_vault() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "OTHER", "v"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let env_dump = exec_args(&["exec"], env_dump_child_cmd());
tsafe()
.args(env_dump)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.env("GITHUB_TOKEN", "parent-token-should-not-leak")
.assert()
.success()
.stdout(predicates::str::contains("GITHUB_TOKEN").not());
}
#[test]
fn exec_strips_configured_extra_parent_env_var() {
let dir = tempdir().unwrap();
let vaults = dir.path().join("vaults");
std::fs::create_dir_all(&vaults).unwrap();
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["config", "add-exec-extra-strip", "OPENAI_API_KEY"])
.env("TSAFE_VAULT_DIR", &vaults)
.assert()
.success();
let env_dump = exec_args(&["exec"], env_dump_child_cmd());
tsafe()
.args(env_dump)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.env("OPENAI_API_KEY", "parent-token-should-not-leak")
.assert()
.success()
.stdout(predicates::str::contains("OPENAI_API_KEY").not());
}
#[test]
fn exec_redact_output_flag_scrubs_child_stdout() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "LEAK_ME", "super-secret-output"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let child = exec_args(&["exec", "--redact-output"], env_value_child_cmd("LEAK_ME"));
tsafe()
.args(child)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("[REDACTED]"))
.stdout(predicates::str::contains("super-secret-output").not());
}
#[test]
fn exec_config_auto_redact_output_can_be_overridden_per_run() {
let dir = tempdir().unwrap();
let vaults = dir.path().join("vaults");
std::fs::create_dir_all(&vaults).unwrap();
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "MODEL_TOKEN", "llm-token-123"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["config", "set-exec-redact-output", "on"])
.env("TSAFE_VAULT_DIR", &vaults)
.assert()
.success();
let redact_child = exec_args(&["exec"], env_value_child_cmd("MODEL_TOKEN"));
let plain_child = exec_args(
&["exec", "--no-redact-output"],
env_value_child_cmd("MODEL_TOKEN"),
);
tsafe()
.args(redact_child)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("[REDACTED]"))
.stdout(predicates::str::contains("llm-token-123").not());
tsafe()
.args(plain_child)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("llm-token-123"));
}
#[test]
fn exec_mode_hardened_redacts_output_without_extra_flags() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "MODE_TOKEN", "preset-secret"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let child = exec_args(
&["exec", "--mode", "hardened"],
env_value_child_cmd("MODE_TOKEN"),
);
tsafe()
.args(child)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("[REDACTED]"))
.stdout(predicates::str::contains("preset-secret").not());
}
#[test]
fn exec_mode_hardened_denies_dangerous_env_without_extra_flag() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "NODE_OPTIONS", "--", "--inspect"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(
&["exec", "--mode", "hardened"],
success_probe_child_cmd(),
))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure()
.stderr(predicates::str::contains("NODE_OPTIONS"));
}
#[test]
fn exec_config_mode_hardened_applies_by_default() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "CFG_TOKEN", "config-secret"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["config", "set-exec-mode", "hardened"])
.env("TSAFE_VAULT_DIR", &vaults)
.assert()
.success();
let child = exec_args(&["exec"], env_value_child_cmd("CFG_TOKEN"));
tsafe()
.args(child)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("[REDACTED]"))
.stdout(predicates::str::contains("config-secret").not());
}
#[test]
fn exec_mode_custom_can_use_clean_inherit_from_config() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "CUSTOM_KEY", "vault-secret"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["config", "set-exec-mode", "custom"])
.env("TSAFE_VAULT_DIR", &vaults)
.assert()
.success();
tsafe()
.args(["config", "set-exec-custom-inherit", "clean"])
.env("TSAFE_VAULT_DIR", &vaults)
.assert()
.success();
let env_dump = exec_args(&["exec"], env_dump_child_cmd());
tsafe()
.args(env_dump)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.env("PARENT_ONLY_VAR", "should-not-appear")
.assert()
.success()
.stdout(predicates::str::contains("PARENT_ONLY_VAR").not())
.stdout(predicates::str::contains("CUSTOM_KEY"));
}
#[test]
fn exec_mode_standard_overrides_hardened_config() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "OVERRIDE_TOKEN", "plain-output-ok"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["config", "set-exec-mode", "hardened"])
.env("TSAFE_VAULT_DIR", &vaults)
.assert()
.success();
let child = exec_args(
&["exec", "--mode", "standard"],
env_value_child_cmd("OVERRIDE_TOKEN"),
);
tsafe()
.args(child)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("plain-output-ok"))
.stdout(predicates::str::contains("[REDACTED]").not());
}
#[test]
fn exec_keys_limits_injected_secrets() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "API_KEY", "api-secret"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "DB_URL", "postgres://db"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "UNUSED_SECRET", "leave-me-out"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let env_dump = exec_args(&["exec", "--keys", "API_KEY,DB_URL"], env_dump_child_cmd());
tsafe()
.args(env_dump)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("API_KEY"))
.stdout(predicates::str::contains("DB_URL"))
.stdout(predicates::str::contains("UNUSED_SECRET").not());
}
#[test]
fn exec_keys_apply_after_namespace_mapping() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "agents/OPENAI_API_KEY", "agent-secret"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "agents/DB_URL", "postgres://agent-db"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let env_dump = exec_args(
&["exec", "--ns", "agents", "--keys", "OPENAI_API_KEY"],
env_dump_child_cmd(),
);
tsafe()
.args(env_dump)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("OPENAI_API_KEY"))
.stdout(predicates::str::contains("DB_URL").not());
}
#[test]
fn exec_keys_missing_name_fails_fast() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "ONLY_THIS", "present"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(
&["exec", "--keys", "ONLY_THIS,MISSING_KEY"],
success_probe_child_cmd(),
))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure()
.stderr(predicates::str::contains("selected key(s) not found"))
.stderr(predicates::str::contains("MISSING_KEY"));
}
#[test]
fn exec_plan_shows_key_selection_and_run_line() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "OPENAI_API_KEY", "agent-secret"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let assert = tsafe()
.args([
"exec",
"--plan",
"--keys",
"OPENAI_API_KEY,MISSING_KEY",
"--",
"npm",
"test",
])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let out = String::from_utf8(assert.get_output().stdout.clone()).unwrap();
assert!(
out.contains("--keys selection"),
"expected selection section"
);
assert!(out.contains("OPENAI_API_KEY"), "expected selected key");
assert!(out.contains("MISSING_KEY"), "expected missing selected key");
assert!(
out.contains("--keys OPENAI_API_KEY,MISSING_KEY -- npm test"),
"expected run line to include --keys"
);
}
#[test]
fn exec_contract_applies_profile_namespace_allowlist_and_hardened_mode() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
#[cfg(not(target_os = "windows"))]
let (allowed_target, child_args): (&str, &[&str]) = (
"printenv",
&["exec", "--contract", "deploy", "--", "printenv"],
);
#[cfg(target_os = "windows")]
let (allowed_target, child_args): (&str, &[&str]) = (
"cmd",
&["exec", "--contract", "deploy", "--", "cmd", "/c", "set"],
);
std::fs::write(
dir.path().join(".tsafe.yml"),
format!(
r#"
contracts:
deploy:
profile: work
namespace: infra
allowed_secrets:
- DB_PASSWORD
- API_KEY
required_secrets:
- DB_PASSWORD
allowed_targets:
- {allowed_target}
trust_level: hardened
"#
),
)
.unwrap();
tsafe()
.args(["--profile", "work", "init"])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["--profile", "work", "set", "infra/DB_PASSWORD", "db-secret"])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["--profile", "work", "set", "infra/API_KEY", "api-secret"])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args([
"--profile",
"work",
"set",
"infra/UNUSED_SECRET",
"leave-me-out",
])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(child_args)
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("DB_PASSWORD="))
.stdout(predicates::str::contains("API_KEY="))
.stdout(predicates::str::contains("[REDACTED]"))
.stdout(predicates::str::contains("UNUSED_SECRET").not());
}
#[test]
fn exec_contract_blocks_keys_outside_allowlist() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
let allowed_target = success_probe_target();
std::fs::write(
dir.path().join(".tsafe.yml"),
format!(
r#"
contracts:
deploy:
profile: work
namespace: infra
allowed_secrets: [DB_PASSWORD]
allowed_targets: [{allowed_target}]
trust_level: standard
"#
),
)
.unwrap();
tsafe()
.args(["--profile", "work", "init"])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["--profile", "work", "set", "infra/DB_PASSWORD", "db-secret"])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(
&[
"exec",
"--contract",
"deploy",
"--keys",
"DB_PASSWORD,API_KEY",
],
success_probe_child_cmd(),
))
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure()
.stderr(predicates::str::contains(
"selected key(s) not allowed by contract 'deploy': API_KEY",
));
}
#[test]
fn exec_contract_plan_shows_contract_source_and_run_line() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
std::fs::write(
dir.path().join(".tsafe.yml"),
r#"
contracts:
deploy:
profile: work
namespace: infra
allowed_secrets: [DB_PASSWORD]
allowed_targets: [npm]
trust_level: hardened
"#,
)
.unwrap();
tsafe()
.args(["--profile", "work", "init"])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["--profile", "work", "set", "infra/DB_PASSWORD", "db-secret"])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let assert = tsafe()
.args([
"exec",
"--contract",
"deploy",
"--plan",
"--",
"npm",
"test",
])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let out = String::from_utf8(assert.get_output().stdout.clone()).unwrap();
assert!(out.contains("Contract:"));
assert!(out.contains("deploy"));
assert!(out.contains("hardened (from contract)"));
assert!(out.contains("--contract deploy -- npm test"));
}
#[test]
fn exec_contract_mode_standard_overrides_hardened_contract() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
#[cfg(not(target_os = "windows"))]
let (allowed_target, child_args): (&str, &[&str]) = (
"printenv",
&[
"exec",
"--contract",
"deploy",
"--mode",
"standard",
"--allow-dangerous-env",
"--",
"printenv",
"NODE_OPTIONS",
],
);
#[cfg(target_os = "windows")]
let (allowed_target, child_args): (&str, &[&str]) = (
"cmd",
&[
"exec",
"--contract",
"deploy",
"--mode",
"standard",
"--allow-dangerous-env",
"--",
"cmd",
"/c",
"echo",
"%NODE_OPTIONS%",
],
);
std::fs::write(
dir.path().join(".tsafe.yml"),
format!(
r#"
contracts:
deploy:
profile: work
namespace: infra
allowed_secrets: [NODE_OPTIONS]
allowed_targets: [{allowed_target}]
trust_level: hardened
"#
),
)
.unwrap();
tsafe()
.args(["--profile", "work", "init"])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args([
"--profile",
"work",
"set",
"infra/NODE_OPTIONS",
"--",
"--inspect",
])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(child_args)
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("--inspect"))
.stderr(predicates::str::contains("NODE_OPTIONS"));
}
#[test]
fn exec_contract_writes_audit_exec_context() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
let allowed_target = success_probe_target();
std::fs::write(
dir.path().join(".tsafe.yml"),
format!(
r#"
contracts:
deploy:
profile: work
namespace: infra
allowed_secrets: [DB_PASSWORD]
required_secrets: [DB_PASSWORD]
allowed_targets: [{allowed_target}]
trust_level: hardened
"#
),
)
.unwrap();
tsafe()
.args(["--profile", "work", "init"])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["--profile", "work", "set", "infra/DB_PASSWORD", "db-secret"])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(
&["exec", "--contract", "deploy"],
success_probe_child_cmd(),
))
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let audit_path = audit_log_path(&vaults, "work");
let content = std::fs::read_to_string(audit_path).unwrap();
let last = content.lines().last().unwrap();
let entry: serde_json::Value = serde_json::from_str(last).unwrap();
assert_eq!(entry["operation"], "exec");
assert_eq!(entry["context"]["exec"]["contract_name"], "deploy");
assert_eq!(entry["context"]["exec"]["authority_profile"], "work");
assert_eq!(entry["context"]["exec"]["authority_namespace"], "infra");
assert_eq!(entry["context"]["exec"]["trust_level"], "hardened");
assert_eq!(entry["context"]["exec"]["access_profile"], "read_write");
assert_eq!(entry["context"]["exec"]["inherit"], "minimal");
assert_eq!(entry["context"]["exec"]["target"], allowed_target);
assert_eq!(entry["context"]["exec"]["target_decision"], "allowed_exact");
assert_eq!(entry["context"]["exec"]["matched_target"], allowed_target);
assert_eq!(
entry["context"]["exec"]["injected_secrets"],
serde_json::json!(["DB_PASSWORD"])
);
}
#[test]
fn exec_contract_read_only_access_profile_runs_with_allowed_secrets() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
#[cfg(not(target_os = "windows"))]
let (allowed_target, child_args): (&str, &[&str]) = (
"printenv",
&["exec", "--contract", "readonly-run", "--", "printenv"],
);
#[cfg(target_os = "windows")]
let (allowed_target, child_args): (&str, &[&str]) = (
"cmd",
&[
"exec",
"--contract",
"readonly-run",
"--",
"cmd",
"/c",
"set",
],
);
std::fs::write(
dir.path().join(".tsafe.yml"),
format!(
r#"
contracts:
readonly-run:
allowed_secrets: [READ_ONLY_KEY]
allowed_targets: [{allowed_target}]
access_profile: read_only
trust_level: standard
"#
),
)
.unwrap();
tsafe()
.args(["init"])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "READ_ONLY_KEY", "visible-secret"])
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(child_args)
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("READ_ONLY_KEY=visible-secret"));
let audit_path = audit_log_path(&vaults, "default");
let content = std::fs::read_to_string(audit_path).unwrap();
let last = content.lines().last().unwrap();
let entry: serde_json::Value = serde_json::from_str(last).unwrap();
assert_eq!(entry["operation"], "exec");
assert_eq!(entry["context"]["exec"]["contract_name"], "readonly-run");
assert_eq!(entry["context"]["exec"]["access_profile"], "read_only");
}
#[test]
fn exec_contract_denial_writes_failure_audit_context() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
std::fs::write(
dir.path().join(".tsafe.yml"),
r#"
contracts:
readonly-run:
profile: work
allowed_secrets: [READ_ONLY_KEY]
allowed_targets: [terraform]
access_profile: read_only
trust_level: hardened
"#,
)
.unwrap();
tsafe()
.args(exec_args(
&["exec", "--contract", "readonly-run"],
success_probe_child_cmd(),
))
.current_dir(dir.path())
.env("TSAFE_VAULT_DIR", &vaults)
.assert()
.failure()
.stderr(predicates::str::contains("allowed_targets"));
let audit_path = audit_log_path(&vaults, "work");
let content = std::fs::read_to_string(audit_path).unwrap();
let last = content.lines().last().unwrap();
let entry: serde_json::Value = serde_json::from_str(last).unwrap();
assert_eq!(entry["operation"], "exec");
assert_eq!(entry["status"], "failure");
assert_eq!(entry["context"]["exec"]["contract_name"], "readonly-run");
assert_eq!(entry["context"]["exec"]["access_profile"], "read_only");
assert_eq!(entry["context"]["exec"]["target_allowed"], false);
assert_eq!(entry["context"]["exec"]["target_decision"], "denied");
assert_eq!(
entry["context"]["exec"]["deny_reason"],
"TARGET_NOT_ALLOWED"
);
}
#[test]
fn exec_contract_plan_shows_access_profile() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
write_manifest(
dir.path(),
r#"
contracts:
readonly-run:
allowed_secrets: [ALLOWED_KEY]
required_secrets: [ALLOWED_KEY]
access_profile: read_only
trust_level: hardened
"#,
);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "ALLOWED_KEY", "allowed-value"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let assert = tsafe()
.args([
"exec",
"--contract",
"readonly-run",
"--plan",
"--",
"npm",
"test",
])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.current_dir(dir.path())
.assert()
.success();
let out = String::from_utf8(assert.get_output().stdout.clone()).unwrap();
assert!(out.contains("Access:"), "plan should show access profile");
assert!(out.contains("read_only"), "plan should show read_only");
}
fn write_manifest(dir: &std::path::Path, yaml: &str) {
std::fs::write(dir.join(".tsafe.yml"), yaml).unwrap();
}
#[test]
fn exec_contract_restricts_injection_to_allowed_secrets() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "ALLOWED_KEY", "allowed-value"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "DENIED_KEY", "denied-value"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
write_manifest(
dir.path(),
r#"
contracts:
limited:
allowed_secrets: [ALLOWED_KEY]
trust_level: standard
"#,
);
let child = exec_args(&["exec", "--contract", "limited"], env_dump_child_cmd());
tsafe()
.args(child)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.current_dir(dir.path())
.assert()
.success()
.stdout(predicates::str::contains("ALLOWED_KEY=allowed-value"))
.stdout(predicates::str::contains("DENIED_KEY").not())
.stdout(predicates::str::contains("denied-value").not());
}
#[test]
fn exec_contract_require_fails_when_required_secret_missing() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
write_manifest(
dir.path(),
r#"
contracts:
strict:
allowed_secrets: [REQUIRED_KEY]
required_secrets: [REQUIRED_KEY]
trust_level: standard
"#,
);
tsafe()
.args(["exec", "--contract", "strict", "--dry-run"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.current_dir(dir.path())
.assert()
.failure()
.stderr(predicates::str::contains("REQUIRED_KEY"));
}
#[test]
fn exec_contract_hardened_trust_applies_policy() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "SECRET_KEY", "s3cr3t"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
write_manifest(
dir.path(),
r#"
contracts:
hardened-run:
allowed_secrets: [SECRET_KEY]
trust_level: hardened
"#,
);
let out = tsafe()
.args([
"exec",
"--contract",
"hardened-run",
"--plan",
"--",
"npm",
"test",
])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.current_dir(dir.path())
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(out.status.success());
assert!(
stdout.contains("Contract:"),
"plan should show contract name"
);
assert!(
stdout.contains("hardened-run"),
"plan should show contract name value"
);
assert!(
stdout.contains("minimal"),
"hardened trust should use minimal inherit"
);
}
#[test]
fn exec_contract_target_allowlist_rejects_disallowed_command() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
write_manifest(
dir.path(),
r#"
contracts:
deploy:
allowed_secrets: []
allowed_targets: [terraform]
trust_level: hardened
"#,
);
tsafe()
.args(exec_args(
&["exec", "--contract", "deploy"],
env_dump_child_cmd(),
))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.current_dir(dir.path())
.assert()
.failure()
.stderr(predicates::str::contains("allowed_targets"));
}
#[test]
fn exec_contract_missing_manifest_reports_clear_error() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["exec", "--contract", "nonexistent", "--dry-run"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.current_dir(dir.path())
.assert()
.failure()
.stderr(predicates::str::contains("manifest").or(predicates::str::contains("tsafe.yml")));
}
#[test]
fn exec_env_flag_injects_vault_key_under_renamed_var() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "INTERNAL_API_KEY", "secret-value-xyz"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let child_cmd = exec_args(
&["exec", "--env", "MY_API_KEY=INTERNAL_API_KEY"],
env_value_child_cmd("MY_API_KEY"),
);
tsafe()
.args(child_cmd)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("secret-value-xyz"));
}
#[test]
fn exec_env_blocked_when_key_not_in_keys_narrowing() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "SAFE_KEY", "safe-value"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "PROD_SECRET", "prod-value"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(
&["exec", "--keys", "SAFE_KEY", "--env", "MY_DB=PROD_SECRET"],
success_probe_child_cmd(),
))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure()
.stderr(
predicates::str::contains("PROD_SECRET")
.and(predicates::str::contains("not in the --keys allowlist")),
);
}
#[test]
fn exec_env_allowed_when_key_in_keys_narrowing() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "PROD_SECRET", "prod-value"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "OTHER_KEY", "other-value"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let child_cmd = exec_args(
&[
"exec",
"--keys",
"PROD_SECRET",
"--env",
"MY_DB=PROD_SECRET",
],
env_value_child_cmd("MY_DB"),
);
tsafe()
.args(child_cmd)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("prod-value"));
}
#[test]
fn exec_env_flag_multiple_mappings() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "VAULT_DB_URL", "postgres://internal"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "VAULT_API_SECRET", "tok-secret-456"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let env_dump = exec_args(
&[
"exec",
"--env",
"DATABASE_URL=VAULT_DB_URL",
"--env",
"API_SECRET=VAULT_API_SECRET",
],
env_dump_child_cmd(),
);
tsafe()
.args(env_dump)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("DATABASE_URL"))
.stdout(predicates::str::contains("postgres://internal"))
.stdout(predicates::str::contains("API_SECRET"))
.stdout(predicates::str::contains("tok-secret-456"));
}
#[test]
fn exec_env_flag_missing_vault_key_fails_fast() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(
&["exec", "--env", "MY_KEY=NONEXISTENT_VAULT_KEY"],
success_probe_child_cmd(),
))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure()
.stderr(
predicates::str::contains("NONEXISTENT_VAULT_KEY")
.and(predicates::str::contains("not found")),
);
}
#[test]
fn exec_env_flag_malformed_fails_with_clear_error() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(
&["exec", "--env", "NO_EQUALS_SIGN"],
success_probe_child_cmd(),
))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure()
.stderr(predicates::str::contains("ENV_NAME=VAULT_KEY").or(predicates::str::contains("=")));
}
#[test]
fn exec_env_flag_blocked_when_key_outside_keys_narrowing() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "KEY_A", "value-a"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "KEY_B", "value-b"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(
&["exec", "--keys", "KEY_A", "--env", "RENAMED=KEY_B"],
success_probe_child_cmd(),
))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure()
.stderr(
predicates::str::contains("KEY_B")
.and(predicates::str::contains("not in the --keys allowlist")),
);
}
#[test]
fn exec_env_flag_blocked_by_contract_allowlist() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
write_manifest(
dir.path(),
r#"
contracts:
limited:
allowed_secrets: [ALLOWED_KEY]
trust_level: standard
"#,
);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "ALLOWED_KEY", "ok-value"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "DENIED_KEY", "secret-value"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(exec_args(
&[
"exec",
"--contract",
"limited",
"--env",
"RENAMED=DENIED_KEY",
],
success_probe_child_cmd(),
))
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.current_dir(dir.path())
.assert()
.failure()
.stderr(
predicates::str::contains("DENIED_KEY")
.and(predicates::str::contains("allowed_secrets")),
);
}
#[test]
fn exec_timeout_kills_long_running_child() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
#[cfg(not(target_os = "windows"))]
let sleep_cmd = vec!["sleep".to_string(), "30".to_string()];
#[cfg(target_os = "windows")]
let sleep_cmd = vec![
"cmd".to_string(),
"/c".to_string(),
"timeout".to_string(),
"/t".to_string(),
"30".to_string(),
"/nobreak".to_string(),
];
let child_args = exec_args(&["exec", "--timeout", "1"], sleep_cmd);
let assert = tsafe()
.args(child_args)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.failure();
let stderr = String::from_utf8(assert.get_output().stderr.clone()).unwrap();
assert!(
stderr.contains("timed out") || stderr.contains("timeout"),
"expected timeout message in stderr; got: {stderr:?}"
);
}
#[test]
fn exec_timeout_does_not_fire_when_child_exits_quickly() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "QUICK_KEY", "quick-val"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let child_cmd = exec_args(
&["exec", "--timeout", "30"],
env_value_child_cmd("QUICK_KEY"),
);
tsafe()
.args(child_cmd)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success()
.stdout(predicates::str::contains("quick-val"));
}
#[test]
fn exec_preset_minimal_strips_arbitrary_parent_env() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "VAULT_TOKEN_A", "from-vault"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let env_dump = exec_args(&["exec", "--preset", "minimal"], env_dump_child_cmd());
tsafe()
.args(env_dump)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.env("ARBITRARY_PARENT_TOKEN", "should-not-leak")
.assert()
.success()
.stdout(predicates::str::contains("PATH").or(predicates::str::contains("path")))
.stdout(predicates::str::contains("VAULT_TOKEN_A"))
.stdout(predicates::str::contains("ARBITRARY_PARENT_TOKEN").not());
}
#[test]
fn exec_preset_full_inherits_parent_env() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "VAULT_TOKEN_B", "from-vault-b"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let env_dump = exec_args(&["exec", "--preset", "full"], env_dump_child_cmd());
tsafe()
.args(env_dump)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.env("PARENT_PASSTHROUGH_VAR", "visible-to-child")
.assert()
.success()
.stdout(predicates::str::contains("VAULT_TOKEN_B"))
.stdout(predicates::str::contains("PARENT_PASSTHROUGH_VAR"));
}
#[test]
fn exec_no_inherit_overrides_preset_full() {
let dir = tempdir().unwrap();
let vaults = vault_dir(&dir);
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
tsafe()
.args(["set", "CLEAN_KEY2", "fromvault2"])
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
let env_dump = exec_args(
&["exec", "--no-inherit", "--preset", "full"],
env_dump_child_cmd(),
);
tsafe()
.args(env_dump)
.env("TSAFE_VAULT_DIR", &vaults)
.env("TSAFE_PASSWORD", "test-pw")
.env("SHOULD_NOT_APPEAR", "leaked")
.assert()
.success()
.stdout(predicates::str::contains("SHOULD_NOT_APPEAR").not())
.stdout(predicates::str::contains("CLEAN_KEY2"));
}