use std::process::Command;
fn repo_local_git_vars() -> Vec<String> {
let output = Command::new("git")
.arg("rev-parse")
.arg("--local-env-vars")
.output()
.expect("run `git rev-parse --local-env-vars`");
assert!(
output.status.success(),
"`git rev-parse --local-env-vars` failed: {}",
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8_lossy(&output.stdout)
.lines()
.map(str::trim)
.filter(|line| !line.is_empty())
.map(str::to_string)
.collect()
}
#[test]
fn suite_does_not_inherit_repo_local_git_env() {
const ALSO_DANGEROUS: &[&str] = &["GIT_NAMESPACE", "GIT_DISCOVERY_ACROSS_FILESYSTEM"];
let mut vars = repo_local_git_vars();
vars.extend(ALSO_DANGEROUS.iter().map(|v| v.to_string()));
let leaked: Vec<String> = vars
.into_iter()
.filter_map(|name| {
std::env::var(&name)
.ok()
.filter(|value| !value.is_empty())
.map(|value| format!(" {name}={value}"))
})
.collect();
assert!(
leaked.is_empty(),
"This test process inherited git's repository-local environment:\n{}\n\n\
Every fixture that shells out to git will act on THAT repository \
instead of its tempdir, regardless of `.current_dir()` — this is the \
999.37 sandbox escape, which corrupts the real checkout.\n\n\
If you reached here from a git hook, clear them first:\n \
unset $(git rev-parse --local-env-vars)\n\
See scripts/hooks/pre-push for the worked example.",
leaked.join("\n")
);
}