Browse Source

Fix Paramiko command quoting (for Unicode->bytes and inclusion of quotes)

Andreas Kloeckner 9 years ago
parent
commit
6e855f684a
1 changed files with 45 additions and 1 deletions
  1. 45 1
      dulwich/contrib/paramiko_vendor.py

+ 45 - 1
dulwich/contrib/paramiko_vendor.py

@@ -108,6 +108,40 @@ class _ParamikoWrapper(object):
         self.stop_monitoring()
 
 
+# {{{ shell quoting
+
+# Adapted from
+# https://github.com/python/cpython/blob/8cd133c63f156451eb3388b9308734f699f4f1af/Lib/shlex.py#L278
+
+def is_shell_safe(s):
+    import re
+    import sys
+
+    flags = 0
+    if sys.version_info >= (3,):
+        flags = re.ASCII
+
+    unsafe_re = re.compile(br'[^\w@%+=:,./-]', flags)
+
+    return unsafe_re.search(s) is None
+
+
+def shell_quote(s):
+    """Return a shell-escaped version of the byte string *s*."""
+
+    # Unconditionally quotes because that's apparently git's behavior, too,
+    # and some code hosting sites (notably Bitbucket) appear to rely on that.
+
+    if not s:
+        return b"''"
+
+    # use single quotes, and put single quotes into double quotes
+    # the string $'b is then quoted as '$'"'"'b'
+    return b"'" + s.replace(b"'", b"'\"'\"'") + b"'"
+
+# }}}
+
+
 class ParamikoSSHVendor(object):
 
     def __init__(self):
@@ -132,8 +166,18 @@ class ParamikoSSHVendor(object):
         # Open SSH session
         channel = client.get_transport().open_session()
 
+        # Quote command
+        assert command
+        assert is_shell_safe(command[0])
+
+        quoted_command = (
+            command[0]
+            + b' '
+            + b' '.join(
+                shell_quote(c) for c in command[1:]))
+
         # Run commands
-        channel.exec_command(subprocess.list2cmdline(command))
+        channel.exec_command(quoted_command)
 
         return _ParamikoWrapper(
             client, channel, progress_stderr=progress_stderr)