-
Notifications
You must be signed in to change notification settings - Fork 1
🔒 fix potential argument injection in git commands #56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
58251d4
06a4fbc
fe2fcd9
fa3d441
6151551
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,94 @@ | ||||||||||
| // 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 with a component starting with a hyphen that could be a git flag. | ||||||||||
| // Note: Git itself has issues with paths starting with hyphen in the CWD | ||||||||||
| // (even with --), so we use a sub-path. | ||||||||||
| let malicious_path = "sub/-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 "sub/-c" is a weird path, but it shouldn't be a Command Injection. | ||||||||||
| // If it was interpreted as `git -C sub/-c`, it would fail with "unknown option" or similar | ||||||||||
| // if our fix wasn't working. | ||||||||||
|
|
||||||||||
| 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 with a component starting with hyphen. | ||||||||||
| // Note: Git itself has issues with paths starting with hyphen in the CWD | ||||||||||
| // (even with --), so we use a sub-path. | ||||||||||
| let path = "sub/-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. | ||||||||||
|
|
||||||||||
|
Comment on lines
+77
to
+79
|
||||||||||
| // 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. | |
| // Add a sparse submodule at a hyphen-prefixed path and verify the path is | |
| // treated literally rather than being interpreted as a command-line option. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change hardens the
git rmcleanup against hyphen-prefixed pathspecs, but the newly added integration tests don’t appear to exercise this cleanup path (it only runs when gix+git2 add_submodule fails and the code falls back to the CLI). Consider adding a targeted test that forces the fallback cleanup to run with a path starting with-/--...and asserts the overall operation succeeds (or at least that the cleanup doesn’t invoke unintendedgit rmoptions).