|
@@ -298,6 +298,15 @@ async fn fetch_host_key_data(config: &Config, node: &Node) -> Result<Vec<u8>> {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn shq(s: &str) -> String {
|
|
pub(crate) fn shq(s: &str) -> String {
|
|
|
|
|
+ // A value that is already a complete single-quoted shell word (e.g. a recipe
|
|
|
|
|
+ // arg the operator pre-quoted, like `'{"json":1}'`) is self-delimiting and
|
|
|
|
|
+ // needs no escaping: single quotes already suppress every metachar/interp,
|
|
|
|
|
+ // so wrapping it again would corrupt the token (the old `if starts_with('''`
|
|
|
|
|
+ // special case). Everything else is wrapped in single quotes with any
|
|
|
|
|
+ // interior quote escaped.
|
|
|
|
|
+ if is_single_quoted_token(s) {
|
|
|
|
|
+ return s.to_string();
|
|
|
|
|
+ }
|
|
|
let mut out = String::with_capacity(s.len() + 2);
|
|
let mut out = String::with_capacity(s.len() + 2);
|
|
|
out.push('\'');
|
|
out.push('\'');
|
|
|
for c in s.chars() {
|
|
for c in s.chars() {
|
|
@@ -311,6 +320,15 @@ pub(crate) fn shq(s: &str) -> String {
|
|
|
out
|
|
out
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/// True when `s` is a well-formed single-quoted shell word: it starts and ends
|
|
|
|
|
+/// with a single quote and its interior contains no (unescaped) quote. A valid
|
|
|
|
|
+/// single-quoted token cannot inject — the shell treats its whole body as one
|
|
|
|
|
+/// literal argument.
|
|
|
|
|
+fn is_single_quoted_token(s: &str) -> bool {
|
|
|
|
|
+ let b = s.as_bytes();
|
|
|
|
|
+ b.len() >= 2 && b[0] == b'\'' && b[b.len() - 1] == b'\'' && !s[1..s.len() - 1].contains('\'')
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/// Upload an in-memory script body to a node and make it executable.
|
|
/// Upload an in-memory script body to a node and make it executable.
|
|
|
///
|
|
///
|
|
|
/// Cheaper than [`upload_dir`]: no tar, just base64-encode the body and
|
|
/// Cheaper than [`upload_dir`]: no tar, just base64-encode the body and
|
|
@@ -445,3 +463,7 @@ impl client::Handler for VerifyingHandler {
|
|
|
Ok(true)
|
|
Ok(true)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+#[cfg(test)]
|
|
|
|
|
+#[path = "ssh_tests.rs"]
|
|
|
|
|
+mod tests;
|