Skip to content

Commit 8b923db

Browse files
committed
Apply more suggestions from the code review.
Signed-off-by: Aalekh Patel <aalekh.gwpeck.7998@icloud.com>
1 parent ba707c1 commit 8b923db

File tree

3 files changed

+56
-41
lines changed

3 files changed

+56
-41
lines changed

src/command.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::escape;
1+
use crate::escape::escape;
22

33
use super::stdio::TryFromChildIo;
44
use super::RemoteChild;
@@ -7,7 +7,6 @@ use super::{Error, Session};
77

88
use std::borrow::Cow;
99
use std::ffi::OsStr;
10-
use std::os::unix::prelude::OsStrExt;
1110
use std::process;
1211

1312
#[derive(Debug)]
@@ -90,15 +89,13 @@ pub trait OverSsh {
9089

9190
impl OverSsh for std::process::Command {
9291
fn over_session<'session>(&self, session: &'session Session) -> Command<'session> {
93-
let program_escaped = escape(self.get_program().as_bytes());
94-
let mut command = session.command(Cow::Borrowed(program_escaped.as_str()));
92+
let program_escaped: Cow<'_, OsStr> = escape(self.get_program());
93+
let mut command = session.raw_command(program_escaped);
9594

96-
self
97-
.get_args()
98-
.for_each(|arg| {
99-
let arg_escaped = escape(arg.as_bytes());
100-
command.arg(Cow::Borrowed(arg_escaped.as_str()));
101-
});
95+
let args = self
96+
.get_args()
97+
.map(|arg| escape(arg));
98+
command.raw_args(args);
10299
command
103100
}
104101
}

src/escape.rs

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,17 @@
44
//! [`shell-escape`]: https://crates.io/crates/shell-escape
55
//! [`shell-escape::unix`]: https://docs.rs/shell-escape/latest/src/shell_escape/lib.rs.html#101
66
7+
use std::{
8+
borrow::Cow,
9+
ffi::{OsStr, OsString},
10+
os::unix::ffi::OsStrExt,
11+
os::unix::ffi::OsStringExt,
12+
};
713

8-
fn whitelisted(ch: char) -> bool {
9-
match ch {
10-
'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '=' | '/' | ',' | '.' | '+' => true,
14+
15+
fn whitelisted(byte: u8) -> bool {
16+
match byte {
17+
b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'-' | b'_' | b'=' | b'/' | b',' | b'.' | b'+' => true,
1118
_ => false,
1219
}
1320
}
@@ -20,62 +27,74 @@ fn whitelisted(ch: char) -> bool {
2027
///
2128
/// [`shell-escape::unix::escape`]: https://docs.rs/shell-escape/latest/src/shell_escape/lib.rs.html#101
2229
///
23-
pub fn escape(s: &[u8]) -> String {
24-
let all_whitelisted = s.iter().all(|x| whitelisted(*x as char));
30+
pub fn escape(s: &OsStr) -> Cow<'_, OsStr> {
31+
let s = s.as_bytes();
32+
let all_whitelisted = s.iter().all(|x| whitelisted(*x));
2533

2634
if !s.is_empty() && all_whitelisted {
27-
// All bytes are whitelisted and valid single-byte UTF-8,
28-
// so we can build the original string and return as is.
29-
return String::from_utf8(s.to_vec()).unwrap();
35+
return OsString::from_vec(s.to_vec()).into();
3036
}
3137

32-
let mut escaped = String::with_capacity(s.len() + 2);
33-
escaped.push('\'');
38+
let mut escaped = Vec::with_capacity(s.len() + 2);
39+
escaped.push(b'\'');
3440

3541
for &b in s {
3642
match b {
3743
b'\'' | b'!' => {
38-
escaped.push_str("'\\");
39-
escaped.push(b as char);
40-
escaped.push('\'');
44+
escaped.push(b'\'');
45+
escaped.push(b'\\');
46+
escaped.push(b);
47+
escaped.push(b'\'');
4148
}
42-
_ => escaped.push(b as char),
49+
_ => escaped.push(b),
4350
}
4451
}
45-
escaped.push('\'');
46-
escaped
52+
escaped.push(b'\'');
53+
OsString::from_vec(escaped).into()
4754
}
4855

4956

5057
#[cfg(test)]
5158
mod tests {
5259
use super::*;
5360

61+
fn test_escape_case(input: &str, expected: &str) {
62+
let input_os_str = OsStr::from_bytes(input.as_bytes());
63+
let observed_os_str = escape(input_os_str);
64+
let expected_os_str = OsStr::from_bytes(expected.as_bytes());
65+
assert_eq!(observed_os_str, expected_os_str);
66+
}
67+
5468
// These tests are courtesy of the `shell-escape` crate.
5569
#[test]
5670
fn test_escape() {
57-
assert_eq!(
58-
escape(b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_=/,.+"),
71+
test_escape_case(
72+
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_=/,.+",
5973
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_=/,.+"
6074
);
61-
assert_eq!(
62-
escape(b"--aaa=bbb-ccc"),
75+
test_escape_case(
76+
"--aaa=bbb-ccc",
6377
"--aaa=bbb-ccc"
6478
);
65-
assert_eq!(
66-
escape(b"linker=gcc -L/foo -Wl,bar"),
79+
test_escape_case(
80+
"linker=gcc -L/foo -Wl,bar",
6781
r#"'linker=gcc -L/foo -Wl,bar'"#
6882
);
69-
assert_eq!(
70-
escape(br#"--features="default""#),
83+
test_escape_case(
84+
r#"--features="default""#,
7185
r#"'--features="default"'"#
7286
);
73-
assert_eq!(
74-
escape(br#"'!\$`\\\n "#),
87+
test_escape_case(
88+
r#"'!\$`\\\n "#,
7589
r#"''\'''\!'\$`\\\n '"#
7690
);
77-
assert_eq!(escape(b""), r#"''"#);
78-
assert_eq!(escape(b" "), r#"' '"#);
79-
assert_eq!(escape(b"\xC4b"), r#"'Äb'"#);
91+
test_escape_case(
92+
"",
93+
r#"''"#
94+
);
95+
test_escape_case(
96+
" ",
97+
r#"' '"#
98+
);
8099
}
81100
}

src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,7 @@ pub use builder::{KnownHosts, SessionBuilder};
169169
mod command;
170170
pub use command::{Command, OverSsh};
171171

172-
mod escape;
173-
pub use escape::escape;
172+
pub(crate) mod escape;
174173

175174
mod child;
176175
pub use child::RemoteChild;

0 commit comments

Comments
 (0)