diff --git a/src/git_ops/mod.rs b/src/git_ops/mod.rs index 4694106..41ddb51 100644 --- a/src/git_ops/mod.rs +++ b/src/git_ops/mod.rs @@ -389,7 +389,7 @@ impl GitOperations for GitOpsManager { // And removed from index let _ = std::process::Command::new("git") - .args(["rm", "--cached", "-r", "--ignore-unmatch"]) + .args(["rm", "--cached", "-r", "--ignore-unmatch", "--"]) .arg(&opts.path) .current_dir(workdir) .output(); @@ -525,7 +525,8 @@ impl GitOperations for GitOpsManager { .or_else(|_| { // CLI fallback: use git read-tree to apply sparse checkout let output = std::process::Command::new("git") - .args(["-C", path, "read-tree", "-mu", "HEAD"]) + .current_dir(path) + .args(["read-tree", "-mu", "HEAD"]) .output() .context("Failed to run git read-tree")?; if output.status.success() { diff --git a/tests/security_tests.rs b/tests/security_tests.rs new file mode 100644 index 0000000..04aefee --- /dev/null +++ b/tests/security_tests.rs @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: 2025 Adam Poulemanos <89049923+bashandbone@users.noreply.github.com> +// +// SPDX-License-Identifier: LicenseRef-PlainMIT OR MIT + +//! Security tests to ensure robustness against various attack vectors. + +mod common; +use common::TestHarness; +use std::fs; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_path_with_hyphen_injection() { + let harness = TestHarness::new().expect("Failed to create test harness"); + harness.init_git_repo().expect("Failed to init git repo"); + + let remote_repo = harness + .create_test_remote("hyphen-remote") + .expect("Failed to create remote"); + let remote_url = format!("file://{}", remote_repo.display()); + + // A path starting with a hyphen that could be a git flag + let malicious_path = "-c"; + + // This should not fail with "unknown option" or similar error from git -C + // It might still fail for other reasons if the path is invalid for a submodule, + // but it shouldn't be interpreted as a flag to the 'git' command itself. + + // Note: Using add_submodule via harness. + // We need to make sure the directory doesn't exist or is handled. + + let result = harness.run_submod(&[ + "add", + &remote_url, + "--name", + "hyphen-sub", + "--path", + malicious_path, + ]); + + // The operation might fail because "-c" is a weird path, but it shouldn't be a Command Injection. + // If it was interpreted as `git -C -c`, it would fail with "unknown option: -c" or similar. + + match result { + Ok(output) => { + let stderr = String::from_utf8_lossy(&output.stderr); + assert!(!stderr.contains("unknown option: -c"), "Potential command injection detected: git interpreted path as a flag"); + }, + Err(e) => { + let err_msg = e.to_string(); + assert!(!err_msg.contains("unknown option: -c"), "Potential command injection detected: git interpreted path as a flag"); + } + } + } + + #[test] + fn test_sparse_checkout_with_hyphen_path() { + let harness = TestHarness::new().expect("Failed to create test harness"); + harness.init_git_repo().expect("Failed to init git repo"); + + let remote_repo = harness + .create_complex_remote("sparse-hyphen") + .expect("Failed to create remote"); + let remote_url = format!("file://{}", remote_repo.display()); + + // Path starting with hyphen + let path = "./-sparse"; + + // Ensure the directory exists to trigger the CLI fallback in apply_sparse_checkout if needed, + // although apply_sparse_checkout is usually called after gix/git2 which might fail or be bypassed. + + harness.run_submod_success(&[ + "add", + &remote_url, + "--name", + "sparse-hyphen", + "--path", + path, + "--sparse-paths", + "src", + ]).expect("Failed to add submodule with hyphenated path"); + + // Verify it worked + assert!(harness.dir_exists("-sparse/src")); + } +}