use super::*; use pretty_assertions::assert_eq; use std::ffi::OsStr; fn run(cwd: &Path, args: &[&str]) -> (i32, String, String) { let mut command = std::process::Command::new("git"); crate::safe_git::isolate_git_command_environment(&mut command); let output = command .args(args) .current_dir(cwd) .output() .expect("run Git"); ( output.status.code().unwrap_or(-1), String::from_utf8_lossy(&output.stdout).into_owned(), String::from_utf8_lossy(&output.stderr).into_owned(), ) } fn run_success(cwd: &Path, args: &[&str]) -> String { let (code, stdout, stderr) = run(cwd, args); assert_eq!(code, 0, "git {args:?}: {stderr}"); stdout.trim().to_string() } fn init_repo() -> tempfile::TempDir { let repo = tempfile::tempdir().expect("tempdir"); init_repo_at(repo.path()); repo } fn init_repo_at(root: &Path) { std::fs::create_dir_all(root).expect("create repository directory"); run_success(root, &["init"]); run_success(root, &["config", "user.email", "codex@example.com"]); run_success(root, &["config", "user.name", "Codex"]); std::fs::write(root.join("file.txt"), "orig\n").expect("write file"); run_success(root, &["add", "file.txt"]); run_success(root, &["commit", "-m", "seed"]); } #[cfg(windows)] fn create_junction(path: &Path, target: &Path) { // Bazel's GNU Windows runner can surface temporary paths with `/` // separators. `mklink` treats those separators as option prefixes, so // pass native separators to the cmd.exe built-in. let path = path.as_os_str().to_string_lossy().replace('/', "\\"); let target = target.as_os_str().to_string_lossy().replace('/', "\\"); let output = std::process::Command::new("cmd.exe") .args(["/D", "/C", "mklink", "/J"]) .arg(path) .arg(target) .output() .expect("create junction"); assert!( output.status.success(), "mklink failed: stdout={} stderr={}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); } fn guard(root: &Path) -> io::Result<()> { let git = GitRunner::for_cwd_io(root)?; ensure_no_worktree_config_sources(&git, root, &[]) } fn add_include(root: &Path, key: &str, value: &str) { run_success(root, &["config", "--add", key, value]); } fn assert_worktree_rejection(error: io::Error) { assert_eq!(error.kind(), io::ErrorKind::PermissionDenied, "{error}"); assert!(error.to_string().contains("worktree-controlled"), "{error}"); } fn run_isolated_source_test(test_name: &str, env: &[(&str, &OsStr)], removed: &[&str]) { let mut command = std::process::Command::new(std::env::current_exe().expect("test binary")); crate::safe_git::isolate_git_command_environment(&mut command); command .arg(test_name) .arg("--exact") .arg("--nocapture") .env("CODEX_GIT_CONFIG_SOURCE_CHILD", "1") .env("RUST_TEST_THREADS", "1"); for (name, value) in env { command.env(name, value); } for name in removed { command.env_remove(name); } let output = command.output().expect("run isolated source test"); assert!( output.status.success(), "isolated test {test_name} failed:\nstdout:\n{}\nstderr:\n{}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); } #[test] fn allows_protected_metadata_and_external_config_sources() { let repo = init_repo(); let root = repo.path(); guard(root).expect("ordinary metadata config"); std::fs::write( root.join(".git/protected.gitconfig"), "[user]\nname = protected\n", ) .expect("write protected config"); add_include(root, "include.path", "protected.gitconfig"); guard(root).expect("protected metadata include"); std::fs::create_dir(root.join(".git/subdir")).expect("create metadata subdirectory"); add_include(root, "include.path", "subdir/../protected.gitconfig"); guard(root).expect("metadata-local parent traversal"); let external = tempfile::tempdir().expect("external config directory"); let external_config = external.path().join("safe.gitconfig"); std::fs::write(&external_config, "[user]\nemail = safe@example.com\n") .expect("write external config"); add_include( root, "include.path", external_config.to_str().expect("UTF-8 external path"), ); guard(root).expect("external include"); add_include( root, "include.path", external .path() .join("missing.gitconfig") .to_str() .expect("UTF-8 missing external path"), ); guard(root).expect("missing external include"); } #[test] fn rejects_empty_primary_global_and_system_sources_in_the_worktree() { const TEST_NAME: &str = "git_config_sources::tests::rejects_empty_primary_global_and_system_sources_in_the_worktree"; if std::env::var_os("CODEX_GIT_CONFIG_SOURCE_CHILD").is_none() { let repo = init_repo(); let root = repo.path(); let unsafe_config = root.join("empty-primary.gitconfig"); std::fs::write(&unsafe_config, "").expect("write empty primary config"); let safe = tempfile::NamedTempFile::new().expect("safe external config"); run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", root.as_os_str()), ("GIT_CONFIG_GLOBAL", unsafe_config.as_os_str()), ("GIT_CONFIG_SYSTEM", safe.path().as_os_str()), ], &[], ); run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", root.as_os_str()), ("GIT_CONFIG_GLOBAL", safe.path().as_os_str()), ("GIT_CONFIG_SYSTEM", unsafe_config.as_os_str()), ], &[], ); return; } let root = PathBuf::from( std::env::var_os("CODEX_GIT_CONFIG_SOURCE_ROOT").expect("fixture repository root"), ); assert_worktree_rejection(guard(&root).expect_err("unsafe primary config source")); } #[test] fn rejects_missing_home_and_xdg_sources_in_the_worktree() { const TEST_NAME: &str = "git_config_sources::tests::rejects_missing_home_and_xdg_sources_in_the_worktree"; if std::env::var_os("CODEX_GIT_CONFIG_SOURCE_CHILD").is_none() { let repo = init_repo(); let root = repo.path(); let xdg = root.join("xdg"); std::fs::create_dir(&xdg).expect("create XDG root"); let safe_system = tempfile::NamedTempFile::new().expect("safe system config"); run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", root.as_os_str()), ("HOME", root.as_os_str()), ("XDG_CONFIG_HOME", xdg.as_os_str()), ("GIT_CONFIG_SYSTEM", safe_system.path().as_os_str()), ], &["GIT_CONFIG_GLOBAL"], ); return; } let root = PathBuf::from( std::env::var_os("CODEX_GIT_CONFIG_SOURCE_ROOT").expect("fixture repository root"), ); assert_worktree_rejection(guard(&root).expect_err("unsafe HOME/XDG source")); } #[test] fn allows_explicitly_disabled_global_and_system_sources() { const TEST_NAME: &str = "git_config_sources::tests::allows_explicitly_disabled_global_and_system_sources"; if std::env::var_os("CODEX_GIT_CONFIG_SOURCE_CHILD").is_none() { let repo = init_repo(); run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", repo.path().as_os_str()), ("GIT_CONFIG_GLOBAL", OsStr::new("")), ("GIT_CONFIG_SYSTEM", OsStr::new("")), ], &[], ); return; } let root = PathBuf::from( std::env::var_os("CODEX_GIT_CONFIG_SOURCE_ROOT").expect("fixture repository root"), ); guard(&root).expect("disabled primary config sources"); } #[cfg(unix)] #[test] fn allows_unset_home_when_git_has_no_global_source() { const TEST_NAME: &str = "git_config_sources::tests::allows_unset_home_when_git_has_no_global_source"; if std::env::var_os("CODEX_GIT_CONFIG_SOURCE_CHILD").is_none() { let repo = init_repo(); run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", repo.path().as_os_str()), ("GIT_CONFIG_NOSYSTEM", OsStr::new("1")), ], &[ "HOME", "XDG_CONFIG_HOME", "GIT_CONFIG_GLOBAL", "GIT_CONFIG_SYSTEM", ], ); return; } let root = PathBuf::from( std::env::var_os("CODEX_GIT_CONFIG_SOURCE_ROOT").expect("fixture repository root"), ); guard(&root).expect("Git without HOME or global config"); } #[test] fn supports_pre_242_git_without_system_var_enumeration() { assert_eq!( primary_sources::parse_git_var_config_paths_result( Some(129), b"", b"usage: git var (-l | )\n", "GIT_CONFIG_SYSTEM", ) .expect("pre-2.42 fallback"), None ); } #[cfg(unix)] #[test] fn rejects_newline_bearing_raw_global_source_before_git_normalizes_it() { const TEST_NAME: &str = "git_config_sources::tests::rejects_newline_bearing_raw_global_source_before_git_normalizes_it"; if std::env::var_os("CODEX_GIT_CONFIG_SOURCE_CHILD").is_none() { let parent = tempfile::tempdir().expect("fixture parent"); let root = parent.path().join("repo"); init_repo_at(&root); let newline_dir = parent.path().join("external\n"); std::fs::create_dir(&newline_dir).expect("create newline path component"); let unsafe_config = root.join("global.gitconfig"); std::fs::write(&unsafe_config, "").expect("write unsafe global config"); let raw = newline_dir.join("../repo/global.gitconfig"); run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", root.as_os_str()), ("GIT_CONFIG_GLOBAL", raw.as_os_str()), ("GIT_CONFIG_NOSYSTEM", OsStr::new("1")), ], &["GIT_CONFIG_SYSTEM"], ); return; } let root = PathBuf::from( std::env::var_os("CODEX_GIT_CONFIG_SOURCE_ROOT").expect("fixture repository root"), ); assert_worktree_rejection(guard(&root).expect_err("newline raw global source")); } #[cfg(unix)] #[test] fn ignores_non_utf8_values_for_unrelated_global_keys() { const TEST_NAME: &str = "git_config_sources::tests::ignores_non_utf8_values_for_unrelated_global_keys"; if std::env::var_os("CODEX_GIT_CONFIG_SOURCE_CHILD").is_none() { let repo = init_repo(); let global = tempfile::NamedTempFile::new().expect("external global config"); std::fs::write(global.path(), b"[user]\nname = \xff\n") .expect("write non-UTF-8 unrelated config value"); run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", repo.path().as_os_str()), ("GIT_CONFIG_GLOBAL", global.path().as_os_str()), ("GIT_CONFIG_NOSYSTEM", OsStr::new("1")), ], &["GIT_CONFIG_SYSTEM"], ); return; } let root = PathBuf::from( std::env::var_os("CODEX_GIT_CONFIG_SOURCE_ROOT").expect("fixture repository root"), ); guard(&root).expect("unrelated non-UTF-8 global value"); } #[cfg(all(unix, not(target_os = "macos")))] #[test] fn preserves_non_utf8_unix_repository_and_config_origin_paths() { use std::os::unix::ffi::OsStringExt; const TEST_NAME: &str = "git_config_sources::tests::preserves_non_utf8_unix_repository_and_config_origin_paths"; if std::env::var_os("CODEX_GIT_CONFIG_SOURCE_CHILD").is_none() { let parent = tempfile::tempdir().expect("non-UTF-8 fixture parent"); let root = parent .path() .join(std::ffi::OsString::from_vec(b"repo-\xff".to_vec())); init_repo_at(&root); let safe = tempfile::NamedTempFile::new().expect("safe included config"); add_include( &root, "include.path", safe.path().to_str().expect("UTF-8 safe include"), ); let global = parent .path() .join(std::ffi::OsString::from_vec(b"global-\xff".to_vec())); std::fs::write( &global, format!("[include]\npath = {}\n", safe.path().display()), ) .expect("write non-UTF-8 global path"); run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", root.as_os_str()), ("GIT_CONFIG_GLOBAL", global.as_os_str()), ("GIT_CONFIG_NOSYSTEM", OsStr::new("1")), ], &["GIT_CONFIG_SYSTEM"], ); return; } let root = PathBuf::from( std::env::var_os("CODEX_GIT_CONFIG_SOURCE_ROOT").expect("fixture repository root"), ); guard(&root).expect("non-UTF-8 repository and config origins"); } #[cfg(unix)] #[test] fn rejects_worktree_fifo_primary_source_without_opening_it() { const TEST_NAME: &str = "git_config_sources::tests::rejects_worktree_fifo_primary_source_without_opening_it"; if std::env::var_os("CODEX_GIT_CONFIG_SOURCE_CHILD").is_none() { let repo = init_repo(); let fifo = repo.path().join("global.fifo"); let status = std::process::Command::new("mkfifo") .arg(&fifo) .status() .expect("run mkfifo"); assert!(status.success(), "mkfifo failed: {status}"); run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", repo.path().as_os_str()), ("GIT_CONFIG_GLOBAL", fifo.as_os_str()), ("GIT_CONFIG_NOSYSTEM", OsStr::new("1")), ], &["GIT_CONFIG_SYSTEM"], ); return; } let root = PathBuf::from( std::env::var_os("CODEX_GIT_CONFIG_SOURCE_ROOT").expect("fixture repository root"), ); assert_worktree_rejection(guard(&root).expect_err("worktree FIFO config source")); } #[test] fn git_config_nosystem_accepts_cross_version_boolean_values() { const TEST_NAME: &str = "git_config_sources::tests::git_config_nosystem_accepts_cross_version_boolean_values"; if std::env::var_os("CODEX_GIT_CONFIG_SOURCE_CHILD").is_none() { let repo = init_repo(); let unsafe_system = repo.path().join("system.gitconfig"); std::fs::write(&unsafe_system, "").expect("write unsafe system config"); for (value, expected) in [ ("1", "ignored"), ("2", "ignored"), ("-1", "ignored"), ("01", "ignored"), ("+1", "ignored"), ("2147483647", "ignored"), ("-2147483647", "ignored"), ("true", "ignored"), ("yes", "ignored"), ("on", "ignored"), ("", "rejected"), ("0", "rejected"), ("-0", "rejected"), ("false", "rejected"), ("no", "rejected"), ("off", "rejected"), ("not-a-bool", "invalid"), ("2147483648", "invalid"), ("-2147483648", "invalid"), ("-2147483649", "invalid"), ] { run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", repo.path().as_os_str()), ("CODEX_GIT_CONFIG_SOURCE_EXPECTED", OsStr::new(expected)), ("GIT_CONFIG_GLOBAL", OsStr::new("")), ("GIT_CONFIG_SYSTEM", unsafe_system.as_os_str()), ("GIT_CONFIG_NOSYSTEM", OsStr::new(value)), ], &[], ); } return; } let root = PathBuf::from( std::env::var_os("CODEX_GIT_CONFIG_SOURCE_ROOT").expect("fixture repository root"), ); match std::env::var("CODEX_GIT_CONFIG_SOURCE_EXPECTED") .expect("expected child outcome") .as_str() { "ignored" => guard(&root).expect("system source disabled"), "rejected" => assert_worktree_rejection(guard(&root).expect_err("system source enabled")), "invalid" => assert_eq!( guard(&root).expect_err("invalid NOSYSTEM value").kind(), io::ErrorKind::InvalidData ), expected => panic!("unexpected child outcome {expected}"), } } #[cfg(windows)] #[test] fn rejects_both_windows_home_candidates_when_home_is_absent() { const TEST_NAME: &str = "git_config_sources::tests::rejects_both_windows_home_candidates_when_home_is_absent"; if std::env::var_os("CODEX_GIT_CONFIG_SOURCE_CHILD").is_none() { let repo = init_repo(); let external_profile = tempfile::tempdir().expect("external USERPROFILE"); for home in [repo.path().to_path_buf(), repo.path().join("future-home")] { let home = home.to_str().expect("UTF-8 Windows test path"); assert!(home.len() >= 3 && home.as_bytes()[1] == b':', "{home}"); run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", repo.path().as_os_str()), ("GIT_CONFIG_NOSYSTEM", OsStr::new("1")), ("HOMEDRIVE", OsStr::new(&home[..2])), ("HOMEPATH", OsStr::new(&home[2..])), ("USERPROFILE", external_profile.path().as_os_str()), ], &[ "HOME", "XDG_CONFIG_HOME", "GIT_CONFIG_GLOBAL", "GIT_CONFIG_SYSTEM", ], ); } let external_home = external_profile .path() .to_str() .expect("UTF-8 external Windows home"); assert!( external_home.len() >= 3 && external_home.as_bytes()[1] == b':', "{external_home}" ); for profile in [ repo.path().to_path_buf(), repo.path().join("future-profile"), ] { run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", repo.path().as_os_str()), ("GIT_CONFIG_NOSYSTEM", OsStr::new("1")), ("HOMEDRIVE", OsStr::new(&external_home[..2])), ("HOMEPATH", OsStr::new(&external_home[2..])), ("USERPROFILE", profile.as_os_str()), ], &[ "HOME", "XDG_CONFIG_HOME", "GIT_CONFIG_GLOBAL", "GIT_CONFIG_SYSTEM", ], ); } return; } let root = PathBuf::from( std::env::var_os("CODEX_GIT_CONFIG_SOURCE_ROOT").expect("fixture repository root"), ); assert_worktree_rejection(guard(&root).expect_err("Windows synthesized HOME source")); } #[cfg(windows)] #[test] fn allows_exact_windows_nul_for_both_primary_config_channels() { const TEST_NAME: &str = "git_config_sources::tests::allows_exact_windows_nul_for_both_primary_config_channels"; if std::env::var_os("CODEX_GIT_CONFIG_SOURCE_CHILD").is_none() { let repo = init_repo(); run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", repo.path().as_os_str()), ("GIT_CONFIG_GLOBAL", OsStr::new("NUL")), ("GIT_CONFIG_SYSTEM", OsStr::new("NUL")), ], &["GIT_CONFIG_NOSYSTEM"], ); return; } let root = PathBuf::from( std::env::var_os("CODEX_GIT_CONFIG_SOURCE_ROOT").expect("fixture repository root"), ); guard(&root).expect("exact NUL disables primary config channels"); } #[cfg(windows)] #[test] fn windows_appdata_global_config_matches_native_git_selection_and_precedence() { const TEST_NAME: &str = "git_config_sources::tests::windows_appdata_global_config_matches_native_git_selection_and_precedence"; if std::env::var_os("CODEX_GIT_CONFIG_SOURCE_CHILD").is_none() { for xdg in [None, Some(OsStr::new(""))] { for existing in [true, false] { let repo = init_repo(); let appdata = repo.path().join("appdata"); if existing { let config = appdata.join("Git/config"); std::fs::create_dir_all(config.parent().expect("APPDATA config parent")) .expect("create APPDATA config parent"); std::fs::write(&config, "[codex]\nappdata-marker = selected\n") .expect("write APPDATA config"); } let external_home = tempfile::tempdir().expect("external HOME"); let mut env = vec![ ("CODEX_GIT_CONFIG_SOURCE_ROOT", repo.path().as_os_str()), ( "CODEX_GIT_CONFIG_SOURCE_EXPECTED", OsStr::new(if existing { "reject-existing-appdata" } else { "reject-missing-appdata" }), ), ("GIT_CONFIG_NOSYSTEM", OsStr::new("1")), ("HOME", external_home.path().as_os_str()), ("APPDATA", appdata.as_os_str()), ]; if let Some(xdg) = xdg { env.push(("XDG_CONFIG_HOME", xdg)); } let mut removed = vec![ "GIT_CONFIG_GLOBAL", "GIT_CONFIG_SYSTEM", "HOMEDRIVE", "HOMEPATH", "USERPROFILE", ]; if xdg.is_none() { removed.push("XDG_CONFIG_HOME"); } run_isolated_source_test(TEST_NAME, &env, &removed); } } let repo = init_repo(); let appdata = repo.path().join("appdata"); let appdata_config = appdata.join("Git/config"); std::fs::create_dir_all(appdata_config.parent().expect("APPDATA config parent")) .expect("create APPDATA config parent"); std::fs::write(&appdata_config, "[codex]\nappdata-marker = appdata\n") .expect("write APPDATA config"); let external_home = tempfile::tempdir().expect("external HOME"); let external_xdg = tempfile::tempdir().expect("external XDG_CONFIG_HOME"); let xdg_config = external_xdg.path().join("git/config"); std::fs::create_dir_all(xdg_config.parent().expect("XDG config parent")) .expect("create XDG config parent"); std::fs::write(&xdg_config, "[codex]\nappdata-marker = xdg\n").expect("write XDG config"); run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", repo.path().as_os_str()), ("CODEX_GIT_CONFIG_SOURCE_EXPECTED", OsStr::new("allow-xdg")), ("GIT_CONFIG_NOSYSTEM", OsStr::new("1")), ("HOME", external_home.path().as_os_str()), ("APPDATA", appdata.as_os_str()), ("XDG_CONFIG_HOME", external_xdg.path().as_os_str()), ], &[ "GIT_CONFIG_GLOBAL", "GIT_CONFIG_SYSTEM", "HOMEDRIVE", "HOMEPATH", "USERPROFILE", ], ); let external_global = tempfile::NamedTempFile::new().expect("external global config"); std::fs::write( external_global.path(), "[codex]\nappdata-marker = explicit\n", ) .expect("write explicit global config"); run_isolated_source_test( TEST_NAME, &[ ("CODEX_GIT_CONFIG_SOURCE_ROOT", repo.path().as_os_str()), ( "CODEX_GIT_CONFIG_SOURCE_EXPECTED", OsStr::new("allow-explicit-global"), ), ("GIT_CONFIG_NOSYSTEM", OsStr::new("1")), ("HOME", repo.path().as_os_str()), ("APPDATA", appdata.as_os_str()), ("XDG_CONFIG_HOME", repo.path().as_os_str()), ("GIT_CONFIG_GLOBAL", external_global.path().as_os_str()), ], &["GIT_CONFIG_SYSTEM", "HOMEDRIVE", "HOMEPATH", "USERPROFILE"], ); return; } let root = PathBuf::from( std::env::var_os("CODEX_GIT_CONFIG_SOURCE_ROOT").expect("fixture repository root"), ); let expected = std::env::var("CODEX_GIT_CONFIG_SOURCE_EXPECTED").expect("expected child outcome"); if expected != "reject-missing-appdata" { let output = std::process::Command::new("git") .args(["config", "--global", "--get", "codex.appdata-marker"]) .current_dir(&root) .output() .expect("run native Git global config query"); assert!( output.status.success(), "native Git global query failed: {}", String::from_utf8_lossy(&output.stderr) ); let actual = String::from_utf8_lossy(&output.stdout); let native_expected = match expected.as_str() { "reject-existing-appdata" => "selected", "allow-xdg" => "xdg", "allow-explicit-global" => "explicit", expected => panic!("unexpected child outcome {expected}"), }; assert_eq!( actual.trim(), native_expected, "native Git global selection" ); } match expected.as_str() { "reject-existing-appdata" | "reject-missing-appdata" => { assert_worktree_rejection(guard(&root).expect_err("worktree APPDATA config")); } "allow-xdg" => guard(&root).expect("nonempty XDG suppresses APPDATA"), "allow-explicit-global" => { guard(&root).expect("explicit global config suppresses HOME, XDG, and APPDATA") } expected => panic!("unexpected child outcome {expected}"), } } #[test] fn rejects_empty_nonempty_absolute_missing_and_conditional_worktree_includes() { for (name, body, include_key, include_value) in [ ( "empty", "", "include.path".to_string(), "../driver-config".to_string(), ), ( "nonempty", "[user]\nname = worktree\n", "include.path".to_string(), "../driver-config".to_string(), ), ( "missing", "", "include.path".to_string(), "../future.gitconfig".to_string(), ), ( "inactive includeIf", "", "includeIf.gitdir:/definitely/not/this/repository/**.path".to_string(), "../driver-config".to_string(), ), ( "case-insensitive gitdir includeIf", "", "includeIf.gitdir/i:/definitely/not/this/repository/**.path".to_string(), "../driver-config".to_string(), ), ( "onbranch includeIf", "", "includeIf.onbranch:definitely-never/**.path".to_string(), "../driver-config".to_string(), ), ( "hasconfig includeIf", "", "includeIf.hasconfig:remote.*.url:https://never.example/**.path".to_string(), "../driver-config".to_string(), ), ] { let repo = init_repo(); let root = repo.path(); if name != "missing" { std::fs::write(root.join("driver-config"), body).expect("write config fixture"); } add_include(root, &include_key, &include_value); assert_worktree_rejection(guard(root).expect_err(name)); } let repo = init_repo(); let root = repo.path(); let config = root.join("absolute.gitconfig"); std::fs::write(&config, "").expect("write absolute config"); add_include( root, "include.path", config.to_str().expect("UTF-8 config path"), ); assert_worktree_rejection(guard(root).expect_err("absolute include")); let repo = init_repo(); let root = repo.path(); std::fs::write(root.join("driver-config"), "").expect("write conditional config"); let git_dir = run_success(root, &["rev-parse", "--absolute-git-dir"]).replace('\\', "/"); add_include( root, &format!("includeIf.gitdir:{git_dir}/.path"), "../driver-config", ); assert_worktree_rejection(guard(root).expect_err("active includeIf")); } #[test] fn command_scoped_include_paths_follow_the_same_boundary() { let repo = init_repo(); let root = repo.path(); let git = GitRunner::for_cwd_io(root).expect("Git runner"); let unsafe_config = root.join("command.gitconfig"); std::fs::write(&unsafe_config, "").expect("write worktree config"); let unsafe_args = vec![ "-c".to_string(), format!("include.path={}", unsafe_config.display()), ]; assert_worktree_rejection( ensure_no_worktree_config_sources(&git, root, &unsafe_args) .expect_err("command-scoped worktree include"), ); let external = tempfile::NamedTempFile::new().expect("safe external config"); let safe_args = vec![ "-c".to_string(), format!("include.path={}", external.path().display()), ]; ensure_no_worktree_config_sources(&git, root, &safe_args) .expect("command-scoped external include"); let relative_args = vec![ "-c".to_string(), "include.path=relative.gitconfig".to_string(), ]; assert!( ensure_no_worktree_config_sources(&git, root, &relative_args).is_err(), "relative command includes must fail closed" ); } #[test] fn rejects_every_duplicate_and_nested_include_target() { for unsafe_first in [true, false] { let repo = init_repo(); let root = repo.path(); std::fs::write(root.join("driver-config"), "").expect("write unsafe config"); let external = tempfile::NamedTempFile::new().expect("safe external config"); let safe = external.path().to_str().expect("UTF-8 safe path"); let values = if unsafe_first { ["../driver-config", safe] } else { [safe, "../driver-config"] }; for value in values { add_include(root, "include.path", value); } assert_worktree_rejection(guard(root).expect_err("duplicate include")); } let repo = init_repo(); let root = repo.path(); std::fs::write(root.join("driver-config"), "").expect("write nested target"); let external = tempfile::tempdir().expect("external config directory"); let outer = external.path().join("outer.gitconfig"); let nested = root.join("driver-config"); run_success( root, &[ "config", "--file", outer.to_str().expect("UTF-8 outer path"), "include.path", nested.to_str().expect("UTF-8 nested path"), ], ); add_include( root, "include.path", outer.to_str().expect("UTF-8 outer path"), ); assert_worktree_rejection(guard(root).expect_err("nested include")); } #[cfg(unix)] #[test] fn rejects_symlink_aliases_across_the_worktree_and_metadata_boundaries() { use std::os::unix::fs::symlink; let repo = init_repo(); let root = repo.path(); let external = tempfile::tempdir().expect("external directory"); std::fs::write(external.path().join("safe.gitconfig"), "").expect("write safe config"); symlink(external.path(), root.join("inside-link")).expect("inside to outside link"); add_include(root, "include.path", "../inside-link/safe.gitconfig"); assert_worktree_rejection(guard(root).expect_err("worktree symlink alias")); let repo = init_repo(); let root = repo.path(); std::fs::write(root.join("driver-config"), "").expect("write worktree target"); let external = tempfile::tempdir().expect("external directory"); symlink(root, external.path().join("outside-link")).expect("outside to inside link"); add_include( root, "include.path", external .path() .join("outside-link/driver-config") .to_str() .expect("UTF-8 alias path"), ); assert_worktree_rejection(guard(root).expect_err("external symlink alias")); let repo = init_repo(); let root = repo.path(); std::fs::write(root.join("driver-config"), "").expect("write escaped target"); symlink(root, root.join(".git/config-link")).expect("metadata escape link"); add_include(root, "include.path", "config-link/driver-config"); assert_worktree_rejection(guard(root).expect_err("metadata symlink escape")); let repo = init_repo(); let root = repo.path(); let external = tempfile::tempdir().expect("external directory"); symlink( root.join("future-config"), external.path().join("dangling-link"), ) .expect("dangling link into worktree"); add_include( root, "include.path", external .path() .join("dangling-link/config") .to_str() .expect("UTF-8 dangling alias"), ); assert_worktree_rejection(guard(root).expect_err("dangling symlink alias")); let repo = init_repo(); let root = repo.path(); let entry = tempfile::tempdir().expect("external entry directory"); let exit = tempfile::tempdir().expect("external exit directory"); std::fs::write(exit.path().join("safe.gitconfig"), "").expect("write exit config"); symlink(root, entry.path().join("through-worktree")).expect("link into worktree"); symlink(exit.path(), root.join("back-out")).expect("worktree-controlled exit link"); add_include( root, "include.path", entry .path() .join("through-worktree/back-out/safe.gitconfig") .to_str() .expect("UTF-8 nested alias"), ); assert_worktree_rejection(guard(root).expect_err("nested symlink traversal")); let repo = init_repo(); let root = repo.path(); let entry = tempfile::tempdir().expect("external entry directory"); let exit = tempfile::tempdir().expect("external exit directory"); std::fs::write(exit.path().join("safe.gitconfig"), "").expect("write chained config"); symlink(exit.path(), root.join("workspace-link")).expect("workspace link out"); symlink( root.join("workspace-link"), entry.path().join("direct-chain"), ) .expect("external link to workspace link"); add_include( root, "include.path", entry .path() .join("direct-chain/safe.gitconfig") .to_str() .expect("UTF-8 direct chain"), ); assert_worktree_rejection(guard(root).expect_err("direct symlink chain")); let repo = init_repo(); let root = repo.path(); std::fs::create_dir(root.join("subdir")).expect("create symlink target"); std::fs::write(root.join("evil.gitconfig"), "[user]\nname = worktree\n") .expect("write worktree config"); std::fs::write( root.join(".git/evil.gitconfig"), "[user]\nname = metadata\n", ) .expect("write metadata decoy"); symlink(root.join("subdir"), root.join(".git/link")).expect("metadata link to worktree"); add_include(root, "include.path", "link/../evil.gitconfig"); assert_worktree_rejection(guard(root).expect_err("symlink parent traversal")); let repo = init_repo(); let root = repo.path(); std::fs::create_dir(root.join("pivot")).expect("create worktree pivot"); std::fs::write(root.join(".git/protected.gitconfig"), "") .expect("write protected metadata config"); add_include( root, "include.path", root.join("pivot/../.git/protected.gitconfig") .to_str() .expect("UTF-8 pivot path"), ); assert_worktree_rejection(guard(root).expect_err("raw path pivots through worktree")); } #[cfg(unix)] #[test] fn rejects_external_symlink_target_with_hidden_worktree_pivot_to_protected_config() { use std::os::unix::fs::symlink; let repo = init_repo(); let root = std::fs::canonicalize(repo.path()).expect("canonical repository"); let external = tempfile::tempdir().expect("external alias directory"); let protected = root.join(".git/protected.gitconfig"); std::fs::write(&protected, "[codex]\n\tpivot = protected\n").expect("write protected config"); std::fs::create_dir(root.join("pivot")).expect("create ordinary worktree pivot"); let entry = external.path().join("entry"); symlink(root.join("pivot/../.git/protected.gitconfig"), &entry) .expect("external symlink target with raw worktree pivot"); add_include( &root, "include.path", entry.to_str().expect("UTF-8 entry path"), ); assert_eq!( run_success(&root, &["config", "--includes", "--get", "codex.pivot"]), "protected" ); assert_worktree_rejection( guard(&root).expect_err("hidden pivot inside external symlink target"), ); } #[cfg(target_os = "macos")] #[test] fn rejects_apfs_alias_symlink_target_with_hidden_worktree_pivot() { use std::os::unix::fs::symlink; let temp = std::fs::canonicalize(std::env::temp_dir()).expect("canonical temp directory"); let parent = tempfile::tempdir_in(temp).expect("Data-volume fixture"); let root = parent.path().join("repo"); init_repo_at(&root); let root = std::fs::canonicalize(root).expect("canonical repository"); let data_alias = PathBuf::from("/System/Volumes/Data") .join(root.strip_prefix("/").expect("absolute repository path")); if std::fs::metadata(&data_alias).is_err() { eprintln!("APFS Data alias unavailable; skipping native pivot assertion"); return; } let protected = root.join(".git/protected.gitconfig"); std::fs::write(&protected, "[codex]\n\tpivot = protected\n").expect("write protected config"); std::fs::create_dir(root.join("pivot")).expect("create ordinary worktree pivot"); let external = tempfile::tempdir().expect("external alias directory"); let entry = external.path().join("entry"); symlink(data_alias.join("pivot/../.git/protected.gitconfig"), &entry) .expect("APFS alias target with raw worktree pivot"); add_include( &root, "include.path", entry.to_str().expect("UTF-8 entry path"), ); assert_eq!( run_success(&root, &["config", "--includes", "--get", "codex.pivot"]), "protected" ); assert_worktree_rejection(guard(&root).expect_err("APFS hidden pivot inside symlink target")); } #[cfg(unix)] #[test] fn rejects_unregistered_same_common_worktree_used_only_as_alias_intermediate() { use std::os::unix::fs::symlink; let parent = tempfile::tempdir().expect("fixture"); let main = parent.path().join("main"); let linked = parent.path().join("linked"); let stale = parent.path().join("stale-unregistered"); let external = parent.path().join("external"); let safe = external.join("safe.gitconfig"); let entry = external.join("entry"); init_repo_at(&main); run_success( &main, &[ "worktree", "add", "-b", "same-common-active", linked.to_str().expect("UTF-8 linked path"), ], ); std::fs::create_dir_all(&stale).expect("create stale worktree"); std::fs::write( stale.join(".git"), format!("gitdir: {}\n", main.join(".git").display()), ) .expect("write stale same-common marker"); std::fs::create_dir_all(&external).expect("create external directory"); std::fs::write(&safe, "[codex]\n\tsameCommon = protected\n") .expect("write external safe config"); symlink(&safe, stale.join("switch")).expect("stale worktree controlled switch"); symlink(stale.join("switch"), &entry).expect("external entry through stale worktree"); add_include( &linked, "include.path", entry.to_str().expect("UTF-8 entry path"), ); assert_eq!( run_success( &linked, &["config", "--includes", "--get", "codex.sameCommon"] ), "protected" ); assert!( guard(&linked).is_err(), "unregistered same-common route intermediate must fail closed" ); } #[cfg(windows)] #[test] fn rejects_windows_junction_aliases_across_worktree_and_metadata_boundaries() { let repo = init_repo(); let root = repo.path(); std::fs::write(root.join("driver-config"), "").expect("write worktree config"); let external = tempfile::tempdir().expect("external directory"); create_junction(&external.path().join("into-worktree"), root); add_include( root, "include.path", external .path() .join("into-worktree/driver-config") .to_str() .expect("UTF-8 junction path"), ); assert_worktree_rejection(guard(root).expect_err("external junction into worktree")); let repo = init_repo(); let root = repo.path(); let external = tempfile::tempdir().expect("external directory"); std::fs::write(external.path().join("safe.gitconfig"), "").expect("write external config"); create_junction(&root.join("out-of-worktree"), external.path()); add_include(root, "include.path", "../out-of-worktree/safe.gitconfig"); assert_worktree_rejection(guard(root).expect_err("worktree junction to external")); let repo = init_repo(); let root = repo.path(); std::fs::write(root.join("driver-config"), "").expect("write worktree config"); create_junction(&root.join(".git/metadata-escape"), root); add_include(root, "include.path", "metadata-escape/driver-config"); assert_worktree_rejection(guard(root).expect_err("metadata junction escape")); let repo = init_repo(); let root = repo.path(); let entry = tempfile::tempdir().expect("external entry directory"); let exit = tempfile::tempdir().expect("external exit directory"); std::fs::write(exit.path().join("safe.gitconfig"), "").expect("write external config"); create_junction(&entry.path().join("through-worktree"), root); create_junction(&root.join("back-out"), exit.path()); add_include( root, "include.path", entry .path() .join("through-worktree/back-out/safe.gitconfig") .to_str() .expect("UTF-8 multi-hop junction path"), ); assert_worktree_rejection(guard(root).expect_err("multi-hop junction traversal")); let repo = init_repo(); let root = repo.path(); let external = tempfile::tempdir().expect("external directory"); create_junction(&external.path().join("into-worktree"), root); add_include( root, "include.path", external .path() .join("into-worktree/future.gitconfig") .to_str() .expect("UTF-8 missing junction target"), ); assert_worktree_rejection(guard(root).expect_err("junction missing suffix")); } #[test] fn linked_worktree_metadata_is_allowed_but_linked_worktree_config_is_rejected() { let parent = tempfile::tempdir().expect("worktree parent"); let main = parent.path().join("main"); let linked = parent.path().join("linked"); init_repo_at(&main); run_success( &main, &[ "worktree", "add", "-b", "config-guard-linked", linked.to_str().expect("UTF-8 linked path"), ], ); let git_dir = PathBuf::from(run_success(&linked, &["rev-parse", "--absolute-git-dir"])); let protected = git_dir.join("protected.gitconfig"); std::fs::write(&protected, "[user]\nname = linked\n").expect("write linked metadata config"); add_include( &linked, "include.path", protected.to_str().expect("UTF-8 protected path"), ); guard(&linked).expect("linked metadata include"); run_success(&linked, &["config", "--unset-all", "include.path"]); std::fs::write(main.join("driver-config"), "").expect("write main-worktree config"); add_include( &linked, "include.path", main.join("driver-config") .to_str() .expect("UTF-8 main-worktree config"), ); assert_worktree_rejection(guard(&linked).expect_err("main worktree include")); run_success(&linked, &["config", "--unset-all", "include.path"]); add_include( &linked, "include.path", main.join("future.gitconfig") .to_str() .expect("UTF-8 missing main-worktree config"), ); assert_worktree_rejection(guard(&linked).expect_err("missing main worktree include")); run_success(&linked, &["config", "--unset-all", "include.path"]); let unsafe_config = linked.join("driver-config"); std::fs::write(&unsafe_config, "").expect("write linked worktree config"); add_include( &linked, "include.path", unsafe_config.to_str().expect("UTF-8 unsafe path"), ); assert_worktree_rejection(guard(&linked).expect_err("linked worktree include")); } #[test] fn enclosing_repository_sibling_worktrees_are_always_untrusted() { let parent = tempfile::tempdir().expect("fixture parent"); let outer = parent.path().join("outer"); let sibling = parent.path().join("sibling"); init_repo_at(&outer); run_success( &outer, &[ "worktree", "add", "-b", "config-guard-sibling", sibling.to_str().expect("UTF-8 sibling worktree"), ], ); let nested = outer.join("nested"); init_repo_at(&nested); std::fs::write(sibling.join("driver-config"), "").expect("write sibling config"); add_include( &nested, "include.path", sibling .join("driver-config") .to_str() .expect("UTF-8 sibling config"), ); assert_worktree_rejection(guard(&nested).expect_err("enclosing repo sibling config")); run_success(&nested, &["config", "--unset-all", "include.path"]); std::fs::remove_dir_all(&sibling).expect("remove sibling but retain registry entry"); add_include( &nested, "include.path", sibling .join("future.gitconfig") .to_str() .expect("UTF-8 missing sibling config"), ); assert_worktree_rejection(guard(&nested).expect_err("missing enclosing repo sibling config")); let nested_primary = outer.join("inner-primary"); let inner_linked = parent.path().join("inner-linked"); init_repo_at(&nested_primary); run_success( &nested_primary, &[ "worktree", "add", "-b", "inner-linked-from-outer", inner_linked.to_str().expect("UTF-8 inner linked worktree"), ], ); for candidate in [ outer.join("outer.txt"), outer.join("future-outer.gitconfig"), ] { if candidate.file_name() == Some(OsStr::new("outer.txt")) { std::fs::write(&candidate, "").expect("write outer config source"); } add_include( &inner_linked, "include.path", candidate.to_str().expect("UTF-8 outer config source"), ); assert_worktree_rejection( guard(&inner_linked).expect_err("enclosing primary config source"), ); run_success(&inner_linked, &["config", "--unset-all", "include.path"]); } add_include( &inner_linked, "include.path", sibling .join("future-from-inner.gitconfig") .to_str() .expect("UTF-8 enclosing sibling source"), ); assert_worktree_rejection( guard(&inner_linked).expect_err("enclosing sibling from linked inner worktree"), ); run_success(&inner_linked, &["config", "--unset-all", "include.path"]); } #[cfg(unix)] #[test] fn preserves_symlinked_caller_ancestry_and_each_include_spelling() { use std::os::unix::fs::symlink; let parent = tempfile::tempdir().expect("fixture parent"); let outer = parent.path().join("outer"); let nested = parent.path().join("external-nested"); init_repo_at(&outer); init_repo_at(&nested); std::fs::write(outer.join("driver-config"), "").expect("write outer config"); symlink(&nested, outer.join("nested-link")).expect("link nested cwd through outer repo"); add_include( &nested, "include.path", outer .join("driver-config") .to_str() .expect("UTF-8 outer config"), ); let git = GitRunner::for_cwd_io(&outer.join("nested-link")) .expect("Git runner from symlinked nested cwd"); assert_worktree_rejection( ensure_no_worktree_config_sources(&git, &nested, &[]) .expect_err("logical outer ancestry config"), ); let repo = init_repo(); let root = repo.path(); std::fs::write(root.join("driver-config"), "").expect("write unsafe child config"); let external = tempfile::tempdir().expect("external include tree"); let shared = external.path().join("shared.gitconfig"); std::fs::write(&shared, "[include]\npath = child.gitconfig\n") .expect("write shared parent config"); let unsafe_dir = external.path().join("unsafe-spelling"); let safe_dir = external.path().join("safe-spelling"); std::fs::create_dir(&unsafe_dir).expect("create unsafe spelling directory"); std::fs::create_dir(&safe_dir).expect("create safe spelling directory"); symlink(&shared, unsafe_dir.join("parent.gitconfig")) .expect("unsafe spelling of shared parent"); symlink(&shared, safe_dir.join("parent.gitconfig")).expect("safe spelling of shared parent"); symlink( root.join("driver-config"), unsafe_dir.join("child.gitconfig"), ) .expect("unsafe relative child"); std::fs::write(safe_dir.join("child.gitconfig"), "").expect("safe relative child"); add_include( root, "include.path", unsafe_dir .join("parent.gitconfig") .to_str() .expect("UTF-8 unsafe spelling"), ); add_include( root, "include.path", safe_dir .join("parent.gitconfig") .to_str() .expect("UTF-8 safe spelling"), ); assert_worktree_rejection(guard(root).expect_err("unsafe relative alias child")); let parent = tempfile::tempdir().expect("linked-main dangling fixture"); let main = parent.path().join("main"); let linked = parent.path().join("linked"); init_repo_at(&main); run_success( &main, &[ "worktree", "add", "-b", "config-guard-dangling-main", linked.to_str().expect("UTF-8 linked worktree"), ], ); let external = tempfile::tempdir().expect("external dangling alias directory"); symlink( main.join("future.gitconfig"), external.path().join("dangling-main-config"), ) .expect("dangling alias into main worktree"); add_include( &linked, "include.path", external .path() .join("dangling-main-config") .to_str() .expect("UTF-8 dangling main alias"), ); assert_worktree_rejection(guard(&linked).expect_err("dangling alias into main worktree")); } #[cfg(target_os = "macos")] #[test] fn rejects_apfs_data_firmlink_aliases_for_existing_and_missing_worktree_configs() { let data_root = Path::new("/System/Volumes/Data"); if !data_root.is_dir() { return; } let home = PathBuf::from(std::env::var_os("HOME").expect("HOME")); let temp_parent = home.join(".cache/codex-git-utils-tests"); std::fs::create_dir_all(&temp_parent).expect("create test temp parent"); for exists in [true, false] { let repo = tempfile::Builder::new() .prefix("firmlink-config-") .tempdir_in(&temp_parent) .expect("home tempdir"); let root = repo.path(); init_repo_at(root); if exists { std::fs::write(root.join("driver-config"), "").expect("write aliased config"); } let alias = data_root .join(root.strip_prefix("/").expect("absolute repository path")) .join("driver-config"); add_include( root, "include.path", alias.to_str().expect("UTF-8 firmlink alias"), ); assert_worktree_rejection(guard(root).expect_err("APFS firmlink alias")); } let family = tempfile::Builder::new() .prefix("firmlink-linked-family-") .tempdir_in(&temp_parent) .expect("home linked-family tempdir"); let main = family.path().join("main"); let linked = family.path().join("linked"); init_repo_at(&main); run_success( &main, &[ "worktree", "add", "-b", "firmlink-linked-main", linked.to_str().expect("UTF-8 linked path"), ], ); for exists in [true, false] { let name = if exists { "driver-config" } else { "future.gitconfig" }; if exists { std::fs::write(main.join(name), "").expect("write main config"); } let alias = data_root .join(main.strip_prefix("/").expect("absolute main path")) .join(name); add_include( &linked, "include.path", alias.to_str().expect("UTF-8 main firmlink alias"), ); assert_worktree_rejection(guard(&linked).expect_err("main firmlink alias")); run_success(&linked, &["config", "--unset-all", "include.path"]); } std::fs::write(main.join(".git/protected.gitconfig"), "") .expect("write protected main metadata config"); let protected_alias = data_root .join(main.strip_prefix("/").expect("absolute main path")) .join(".git/protected.gitconfig"); add_include( &linked, "include.path", protected_alias .to_str() .expect("UTF-8 protected metadata alias"), ); guard(&linked).expect("protected metadata firmlink alias"); } #[test] fn include_path_expansion_supports_git_prefix() { let repo = init_repo(); let root = repo.path(); let git = GitRunner::for_cwd_io(root).expect("Git runner"); let prefix = expand_git_config_path(&git, root, "%(prefix)/etc/gitconfig").expect("expand Git prefix"); assert!(prefix.is_absolute()); } #[cfg(not(windows))] #[test] fn include_path_expansion_preserves_colon_parentheses_as_literal_text() { let repo = init_repo(); let root = repo.path(); let git = GitRunner::for_cwd_io(root).expect("Git runner"); let optional = GitConfigEntry { scope: crate::git_config::GitConfigScope::Local, origin: crate::git_config::GitConfigOrigin::File(".git/config".into()), key: "include.path".to_string(), value: ":(optional)../future.gitconfig".to_string(), }; let optional_path = resolve_include_path(&git, root, &optional).expect("resolve optional path"); assert_eq!( AbsolutePathBuf::resolve_path_against_base(optional_path, root).as_path(), root.join(".git/:(optional)../future.gitconfig") ); let unknown = GitConfigEntry { value: ":(unknown)../future.gitconfig".to_string(), ..optional }; let unknown_path = resolve_include_path(&git, root, &unknown).expect("resolve literal unknown path"); assert_eq!( AbsolutePathBuf::resolve_path_against_base(unknown_path, root).as_path(), root.join(".git/:(unknown)../future.gitconfig") ); for spelling in [":(optional)", ":(unknown)"] { let external = tempfile::tempdir().expect("external literal config directory"); let literal_dir = external.path().join(spelling); std::fs::create_dir(&literal_dir).expect("create literal include directory"); std::fs::write(literal_dir.join("child"), "[probe]\nvalue = literal\n") .expect("write literal include child"); let parent = external.path().join("parent.gitconfig"); std::fs::write(&parent, format!("[include]\npath = {spelling}/child\n")) .expect("write literal parent config"); let value = run_success( root, &[ "config", "--file", parent.to_str().expect("UTF-8 parent path"), "--includes", "--get", "probe.value", ], ); assert_eq!(value, "literal"); add_include( root, "include.path", parent.to_str().expect("UTF-8 parent path"), ); guard(root).expect("literal include path is external"); run_success(root, &["config", "--unset-all", "include.path"]); } } #[test] fn windows_path_validator_rejects_namespaces_streams_and_alias_components() { for path in [ r"\??\C:\repo\config", r"\\?\GLOBALROOT\Device\config", r"\\.\pipe\config", r"C:\repo\config:stream", r"C:relative\config", r":(optional)..\future.gitconfig", r":(unknown)..\future.gitconfig", r"C:\repo\NUL.gitconfig", r"C:\repo\COM¹.gitconfig", r"C:\repo\LPT³.gitconfig", r"C:\repo\trailing.\config", ] { assert!(windows_config_path_is_ambiguous(path), "{path:?}"); } for path in [ r"C:\external\config", r"\\server\share\config", r"\\?\C:\repo\.git\config", r"\\.\C:\repo\.git\config", r"\\?\UNC\server\share\config", r"\\.\UNC\server\share\config", r"..\driver-config", r".\driver-config", ] { assert!(!windows_config_path_is_ambiguous(path), "{path:?}"); } }