openssh/
escape.rs
1use std::{
8 borrow::Cow,
9 ffi::{OsStr, OsString},
10 os::unix::ffi::OsStrExt,
11 os::unix::ffi::OsStringExt,
12};
13
14fn allowed(byte: u8) -> bool {
15 matches!(byte, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'-' | b'_' | b'=' | b'/' | b',' | b'.' | b'+')
16}
17
18pub(crate) fn escape(s: &OsStr) -> Cow<'_, OsStr> {
27 let as_bytes = s.as_bytes();
28 let all_allowed = as_bytes.iter().copied().all(allowed);
29
30 if !as_bytes.is_empty() && all_allowed {
31 return Cow::Borrowed(s);
32 }
33
34 let mut escaped = Vec::with_capacity(as_bytes.len() + 2);
35 escaped.push(b'\'');
36
37 for &b in as_bytes {
38 match b {
39 b'\'' | b'!' => {
40 escaped.reserve(4);
41 escaped.push(b'\'');
42 escaped.push(b'\\');
43 escaped.push(b);
44 escaped.push(b'\'');
45 }
46 _ => escaped.push(b),
47 }
48 }
49 escaped.push(b'\'');
50 OsString::from_vec(escaped).into()
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 fn test_escape_case(input: &str, expected: &str) {
58 test_escape_from_bytes(input.as_bytes(), expected.as_bytes())
59 }
60
61 fn test_escape_from_bytes(input: &[u8], expected: &[u8]) {
62 let input_os_str = OsStr::from_bytes(input);
63 let observed_os_str = escape(input_os_str);
64 let expected_os_str = OsStr::from_bytes(expected);
65 assert_eq!(observed_os_str, expected_os_str);
66 }
67
68 #[test]
70 fn test_escape() {
71 test_escape_case(
72 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_=/,.+",
73 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_=/,.+",
74 );
75 test_escape_case("--aaa=bbb-ccc", "--aaa=bbb-ccc");
76 test_escape_case(
77 "linker=gcc -L/foo -Wl,bar",
78 r#"'linker=gcc -L/foo -Wl,bar'"#,
79 );
80 test_escape_case(r#"--features="default""#, r#"'--features="default"'"#);
81 test_escape_case(r#"'!\$`\\\n "#, r#"''\'''\!'\$`\\\n '"#);
82 test_escape_case("", r#"''"#);
83 test_escape_case(" ", r#"' '"#);
84 test_escape_case("*", r#"'*'"#);
85
86 test_escape_from_bytes(
87 &[0x66, 0x6f, 0x80, 0x6f],
88 &[b'\'', 0x66, 0x6f, 0x80, 0x6f, b'\''],
89 );
90 }
91}