fix(security): sanitize gpg.ssh.program to prevent global config leaks

Explicitly inject `gpg.ssh.program=ssh-keygen` during blind injection.
Leaving this unset would allow global configuration (potentially malicious)
to bleed into the ephemeral session. Setting it to empty string causes
Git to crash. Using the system default `ssh-keygen` ensures both
stability and isolation.
This commit is contained in:
INX "Xenon" 2026-01-28 17:52:46 +08:00
parent e512bd9e2a
commit 1bc109ae64
Signed by: inx
SSH key fingerprint: SHA256:oEFbclBdeqw4M09C3hfnDej0ioZdzZW6BKxsZH6quX8
2 changed files with 39 additions and 14 deletions

View file

@ -106,7 +106,7 @@ fn clean_existing_profiles(profile_dir: &Path) -> Result<()> {
fn run_exec(config: &GoshConfig, profile_id: &str, args: &[String]) -> Result<()> {
let profile_path = get_profile_path(config, profile_id)?;
let injections = sanitizer::get_blind_injections();
let injections = sanitizer::BLIND_INJECTIONS;
let mut cmd = Command::new("git");
@ -188,6 +188,8 @@ fn run_switch(config: &GoshConfig, profile_id: &str, force: bool) -> Result<()>
run_command(&mut cmd)?;
println!("Switched to profile '{}'", profile_id);
warn_if_dirty_config(profile_id)?;
Ok(())
}
@ -272,3 +274,28 @@ fn extract_basename(url: &str) -> PathBuf {
let path = Path::new(s);
path.file_name().map(|n| PathBuf::from(n)).unwrap_or_else(|| PathBuf::from("repo"))
}
fn warn_if_dirty_config(profile_id: &str) -> Result<()> {
let config_path = Path::new(".git/config");
if config_path.exists() {
let content = std::fs::read_to_string(config_path)?;
// Check for sections that typically contain identity or dangerous settings
// We look for [user], [author], [gpg] sections, or specific keys like sshCommand/gpgsign
let is_dirty = content.contains("[user]")
|| content.contains("[author]")
|| content.contains("[gpg]")
|| content.contains("sshCommand")
|| content.contains("gpgsign");
if is_dirty {
println!("\n⚠️ WARNING: Dirty Local Config Detected!");
println!(" Your .git/config contains hardcoded '[user]' or '[core]' settings.");
println!(" These settings (like signing keys) are merging with your profile");
println!(" and causing a \"Frankenstein Identity\".");
println!("");
println!(" 👉 Fix it: Run 'gosh {} -f' to force clean.\n", profile_id);
}
}
Ok(())
}

View file

@ -7,8 +7,7 @@ pub const BLACKLIST_KEYS: &[&str] = &[
"http.cookieFile",
];
pub fn get_blind_injections() -> Vec<(&'static str, &'static str)> {
vec![
pub const BLIND_INJECTIONS: &[(&str, &str)] = &[
("user.name", ""),
("user.email", ""),
("user.signingkey", ""),
@ -18,5 +17,4 @@ pub fn get_blind_injections() -> Vec<(&'static str, &'static str)> {
("gpg.program", "gpg"),
("commit.gpgsign", "false"),
("tag.gpgsign", "false"),
]
}
];