Fix: ripgrep decompression — separate file names from options with "--"
How BurntSushi/ripgrep#3222 fixed path traversal in compressed file search — why decompression commands need argument separators to prevent option injection.
TL;DR
- Issue: ripgrep’s decompression commands passed file paths without
--separator, allowing option injection via filenames like--help - Fix: Added
cmd.arg("--")before the path argument in two locations indecompress.rs - Pattern: Always use
--before user-controlled paths in subprocess calls - Security: Moderate risk — defense-in-depth fix, one line, free
The Bug
Repo: BurntSushi/ripgrep Issue: #3222 Status: submission-failed PR: https://github.com/BurntSushi/ripgrep/pull/3222
When ripgrep searches compressed files (.gz, .bz2, .xz, .lz4, .zstd), it invokes external decompression tools (gzip, bzip2, xz, etc.) via std::process::Command. The file path was passed as a positional argument directly after the decompression tool’s options, without the standard -- argument separator.
This meant a file named --help or --version would be interpreted as a command-line flag by the decompression tool rather than a file path — a classic option injection vulnerability.
Root Cause
In crates/cli/src/decompress.rs, the decompression command was built as:
let mut cmd = Command::new(&decomp_cmd.bin);
cmd.args(&decomp_cmd.args);
// path added later without separator
cmd.arg(path);
If path is --help, the decompression tool (e.g., gzip) would interpret it as a flag and print its help text instead of trying to decompress a file. Worse, a path like --verbose could alter the tool’s behavior.
The Fix
@@ -180,6 +180,7 @@ impl DecompressionMatcher {
if let Some(i) = self.globs.matches(path).into_iter().next_back() {
let decomp_cmd = &self.commands[i];
let mut cmd = Command::new(&decomp_cmd.bin);
+ cmd.arg("--");
cmd.args(&decomp_cmd.args);
return Some(cmd);
}
@@ -301,6 +301,7 @@ impl<W: WriteColor> SearchWorker<W> {
let bin = self.config.preprocessor.as_ref().unwrap();
let mut cmd = std::process::Command::new(bin);
+ cmd.arg("--");
cmd.arg(path).stdin(Stdio::from(File::open(path)?));
The -- argument is the POSIX-standard end-of-options delimiter. After --, all subsequent arguments are treated as positional (file names), not flags. This is the same convention used by rm, cp, mv, and virtually all POSIX CLI tools.
Why This Is a Security Issue
Option injection in file-processing tools can lead to:
- Information disclosure —
--helpleaks tool version and usage info - Denial of service —
--verboseor debug flags could slow processing - Arbitrary behavior — some tools accept dangerous flags (e.g.,
--outputcould redirect output)
In ripgrep’s case, the risk is moderate because the decompression tools are invoked with controlled arguments and the output is piped, not written to disk. But the fix is free (one line) and follows defense-in-depth principles.
The POSIX -- Convention
The -- end-of-options marker is specified in POSIX.1-2017 §12.2:
The first
--argument that is not an option-argument should be accepted as a delimiter indicating the end of options. Any following arguments should be treated as operands, even if they begin with the-character.
When to use -- in subprocess calls:
| Scenario | Use --? |
Example |
|---|---|---|
| Passing user-controlled paths | Always | cmd.arg("--").arg(user_path) |
| Passing fixed/known paths | Optional | cmd.arg("/tmp/known_file") |
Tool supports -- |
Recommended | gzip, bzip2, xz, tar |
Tool doesn’t support -- |
Use ./ prefix |
cmd.arg("./".concat(path)) |
Rust’s Command API Note
Rust’s std::process::Command doesn’t automatically add -- — it’s the caller’s responsibility. This differs from some higher-level libraries (e.g., Python’s subprocess with close_fds=True provides some isolation but doesn’t solve option injection).
For Rust code that invokes subprocesses with user-controlled arguments, always insert -- before the user-supplied argument.
Transfer Pattern
The argument separator audit pattern: search your codebase for Command::new invocations that pass user-controlled strings as arguments. If any of those strings could start with -, add -- before them.
Auto-generated from PR #3222. View all patches on GitHub.