@@ -502,17 +502,20 @@ async def openssh_execute(self, commands, stdin: Optional[str] = None, timeout:
502502 return process .returncode , stdout .decode (), stderr .decode ()
503503
504504 def _escape_for_rcp (self , path : str ) -> str :
505- """Escape special characters for scp RCP mode using backslashes .
505+ """Backslash-escape shell metacharacters for scp's RCP protocol .
506506
507- Note: escape_for_bash() does NOT work here because scp's RCP protocol
508- expects backslash-escaped paths, not quote-wrapped strings.
507+ Glob characters (``*``, ``?``, ``[``, ``]``) are deliberately left
508+ unescaped so the remote shell expands them; the trade-off is that
509+ filenames containing literal glob characters cannot be addressed
510+ in RCP mode. :func:`escape_for_bash` is unsuitable here because it
511+ quote-wraps rather than backslash-escapes.
509512
510- :param path: The path to escape
511- :return: The escaped path with backslashes before special characters
513+ :param path: The path to escape.
514+ :return: The escaped path.
512515 """
513516
514517 result = []
515- special = set (' \t \n "\' `$\\ !#&*? ;<>|(){}[] ' )
518+ special = set (' \t \n "\' `$\\ !#&;<>|(){}' )
516519 for char in path :
517520 if char in special :
518521 result .append ('\\ ' )
@@ -522,11 +525,13 @@ def _escape_for_rcp(self, path: str) -> str:
522525 def _escape_for_scp (self , path : str ) -> str :
523526 """Prepare a path for use in scp commands.
524527
525- In OpenSSH < 9.0, scp uses the RCP protocol which passes paths through
526- the remote shell. We must escape shell metacharacters with backslashes
527- to prevent shell expansion/injection.
528-
529- OpenSSH 9.0+, however, uses SFTP mode by default - paths are binary data, no shell quoting needed.
528+ OpenSSH >= 9.0 uses SFTP mode: paths are sent as binary, no
529+ escaping needed. OpenSSH < 9.0 uses RCP mode: paths are interpreted
530+ by a shell on both ends (scp itself shells out internally for local
531+ paths), so shell metacharacters are backslash-escaped via
532+ :meth:`_escape_for_rcp` while glob characters are deliberately
533+ preserved for pattern matching. Mode is selected via
534+ :attr:`is_openssh_9_or_higher`.
530535 """
531536
532537 if self .is_openssh_9_or_higher :
0 commit comments