5 releases
Uses old Rust 2015
0.13.7 | Dec 22, 2023 |
---|---|
0.13.6 | Nov 29, 2022 |
0.13.5 | Feb 4, 2021 |
0.13.4 | Apr 13, 2020 |
0.1.0 | May 30, 2017 |
#366 in Command-line interface
43,766 downloads per month
Used in 12 crates
(11 directly)
115KB
1.5K
SLoC
duct_sh
A convenience wrapper around the duct
crate for building commands out of
shell strings. Duct normally executes commands directly, without going
through a shell at all. This is preferable where possile, because it avoids
tricky whitespace issues and shell injection vulnerabilities. However,
sometimes you need access to more esoteric shell features (e.g. shell
builtins like source
and exec
, or process substitution with <()
), and
other times you're just stuck with a string of shell code that you have to
run somehow.
In these situations, duct_sh
is a "cross-platform" way to run shell
commands, in combination with all the usual features of Duct.
"Cross-platform" is in scare quotes, however, because shell code is
necessarily platform-specific. The typical Bourne-like shells on Unix and
the cmd.exe shell on Windows systems do have a lot in common, including the
|
and >
operators, but they diverge very quickly when you start to look
at the details. For that reason, seriously cross-platform programs should
avoid shell code as much as possible.
Example
// Execute a static string of shell code.
let output = duct_sh::sh("echo $MSG | sed s/i/a/").env("MSG", "hi").read()?;
assert_eq!(output, "ha");
// Execute a dynamic string of shell code. Note that this requires the
// "sh_dangerous" function. See the Security section below.
let arg = "hi";
let command = format!("echo {} | sed s/i/a/", arg);
let output = duct_sh::sh_dangerous(&command).read()?;
assert_eq!(output, "ha");
Security
The sh
function is restricted to static strings. The sh_dangerous
function does exactly the same thing, but it accepts dynamic strings. The
scary name is because of security issues related to shell injection.
Consider this innocent-looking function:
fn echo_string(s: &str) {
duct_sh::sh_dangerous(format!("echo {}", s)).run().unwrap();
}
This function appears to work when s
is a well-behaved string like
"foo"
or "foo bar"
. You might notice something wrong if you try
"foo bar"
, because those three spaces will collapse into one in the
output. But the real problem is a string like "; rm -rf /"
. The resulting
command will print a newline and then try to delete your entire hard drive.
Any function resembling echo_string
, exposed to any kind of untrusted
input, becomes a serious security issue.
It's possible to work around these issues by escaping special characters,
but escaping is tricky, and it's difficult to test that you've covered
every character. Special characters are also platform-specific, making it
even harder to get decent test coverage. The difficulty of doing all this
correctly will generally outweigh any convenience provided by the duct_sh
crate itself.
For these reasons, in addition to the portability concerned discussed
above, most programs intended for production should prefer duct
over
duct_sh
. Since duct
avoids invoking the shell at all, it isn't usually
vulnerable to shell injection.
Dependencies
~0–6.5MB
~38K SLoC