use super::*;
use std::sync::{Mutex, MutexGuard};
const EXACT_KEY: &str = "ALEF_TEST_APPS_ENV_EXACTNESS";
static EXACT_KEY_LOCK: Mutex<()> = Mutex::new(());
struct InheritedEnvGuard {
_lock: MutexGuard<'static, ()>,
previous: Option<String>,
}
impl InheritedEnvGuard {
fn set(value: Option<&str>) -> Self {
let lock = EXACT_KEY_LOCK.lock().unwrap_or_else(|error| error.into_inner());
let previous = std::env::var(EXACT_KEY).ok();
unsafe {
match value {
Some(value) => std::env::set_var(EXACT_KEY, value),
None => std::env::remove_var(EXACT_KEY),
}
}
Self { _lock: lock, previous }
}
}
impl Drop for InheritedEnvGuard {
fn drop(&mut self) {
unsafe {
match &self.previous {
Some(value) => std::env::set_var(EXACT_KEY, value),
None => std::env::remove_var(EXACT_KEY),
}
}
}
}
fn base_config() -> ResolvedCrateConfig {
let cfg: crate::core::config::NewAlefConfig = toml::from_str(
r#"
[workspace]
languages = ["python"]
[[crates]]
name = "my-lib"
sources = ["src/lib.rs"]
[crates.e2e]
fixtures = "fixtures"
output = "e2e"
[crates.e2e.call]
function = "process"
module = "my-lib"
result_var = "result"
"#,
)
.expect("fixture config should parse");
cfg.resolve().expect("fixture config should resolve").remove(0)
}
fn observed_env_value(configured: &str, inherited: Option<&str>) -> String {
let _guard = InheritedEnvGuard::set(inherited);
let temp = tempfile::tempdir().expect("tempdir");
let sink = temp.path().join("observed");
let mut config = base_config();
let e2e = config.e2e.as_mut().expect("fixture config has an e2e section");
e2e.env.insert(EXACT_KEY.to_owned(), configured.to_owned());
e2e.registry.run.insert(
"python".to_owned(),
crate::core::config::output::TestAppRunConfig {
precondition: Some("true".to_owned()),
before: None,
run: Some(crate::core::config::output::StringOrVec::Single(format!(
"printf '%s' \"${EXACT_KEY}\" > '{}'",
sink.display()
))),
argv_run: None,
},
);
let names = ["python".to_owned()];
test_apps_run(&config, Path::new("env_exactness_nonexistent_alef.toml"), &names)
.expect("the probe run command should succeed");
std::fs::read_to_string(&sink).expect("the probe should have written the observed value")
}
#[test]
fn inherited_value_must_not_be_appended() {
let configured = "http://127.0.0.1:53211/";
let inherited = "http://attacker.example/inherited";
let observed = observed_env_value(configured, Some(inherited));
assert_eq!(
observed, configured,
"the configured value must arrive byte-exact; an inherited value must not be appended"
);
assert!(
!observed.contains(inherited),
"the inherited value leaked into the child's environment: {observed:?}"
);
assert_ne!(
observed,
format!("{configured}:{inherited}"),
"this is the exact corruption the PATH-prepend guard produced"
);
}
#[test]
fn configured_value_arrives_exactly_when_nothing_is_inherited() {
let configured = "http://127.0.0.1:53211/";
assert_eq!(observed_env_value(configured, None), configured);
}
#[test]
fn a_deliberately_wrong_expected_value_does_not_match() {
let configured = "http://127.0.0.1:53211/";
let observed = observed_env_value(configured, Some("http://attacker.example/inherited"));
assert_ne!(
observed, "http://127.0.0.1:53212/",
"a different port must not compare equal"
);
assert_ne!(observed, "", "an empty value must not compare equal");
assert_ne!(
observed, "http://127.0.0.1:53211",
"a value missing the trailing slash must not compare equal"
);
}
#[test]
fn hostile_characters_in_an_env_value_arrive_verbatim() {
let configured = "v'1 $(id) `id` \"dq\" a;b|c&&d\nsecond-line";
let observed = observed_env_value(configured, None);
assert_eq!(
observed, configured,
"the value must arrive byte-exact, newline included"
);
assert_eq!(
observed.lines().count(),
2,
"the value must not be truncated at its newline: {observed:?}"
);
}