Skip to content

Commit c3ec5e9

Browse files
authored
Merge pull request #1305 from cgwalters/dont-quote-me-on-this
Dont quote me on this
2 parents 60e42c0 + 7732fe6 commit c3ec5e9

File tree

2 files changed

+39
-6
lines changed

2 files changed

+39
-6
lines changed

utils/src/command.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,6 @@ mod tests {
262262
"to-existing-root",
263263
]);
264264

265-
assert_eq!(cmd.to_string_pretty(), "podman run --privileged '--pid=host' '--user=root:root' -v /var/lib/containers:/var/lib/containers -v 'this has spaces' 'label=type:unconfined_t' '--env=RUST_LOG=trace' quay.io/ckyrouac/bootc-dev bootc install to-existing-root");
265+
similar_asserts::assert_eq!(cmd.to_string_pretty(), "podman run --privileged --pid=host --user=root:root -v /var/lib/containers:/var/lib/containers -v 'this has spaces' label=type:unconfined_t --env=RUST_LOG=trace quay.io/ckyrouac/bootc-dev bootc install to-existing-root");
266266
}
267267
}

utils/src/path.rs

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,21 @@ pub struct PathQuotedDisplay<'a> {
88
path: &'a Path,
99
}
1010

11+
/// A pretty conservative check for "shell safe" characters. These
12+
/// are basically ones which are very common in filenames or command line
13+
/// arguments, which are the primary use case for this. There are definitely
14+
/// characters such as '+' which are typically safe, but it's fine if
15+
/// we're overly conservative.
16+
///
17+
/// For bash for example: https://www.gnu.org/software/bash/manual/html_node/Definitions.html#index-metacharacter
18+
fn is_shellsafe(c: char) -> bool {
19+
matches!(c, '/' | '.' | '-' | '_' | ',' | '=' | ':') || c.is_alphanumeric()
20+
}
21+
1122
impl<'a> Display for PathQuotedDisplay<'a> {
1223
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1324
if let Some(s) = self.path.to_str() {
14-
if s.chars()
15-
.all(|c| matches!(c, '/' | '.') || c.is_alphanumeric())
16-
{
25+
if s.chars().all(is_shellsafe) {
1726
return f.write_str(s);
1827
}
1928
}
@@ -45,11 +54,30 @@ mod tests {
4554

4655
#[test]
4756
fn test_unquoted() {
48-
for v in ["", "foo", "/foo/bar", "/foo/bar/../baz", "/foo9/bar10"] {
57+
for v in [
58+
"",
59+
"foo",
60+
"/foo/bar",
61+
"/foo/bar/../baz",
62+
"/foo9/bar10",
63+
"--foo",
64+
"--virtiofs=/foo,/bar",
65+
"/foo:/bar",
66+
"--label=type=unconfined_t",
67+
] {
4968
assert_eq!(v, format!("{}", PathQuotedDisplay::new(&v)));
5069
}
5170
}
5271

72+
#[test]
73+
fn test_bash_metachars() {
74+
// https://www.gnu.org/software/bash/manual/html_node/Definitions.html#index-metacharacter
75+
let bash_metachars = "|&;()<>";
76+
for c in bash_metachars.chars() {
77+
assert!(!is_shellsafe(c));
78+
}
79+
}
80+
5381
#[test]
5482
fn test_quoted() {
5583
let cases = [
@@ -59,7 +87,12 @@ mod tests {
5987
(r#"/path/"withquotes'"#, r#""/path/\"withquotes'""#),
6088
];
6189
for (v, quoted) in cases {
62-
assert_eq!(quoted, format!("{}", PathQuotedDisplay::new(&v)));
90+
let q = PathQuotedDisplay::new(&v).to_string();
91+
assert_eq!(quoted, q.as_str());
92+
// Also sanity check there's exactly one token
93+
let token = shlex::split(&q).unwrap();
94+
assert_eq!(1, token.len());
95+
assert_eq!(v, token[0]);
6396
}
6497
}
6598

0 commit comments

Comments
 (0)