From acc7f9fc83852b325699a6060556e8ab357eccab Mon Sep 17 00:00:00 2001 From: Axel Burri Date: Sun, 15 Aug 2021 12:27:54 +0200 Subject: [PATCH] btrbk: quote unsafe characters in shell commands As filenames can contain meta characters like '$', we can't just put ssh commands in double quotes. E.g. the following would trigger variable expansion on local shell: ssh example.com "ls -l 'evil$x'" Instead, we quote the ssh using single quotes (adding the need to escape single quotes), while also quoting unsafe filenames using single quotes. The above becomes: ssh example.com 'ls -l '\''evil$x'\''' Or more complex, for a file named "file with'single quotes'": ssh example.com 'ls -l '\''file with'\''\'\'''\''single quotes'\''\'\'''\'''\''' On the remote shell, this will expand to: ls -l 'file with'\''single quotes'\''' --- btrbk | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/btrbk b/btrbk index d4c3dfd..cc5a27f 100755 --- a/btrbk +++ b/btrbk @@ -773,18 +773,24 @@ sub _piped_cmd_txt($) return $cmd; } +sub quoteshell(@) { + # replace ' -> '\'' + join ' ', map { "'" . s/'/'\\''/gr . "'" } @_ +} + sub _safe_cmd($$) { - # NOTE: this function alters $aref: hashes of form: "{ unsafe => 'string' }" get translated to "string" + # NOTE: this function alters $aref: hashes of form: "{ unsafe => 'string' }" get translated to "'string'" my $aref = shift; my $offending = shift; foreach(@$aref) { if(ref($_) eq 'HASH') { $_ = $_->{unsafe}; # replace in-place - # NOTE: all files must be absolute + # NOTE: all files must be absolute (if not, check for leading dash '-' here!) unless(defined(check_file($_, { absolute => 1 }))) { push @$offending, "\"$_\""; } + $_ = quoteshell($_); } } return join(' ', @$aref); @@ -900,7 +906,8 @@ sub run_cmd(@) my $rsh_text = _safe_cmd($href->{rsh}, \@unsafe_cmd); return undef unless(defined($rsh_text)); - $href->{cmd_text} = $rsh_text . " '" . _piped_cmd_txt(\@rsh_cmd_pipe) . "'"; + + $href->{cmd_text} = $rsh_text . ' ' . quoteshell(_piped_cmd_txt(\@rsh_cmd_pipe)); } # local stream_buffer, rate_limit and show_progress in front of stream sink