This is an automated email from the ASF dual-hosted git repository. tomaswolf pushed a commit to branch rc-2.19.0 in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit a792e08922482894db080dd791c112623ed42601 Author: Thomas Wolf <[email protected]> AuthorDate: Thu Jun 18 18:26:51 2026 +0200 SCP: escape newlines in file names on sending Unix filenames may contain literal newlines. Escape them as ^J like OpenSSH does to avoid breaking the line-oriented SCP protocol. Note that OpenSSH actually produces \^J for a newline, but backslashes might be problematic for receivers on Windows. Most likely that extra backslash is a bug in OpenSSH? Also handle \^J in filenames on reception by removing the backslash on Windows lest it be taken as a file separator. --- CHANGES.md | 1 + .../java/org/apache/sshd/scp/common/ScpFileOpener.java | 16 +++++++++++----- .../main/java/org/apache/sshd/scp/common/ScpHelper.java | 12 +++++++++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 23bdfc4a3..6c19fa96d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -38,6 +38,7 @@ * Improve checking SSH user certificates in public-key authentication * Improve handling of repository paths in `sshd-git` on Windows. * Validate file names in SCP +* Escape newlines in filenames in the SCP protocol ## New Features diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpFileOpener.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpFileOpener.java index 82cb32465..4f245b7de 100644 --- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpFileOpener.java +++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpFileOpener.java @@ -71,14 +71,20 @@ public interface ScpFileOpener { || fileName.indexOf('/') >= 0) { throw new InvalidPathException(fileName, "Not a valid SCP fileName"); } - if (OsUtils.isWin32() && (fileName.indexOf('\\') >= 0 || fileName.indexOf(':') >= 0)) { - throw new InvalidPathException(fileName, "Not a valid SCP fileName"); + String name = fileName; + if (OsUtils.isWin32()) { + // OpenSSH replaces literal newlines in file names with "\^J" in the SCP protocol. The backslash is likely + // a bug in OpenSSH and causes trouble on Windows. + name = name.replace("\\^J", "^J"); + if (name.indexOf('\\') >= 0 || name.indexOf(':') >= 0) { + throw new InvalidPathException(fileName, "Not a valid SCP fileName"); + } } - Path p = fs.getPath(fileName); - if (p.isAbsolute() || !fileName.equals(p.getFileName().toString())) { + Path p = fs.getPath(name); + if (p.isAbsolute() || !name.equals(p.getFileName().toString())) { throw new InvalidPathException(fileName, "Not a valid SCP fileName"); } - return fileName; + return name; } /** diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java index bff83ff78..38f265ddc 100644 --- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java +++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java @@ -561,7 +561,10 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess String octalPerms = ((!preserve) || GenericUtils.isEmpty(perms)) ? ScpReceiveFileCommandDetails.DEFAULT_FILE_OCTAL_PERMISSIONS : ScpPathCommandDetailsSupport.getOctalPermissions(perms); - String fileName = resolver.getFileName(); + // Replace literal \n in the fileName (may occur on Linux) like OpenSSH does. A newline in the file name would + // break the line-oriented SCP protocol. Note that OpenSSH actually produces \^J, but I believe this is a bug? + // In any case adding a backslash might cause trouble for Windows remotes. + String fileName = resolver.getFileName().replace("\n", "^J"); String cmd = ScpReceiveFileCommandDetails.COMMAND_NAME + octalPerms + " " + fileSize + " " + fileName; if (debugEnabled) { log.debug("sendStream({})[{}] send 'C' command: {}", this, resolver, cmd); @@ -675,8 +678,11 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess String octalPerms = ((!preserve) || GenericUtils.isEmpty(perms)) ? ScpReceiveDirCommandDetails.DEFAULT_DIR_OCTAL_PERMISSIONS : ScpPathCommandDetailsSupport.getOctalPermissions(perms); - String cmd = ScpReceiveDirCommandDetails.COMMAND_NAME + octalPerms + " " + "0" + " " - + Objects.toString(path.getFileName(), null); + // Replace literal \n in the fileName (may occur on Linux) like OpenSSH does. A newline in the file name would + // break the line-oriented SCP protocol. Note that OpenSSH actually produces \^J, but I believe this is a bug? + // In any case adding a backslash might cause trouble for Windows remotes. + String fileName = Objects.toString(path.getFileName(), "").replace("\n", "^J"); + String cmd = ScpReceiveDirCommandDetails.COMMAND_NAME + octalPerms + " 0 " + fileName; if (debugEnabled) { log.debug("sendDir({})[{}] send 'D' command: {}", this, path, cmd); }
