Files
Nadav0077 600be0de54 Reject unsafe server-supplied names in SCP recursive download
A malicious or compromised SCP server could return file or directory names containing
path separators, drive qualifiers, or parent-directory references.
ScpClient.Download(string, DirectoryInfo) combined these into a local path without
validation, allowing writes outside the destination directory. Server-supplied C and D
record names are now validated before being combined into a local path.

Signed-off-by: Nadav0077 <18245584+Nadav0077@users.noreply.github.com>
2026-08-09 18:32:00 +02:00

2229 lines
84 KiB
C#

using Renci.SshNet.Common;
#pragma warning disable CS0618 // These SCP tests use the obsolete default-transformation constructors.
namespace Renci.SshNet.IntegrationTests
{
// TODO SCP: UPLOAD / DOWNLOAD ZERO LENGTH FILES
// TODO SCP: UPLOAD / DOWNLOAD EMPTY DIRECTORY
// TODO SCP: UPLOAD DIRECTORY THAT ALREADY EXISTS ON REMOTE HOST
[TestClass]
public class ScpTests : TestBase
{
private IConnectionInfoFactory _connectionInfoFactory;
private IRemotePathTransformation _remotePathTransformation;
[TestInitialize]
public void SetUp()
{
_connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort);
_remotePathTransformation = RemotePathTransformation.ShellQuote;
}
[TestMethod]
[DynamicData(nameof(GetScpDownloadStreamDirectoryDoesNotExistData))]
public void Scp_Download_Stream_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation,
string remotePath,
string remoteFile)
{
var completeRemotePath = CombinePaths(remotePath, remoteFile);
// remove complete directory if it's not the home directory of the user
// or else remove the remote file
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
try
{
using (var downloaded = new MemoryStream())
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
try
{
client.Download(completeRemotePath, downloaded);
Assert.Fail();
}
catch (ScpException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual($"scp: {completeRemotePath}: No such file or directory", ex.Message);
}
}
}
finally
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpDownloadStreamFileDoesNotExistData))]
public void Scp_Download_Stream_FileDoesNotExist(IRemotePathTransformation remotePathTransformation,
string remotePath,
string remoteFile)
{
var completeRemotePath = CombinePaths(remotePath, remoteFile);
// remove complete directory if it's not the home directory of the user
// or else remove the remote file
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
client.CreateDirectory(remotePath);
}
}
try
{
using (var downloaded = new MemoryStream())
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
try
{
client.Download(completeRemotePath, downloaded);
Assert.Fail();
}
catch (ScpException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual($"scp: {completeRemotePath}: No such file or directory", ex.Message);
}
}
}
finally
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpDownloadDirectoryInfoDirectoryDoesNotExistData))]
public void Scp_Download_DirectoryInfo_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation,
string remotePath)
{
var localDirectory = Path.GetTempFileName();
File.Delete(localDirectory);
Directory.CreateDirectory(localDirectory);
try
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
try
{
client.Download(remotePath, new DirectoryInfo(localDirectory));
Assert.Fail();
}
catch (ScpException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual($"scp: {remotePath}: No such file or directory", ex.Message);
}
}
}
finally
{
Directory.Delete(localDirectory, true);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpDownloadDirectoryInfoExistingFileData))]
public void Scp_Download_DirectoryInfo_ExistingFile(IRemotePathTransformation remotePathTransformation,
string remotePath)
{
var content = CreateMemoryStream(100);
content.Position = 0;
var localDirectory = Path.GetTempFileName();
File.Delete(localDirectory);
Directory.CreateDirectory(localDirectory);
var localFile = Path.Combine(localDirectory, PosixPath.GetFileName(remotePath));
try
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
client.UploadFile(content, remotePath);
}
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
client.Download(remotePath, new DirectoryInfo(localDirectory));
}
Assert.IsTrue(File.Exists(localFile));
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
using (var downloaded = new MemoryStream())
{
client.DownloadFile(remotePath, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateFileHash(localFile), CreateHash(downloaded));
}
}
}
finally
{
Directory.Delete(localDirectory, true);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(remotePath))
{
client.DeleteFile(remotePath);
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpDownloadDirectoryInfoExistingDirectoryData))]
public void Scp_Download_DirectoryInfo_ExistingDirectory(IRemotePathTransformation remotePathTransformation,
string remotePath)
{
var localDirectory = Path.GetTempFileName();
File.Delete(localDirectory);
Directory.CreateDirectory(localDirectory);
var localPathFile1 = Path.Combine(localDirectory, "file1 23");
var remotePathFile1 = CombinePaths(remotePath, "file1 23");
var contentFile1 = CreateMemoryStream(1024);
contentFile1.Position = 0;
var localPathFile2 = Path.Combine(localDirectory, "file2 #$%");
var remotePathFile2 = CombinePaths(remotePath, "file2 #$%");
var contentFile2 = CreateMemoryStream(2048);
contentFile2.Position = 0;
var localPathSubDirectory = Path.Combine(localDirectory, "subdir $1%#");
var remotePathSubDirectory = CombinePaths(remotePath, "subdir $1%#");
var localPathFile3 = Path.Combine(localPathSubDirectory, "file3 %$#");
var remotePathFile3 = CombinePaths(remotePathSubDirectory, "file3 %$#");
var contentFile3 = CreateMemoryStream(256);
contentFile3.Position = 0;
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(remotePathFile1))
{
client.DeleteFile(remotePathFile1);
}
if (client.Exists(remotePathFile2))
{
client.DeleteFile(remotePathFile2);
}
if (client.Exists(remotePathFile3))
{
client.DeleteFile(remotePathFile3);
}
if (client.Exists(remotePathSubDirectory))
{
client.DeleteDirectory(remotePathSubDirectory);
}
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
try
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (!client.Exists(remotePath))
{
client.CreateDirectory(remotePath);
}
client.UploadFile(contentFile1, remotePathFile1);
client.UploadFile(contentFile1, remotePathFile2);
client.CreateDirectory(remotePathSubDirectory);
client.UploadFile(contentFile3, remotePathFile3);
}
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
client.Download(remotePath, new DirectoryInfo(localDirectory));
}
var localFiles = Directory.GetFiles(localDirectory);
Assert.HasCount(2, localFiles);
Assert.IsTrue(localFiles.Contains(localPathFile1));
Assert.IsTrue(localFiles.Contains(localPathFile2));
var localSubDirecties = Directory.GetDirectories(localDirectory);
Assert.HasCount(1, localSubDirecties);
Assert.AreEqual(localPathSubDirectory, localSubDirecties[0]);
var localFilesSubDirectory = Directory.GetFiles(localPathSubDirectory);
Assert.HasCount(1, localFilesSubDirectory);
Assert.AreEqual(localPathFile3, localFilesSubDirectory[0]);
Assert.IsEmpty(Directory.GetDirectories(localPathSubDirectory));
}
finally
{
Directory.Delete(localDirectory, true);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(remotePathFile1))
{
client.DeleteFile(remotePathFile1);
}
if (client.Exists(remotePathFile2))
{
client.DeleteFile(remotePathFile2);
}
if (client.Exists(remotePathFile3))
{
client.DeleteFile(remotePathFile3);
}
if (client.Exists(remotePathSubDirectory))
{
client.DeleteDirectory(remotePathSubDirectory);
}
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpDownloadFileInfoDirectoryDoesNotExistData))]
public void Scp_Download_FileInfo_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation,
string remotePath,
string remoteFile)
{
var completeRemotePath = CombinePaths(remotePath, remoteFile);
// remove complete directory if it's not the home directory of the user
// or else remove the remote file
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
var fileInfo = new FileInfo(Path.GetTempFileName());
try
{
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
try
{
client.Download(completeRemotePath, fileInfo);
Assert.Fail();
}
catch (ScpException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual($"scp: {completeRemotePath}: No such file or directory", ex.Message);
}
}
}
finally
{
fileInfo.Delete();
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpDownloadFileInfoFileDoesNotExistData))]
public void Scp_Download_FileInfo_FileDoesNotExist(IRemotePathTransformation remotePathTransformation,
string remotePath,
string remoteFile)
{
var completeRemotePath = CombinePaths(remotePath, remoteFile);
// remove complete directory if it's not the home directory of the user
// or else remove the remote file
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
client.CreateDirectory(remotePath);
}
}
var fileInfo = new FileInfo(Path.GetTempFileName());
try
{
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
try
{
client.Download(completeRemotePath, fileInfo);
Assert.Fail();
}
catch (ScpException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual($"scp: {completeRemotePath}: No such file or directory", ex.Message);
}
}
}
finally
{
fileInfo.Delete();
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpDownloadFileInfoExistingDirectoryData))]
public void Scp_Download_FileInfo_ExistingDirectory(IRemotePathTransformation remotePathTransformation,
string remotePath)
{
// remove complete directory if it's not the home directory of the user
// or else remove the remote file
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
client.CreateDirectory(remotePath);
}
}
var fileInfo = new FileInfo(Path.GetTempFileName());
fileInfo.Delete();
try
{
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
try
{
client.Download(remotePath, fileInfo);
Assert.Fail();
}
catch (ScpException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual($"scp: {remotePath}: not a regular file", ex.Message);
}
Assert.IsFalse(fileInfo.Exists);
}
}
finally
{
fileInfo.Delete();
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpDownloadFileInfoExistingFileData))]
public void Scp_Download_FileInfo_ExistingFile(IRemotePathTransformation remotePathTransformation,
string remotePath,
string remoteFile,
int size)
{
var completeRemotePath = CombinePaths(remotePath, remoteFile);
// remove complete directory if it's not the home directory of the user
// or else remove the remote file
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
client.CreateDirectory(remotePath);
}
}
// Create a local file larger than the remote file in order to test truncation.
var fileInfo = new FileInfo(CreateTempFile(size + 64));
try
{
var content = CreateMemoryStream(size);
content.Position = 0;
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
client.Upload(content, completeRemotePath);
}
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
client.Download(completeRemotePath, fileInfo);
}
using (var fs = fileInfo.OpenRead())
{
var downloadedBytes = new byte[fs.Length];
Assert.AreEqual(downloadedBytes.Length, fs.Read(downloadedBytes, 0, downloadedBytes.Length));
content.Position = 0;
Assert.AreEqual(CreateHash(content), CreateHash(downloadedBytes));
}
}
finally
{
fileInfo.Delete();
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpDownloadStreamExistingDirectoryData))]
public void Scp_Download_Stream_ExistingDirectory(IRemotePathTransformation remotePathTransformation,
string remotePath)
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
client.CreateDirectory(remotePath);
}
}
var file = Path.GetTempFileName();
File.Delete(file);
try
{
using (var fs = File.OpenWrite(file))
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
try
{
client.Download(remotePath, fs);
Assert.Fail();
}
catch (ScpException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual($"scp: {remotePath}: not a regular file", ex.Message);
}
Assert.AreEqual(0, fs.Length);
}
}
finally
{
File.Delete(file);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpDownloadStreamExistingFileData))]
public void Scp_Download_Stream_ExistingFile(IRemotePathTransformation remotePathTransformation,
string remotePath,
string remoteFile,
int size)
{
var completeRemotePath = CombinePaths(remotePath, remoteFile);
// remove complete directory if it's not the home directory of the user
// or else remove the remote file
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
client.CreateDirectory(remotePath);
}
}
var file = CreateTempFile(size);
try
{
using (var fs = File.OpenRead(file))
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
client.Upload(fs, completeRemotePath);
}
using (var fs = File.OpenRead(file))
using (var downloaded = new MemoryStream(size))
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
client.Download(completeRemotePath, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateHash(fs), CreateHash(downloaded));
}
}
finally
{
File.Delete(file);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpUploadFileStreamDirectoryDoesNotExistData))]
public void Scp_Upload_FileStream_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation,
string remotePath,
string remoteFile)
{
var completeRemotePath = CombinePaths(remotePath, remoteFile);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
var file = CreateTempFile(1000);
try
{
using (var fs = File.OpenRead(file))
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
try
{
client.Upload(fs, completeRemotePath);
Assert.Fail();
}
catch (ScpException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual($"scp: {remotePath}: No such file or directory", ex.Message);
}
}
}
finally
{
File.Delete(file);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpUploadFileStreamExistingDirectoryData))]
public void Scp_Upload_FileStream_ExistingDirectory(IRemotePathTransformation remotePathTransformation,
string remoteFile)
{
using (var client = new SshClient(_connectionInfoFactory.Create()))
{
client.Connect();
using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteFile)))
{
command.Execute();
}
}
var file = CreateTempFile(1000);
try
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
client.CreateDirectory(remoteFile);
}
using (var fs = File.OpenRead(file))
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
try
{
client.Upload(fs, remoteFile);
Assert.Fail();
}
catch (ScpException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual($"scp: {remoteFile}: Is a directory", ex.Message);
}
}
}
finally
{
File.Delete(file);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(remoteFile))
{
client.DeleteDirectory(remoteFile);
}
}
}
}
[TestMethod]
[DynamicData(nameof(ScpUploadFileStreamExistingFileData))]
public void Scp_Upload_FileStream_ExistingFile(IRemotePathTransformation remotePathTransformation,
string remoteFile)
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(remoteFile))
{
client.DeleteFile(remoteFile);
}
}
// original content is bigger than new content to ensure file is fully overwritten
var originalContent = CreateMemoryStream(2000);
var file = CreateTempFile(1000);
try
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
originalContent.Position = 0;
client.UploadFile(originalContent, remoteFile);
}
using (var fs = File.OpenRead(file))
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
client.Upload(fs, remoteFile);
}
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
using (var downloaded = new MemoryStream(1000))
{
client.DownloadFile(remoteFile, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateFileHash(file), CreateHash(downloaded));
}
}
}
finally
{
File.Delete(file);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(remoteFile))
{
client.DeleteFile(remoteFile);
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpUploadFileStreamFileDoesNotExistData))]
public void Scp_Upload_FileStream_FileDoesNotExist(IRemotePathTransformation remotePathTransformation,
string remotePath,
string remoteFile,
int size)
{
var completeRemotePath = CombinePaths(remotePath, remoteFile);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
// remove complete directory if it's not the home directory of the user
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
var file = CreateTempFile(size);
try
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
// create directory if it's not the home directory of the user
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (!client.Exists((remotePath)))
{
client.CreateDirectory(remotePath);
}
}
}
using (var fs = File.OpenRead(file))
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
client.Upload(fs, completeRemotePath);
}
using (var fs = File.OpenRead(file))
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
var sftpFile = client.Get(completeRemotePath);
Assert.AreEqual(GetAbsoluteRemotePath(client, remotePath, remoteFile), sftpFile.FullName);
Assert.AreEqual(size, sftpFile.Length);
var downloaded = client.ReadAllBytes(completeRemotePath);
Assert.AreEqual(CreateHash(fs), CreateHash(downloaded));
}
}
finally
{
File.Delete(file);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
// remove complete directory if it's not the home directory of the user
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
}
/// <summary>
/// https://github.com/sshnet/SSH.NET/issues/289
/// </summary>
[TestMethod]
[DynamicData(nameof(GetScpUploadFileInfoDirectoryDoesNotExistData))]
public void Scp_Upload_FileInfo_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation,
string remotePath,
string remoteFile)
{
var completeRemotePath = CombinePaths(remotePath, remoteFile);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
var file = CreateTempFile(1000);
try
{
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
try
{
client.Upload(new FileInfo(file), completeRemotePath);
Assert.Fail();
}
catch (ScpException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual($"scp: {remotePath}: No such file or directory", ex.Message);
}
}
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
Assert.IsFalse(client.Exists(completeRemotePath));
Assert.IsFalse(client.Exists(remotePath));
}
}
finally
{
File.Delete(file);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
/// <summary>
/// https://github.com/sshnet/SSH.NET/issues/286
/// </summary>
[TestMethod]
[DynamicData(nameof(GetScpUploadFileInfoExistingDirectoryData))]
public void Scp_Upload_FileInfo_ExistingDirectory(IRemotePathTransformation remotePathTransformation,
string remoteFile)
{
using (var client = new SshClient(_connectionInfoFactory.Create()))
{
client.Connect();
using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteFile)))
{
command.Execute();
}
}
var file = CreateTempFile(1000);
try
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
client.CreateDirectory(remoteFile);
}
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
try
{
client.Upload(new FileInfo(file), remoteFile);
Assert.Fail();
}
catch (ScpException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual($"scp: {remoteFile}: Is a directory", ex.Message);
}
}
}
finally
{
File.Delete(file);
using (var client = new SshClient(_connectionInfoFactory.Create()))
{
client.Connect();
using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteFile)))
{
command.Execute();
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpUploadFileInfoExistingFileData))]
public void Scp_Upload_FileInfo_ExistingFile(IRemotePathTransformation remotePathTransformation,
string remoteFile)
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(remoteFile))
{
client.DeleteFile(remoteFile);
}
}
// original content is bigger than new content to ensure file is fully overwritten
var originalContent = CreateMemoryStream(2000);
var file = CreateTempFile(1000);
try
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
originalContent.Position = 0;
client.UploadFile(originalContent, remoteFile);
}
var fileInfo = new FileInfo(file)
{
LastAccessTimeUtc = new DateTime(1973, 8, 13, 20, 15, 33, DateTimeKind.Utc),
LastWriteTimeUtc = new DateTime(1974, 1, 24, 3, 55, 12, DateTimeKind.Utc)
};
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
client.Upload(fileInfo, remoteFile);
}
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
var uploadedFile = client.Get(remoteFile);
Assert.AreEqual(fileInfo.LastAccessTimeUtc, uploadedFile.LastAccessTimeUtc);
Assert.AreEqual(fileInfo.LastWriteTimeUtc, uploadedFile.LastWriteTimeUtc);
using (var downloaded = new MemoryStream(1000))
{
client.DownloadFile(remoteFile, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateFileHash(file), CreateHash(downloaded));
}
}
}
finally
{
File.Delete(file);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(remoteFile))
{
client.DeleteFile(remoteFile);
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpUploadFileInfoFileDoesNotExistData))]
public void Scp_Upload_FileInfo_FileDoesNotExist(IRemotePathTransformation remotePathTransformation,
string remotePath,
string remoteFile,
int size)
{
var completeRemotePath = CombinePaths(remotePath, remoteFile);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.DeleteFile(completeRemotePath);
}
// remove complete directory if it's not the home directory of the user
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
var file = CreateTempFile(size);
try
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
// create directory if it's not the home directory of the user
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (!client.Exists(remotePath))
{
client.CreateDirectory(remotePath);
}
}
}
var fileInfo = new FileInfo(file)
{
LastAccessTimeUtc = new DateTime(1973, 8, 13, 20, 15, 33, DateTimeKind.Utc),
LastWriteTimeUtc = new DateTime(1974, 1, 24, 3, 55, 12, DateTimeKind.Utc)
};
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
client.Upload(fileInfo, completeRemotePath);
}
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
var uploadedFile = client.Get(completeRemotePath);
Assert.AreEqual(fileInfo.LastAccessTimeUtc, uploadedFile.LastAccessTimeUtc);
Assert.AreEqual(fileInfo.LastWriteTimeUtc, uploadedFile.LastWriteTimeUtc);
Assert.AreEqual(size, uploadedFile.Length);
using (var downloaded = new MemoryStream(size))
{
client.DownloadFile(completeRemotePath, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateFileHash(file), CreateHash(downloaded));
}
}
}
finally
{
File.Delete(file);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(completeRemotePath))
{
client.Delete(completeRemotePath);
}
// remove complete directory if it's not the home directory of the user
if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
{
if (client.Exists(remotePath))
{
client.DeleteDirectory(remotePath);
}
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpUploadDirectoryInfoDirectoryDoesNotExistData))]
public void Scp_Upload_DirectoryInfo_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation,
string remoteDirectory)
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists((remoteDirectory)))
{
client.DeleteDirectory(remoteDirectory);
}
}
var localDirectory = Path.GetTempFileName();
File.Delete(localDirectory);
Directory.CreateDirectory(localDirectory);
try
{
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
try
{
client.Upload(new DirectoryInfo(localDirectory), remoteDirectory);
Assert.Fail();
}
catch (ScpException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual($"scp: {remoteDirectory}: No such file or directory", ex.Message);
}
}
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
Assert.IsFalse(client.Exists(remoteDirectory));
}
}
finally
{
Directory.Delete(localDirectory, true);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists((remoteDirectory)))
{
client.DeleteDirectory(remoteDirectory);
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpUploadDirectoryInfoExistingDirectoryData))]
public void Scp_Upload_DirectoryInfo_ExistingDirectory(IRemotePathTransformation remotePathTransformation,
string remoteDirectory)
{
string absoluteRemoteDirectory = GetAbsoluteRemotePath(_connectionInfoFactory, remoteDirectory);
var remotePathFile1 = CombinePaths(remoteDirectory, "file1");
var remotePathFile2 = CombinePaths(remoteDirectory, "file2");
var absoluteRemoteSubDirectory1 = CombinePaths(absoluteRemoteDirectory, "sub1");
var remoteSubDirectory1 = CombinePaths(remoteDirectory, "sub1");
var remotePathSubFile1 = CombinePaths(remoteSubDirectory1, "file1");
var remotePathSubFile2 = CombinePaths(remoteSubDirectory1, "file2");
var absoluteRemoteSubDirectory2 = CombinePaths(absoluteRemoteDirectory, "sub2");
var remoteSubDirectory2 = CombinePaths(remoteDirectory, "sub2");
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(remotePathSubFile1))
{
client.DeleteFile(remotePathSubFile1);
}
if (client.Exists(remotePathSubFile2))
{
client.DeleteFile(remotePathSubFile2);
}
if (client.Exists(remoteSubDirectory1))
{
client.DeleteDirectory(remoteSubDirectory1);
}
if (client.Exists(remoteSubDirectory2))
{
client.DeleteDirectory(remoteSubDirectory2);
}
if (client.Exists(remotePathFile1))
{
client.DeleteFile(remotePathFile1);
}
if (client.Exists(remotePathFile2))
{
client.DeleteFile(remotePathFile2);
}
if (remoteDirectory.Length > 0 && remoteDirectory != "." && remoteDirectory != client.WorkingDirectory)
{
if (client.Exists(remoteDirectory))
{
client.DeleteDirectory(remoteDirectory);
}
client.CreateDirectory(remoteDirectory);
}
}
var localDirectory = Path.GetTempFileName();
File.Delete(localDirectory);
Directory.CreateDirectory(localDirectory);
var localPathFile1 = Path.Combine(localDirectory, "file1");
var localPathFile2 = Path.Combine(localDirectory, "file2");
var localSubDirectory1 = Path.Combine(localDirectory, "sub1");
var localPathSubFile1 = Path.Combine(localSubDirectory1, "file1");
var localPathSubFile2 = Path.Combine(localSubDirectory1, "file2");
var localSubDirectory2 = Path.Combine(localDirectory, "sub2");
try
{
CreateFile(localPathFile1, 2000);
File.SetLastWriteTimeUtc(localPathFile1, new DateTime(2015, 8, 24, 5, 32, 16, DateTimeKind.Utc));
CreateFile(localPathFile2, 1000);
File.SetLastWriteTimeUtc(localPathFile2, new DateTime(2012, 3, 27, 23, 2, 54, DateTimeKind.Utc));
// create subdirectory with two files
Directory.CreateDirectory(localSubDirectory1);
CreateFile(localPathSubFile1, 1000);
File.SetLastWriteTimeUtc(localPathSubFile1, new DateTime(2013, 4, 12, 16, 54, 22, DateTimeKind.Utc));
CreateFile(localPathSubFile2, 2000);
File.SetLastWriteTimeUtc(localPathSubFile2, new DateTime(2015, 8, 4, 12, 43, 12, DateTimeKind.Utc));
Directory.SetLastWriteTimeUtc(localSubDirectory1,
new DateTime(2014, 6, 12, 13, 2, 44, DateTimeKind.Utc));
// create empty subdirectory
Directory.CreateDirectory(localSubDirectory2);
Directory.SetLastWriteTimeUtc(localSubDirectory2,
new DateTime(2011, 5, 14, 1, 5, 12, DateTimeKind.Utc));
Directory.SetLastWriteTimeUtc(localDirectory, new DateTime(2015, 10, 14, 22, 45, 11, DateTimeKind.Utc));
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
client.Upload(new DirectoryInfo(localDirectory), remoteDirectory);
}
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
Assert.IsTrue(client.Exists(remoteDirectory));
var remoteSftpDirectory = client.Get(remoteDirectory);
Assert.IsNotNull(remoteSftpDirectory);
Assert.AreEqual(absoluteRemoteDirectory, remoteSftpDirectory.FullName);
Assert.IsTrue(remoteSftpDirectory.IsDirectory);
Assert.IsFalse(remoteSftpDirectory.IsRegularFile);
// Due to CVE-2018-20685, we can no longer set the times or modes on a file or directory
// that refers to the current directory ('.'), the parent directory ('..') or a directory
// containing a forward slash ('/').
Assert.AreNotEqual(Directory.GetLastWriteTimeUtc(localDirectory), remoteSftpDirectory.LastWriteTimeUtc);
Assert.IsTrue(client.Exists(remotePathFile1));
Assert.AreEqual(CreateFileHash(localPathFile1), CreateRemoteFileHash(client, remotePathFile1));
var remoteSftpFile = client.Get(remotePathFile1);
Assert.IsNotNull(remoteSftpFile);
Assert.IsFalse(remoteSftpFile.IsDirectory);
Assert.IsTrue(remoteSftpFile.IsRegularFile);
Assert.AreEqual(File.GetLastWriteTimeUtc(localPathFile1), remoteSftpFile.LastWriteTimeUtc);
Assert.IsTrue(client.Exists(remotePathFile2));
Assert.AreEqual(CreateFileHash(localPathFile2), CreateRemoteFileHash(client, remotePathFile2));
remoteSftpFile = client.Get(remotePathFile2);
Assert.IsNotNull(remoteSftpFile);
Assert.IsFalse(remoteSftpFile.IsDirectory);
Assert.IsTrue(remoteSftpFile.IsRegularFile);
Assert.AreEqual(File.GetLastWriteTimeUtc(localPathFile2), remoteSftpFile.LastWriteTimeUtc);
Assert.IsTrue(client.Exists(remoteSubDirectory1));
remoteSftpDirectory = client.Get(remoteSubDirectory1);
Assert.IsNotNull(remoteSftpDirectory);
Assert.AreEqual(absoluteRemoteSubDirectory1, remoteSftpDirectory.FullName);
Assert.IsTrue(remoteSftpDirectory.IsDirectory);
Assert.IsFalse(remoteSftpDirectory.IsRegularFile);
Assert.AreEqual(Directory.GetLastWriteTimeUtc(localSubDirectory1), remoteSftpDirectory.LastWriteTimeUtc);
Assert.IsTrue(client.Exists(remotePathSubFile1));
Assert.AreEqual(CreateFileHash(localPathSubFile1), CreateRemoteFileHash(client, remotePathSubFile1));
Assert.IsTrue(client.Exists(remotePathSubFile2));
Assert.AreEqual(CreateFileHash(localPathSubFile2), CreateRemoteFileHash(client, remotePathSubFile2));
Assert.IsTrue(client.Exists(remoteSubDirectory2));
remoteSftpDirectory = client.Get(remoteSubDirectory2);
Assert.IsNotNull(remoteSftpDirectory);
Assert.AreEqual(absoluteRemoteSubDirectory2, remoteSftpDirectory.FullName);
Assert.IsTrue(remoteSftpDirectory.IsDirectory);
Assert.IsFalse(remoteSftpDirectory.IsRegularFile);
Assert.AreEqual(Directory.GetLastWriteTimeUtc(localSubDirectory2), remoteSftpDirectory.LastWriteTimeUtc);
}
}
finally
{
Directory.Delete(localDirectory, true);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(remotePathSubFile1))
{
client.DeleteFile(remotePathSubFile1);
}
if (client.Exists(remotePathSubFile2))
{
client.DeleteFile(remotePathSubFile2);
}
if (client.Exists(remoteSubDirectory1))
{
client.DeleteDirectory(remoteSubDirectory1);
}
if (client.Exists(remoteSubDirectory2))
{
client.DeleteDirectory(remoteSubDirectory2);
}
if (client.Exists(remotePathFile1))
{
client.DeleteFile(remotePathFile1);
}
if (client.Exists(remotePathFile2))
{
client.DeleteFile(remotePathFile2);
}
if (remoteDirectory.Length > 0 && remoteDirectory != "." && remoteDirectory != client.WorkingDirectory)
{
if (client.Exists(remoteDirectory))
{
client.DeleteDirectory(remoteDirectory);
}
}
}
}
}
[TestMethod]
[DynamicData(nameof(GetScpUploadDirectoryInfoExistingFileData))]
public void Scp_Upload_DirectoryInfo_ExistingFile(IRemotePathTransformation remotePathTransformation,
string remoteDirectory)
{
var remotePathFile1 = CombinePaths(remoteDirectory, "file1");
var remotePathFile2 = CombinePaths(remoteDirectory, "file2");
using (var client = new SshClient(_connectionInfoFactory.Create()))
{
client.Connect();
Console.WriteLine(client.ConnectionInfo.CurrentKeyExchangeAlgorithm);
using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory)))
{
command.Execute();
}
}
var localDirectory = Path.GetTempFileName();
File.Delete(localDirectory);
Directory.CreateDirectory(localDirectory);
var localPathFile1 = Path.Combine(localDirectory, "file1");
var localPathFile2 = Path.Combine(localDirectory, "file2");
try
{
CreateFile(localPathFile1, 50);
CreateFile(localPathFile2, 50);
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
if (remotePathTransformation != null)
{
client.RemotePathTransformation = remotePathTransformation;
}
client.Connect();
CreateRemoteFile(client, remoteDirectory, 10);
try
{
client.Upload(new DirectoryInfo(localDirectory), remoteDirectory);
Assert.Fail();
}
catch (ScpException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual($"scp: {remoteDirectory}: Not a directory", ex.Message);
}
}
}
finally
{
Directory.Delete(localDirectory, true);
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
client.Connect();
if (client.Exists(remotePathFile1))
{
client.DeleteFile(remotePathFile1);
}
if (client.Exists(remotePathFile2))
{
client.DeleteFile(remotePathFile2);
}
if (client.Exists((remoteDirectory)))
{
client.DeleteFile(remoteDirectory);
}
}
}
}
private static IEnumerable<object[]> GetScpDownloadStreamDirectoryDoesNotExistData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-directorydoesnotexist", "scp-file" };
yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-directorydoesnotexist", "scp-file" };
}
private static IEnumerable<object[]> GetScpUploadFileInfoFileDoesNotExistData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet", "test123", 0 };
yield return new object[] { RemotePathTransformation.None, "/home/sshnet", "test123", 5 * 1024 * 1024 };
yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 1024 };
yield return new object[] { null, "/home/sshnet/scp test", "file 123", 1024 };
yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-test", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 };
yield return new object[] { null, "", "scp-issue280", 1024 };
}
private static IEnumerable<object[]> GetScpUploadFileStreamFileDoesNotExistData()
{
yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 0 };
yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 1024 };
yield return new object[] { null, "/home/sshnet/scp test", "file 123", 1024 };
yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/scp-test", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 };
yield return new object[] { RemotePathTransformation.None, "", "scp-issue280", 1024 };
}
private static IEnumerable<object[]> GetScpUploadDirectoryInfoExistingDirectoryData()
{
yield return new object[] { RemotePathTransformation.None, "scp-directorydoesnotexist" };
yield return new object[] { RemotePathTransformation.None, "." };
yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%" };
}
private static IEnumerable<object[]> GetScpUploadDirectoryInfoExistingFileData()
{
yield return new object[] { RemotePathTransformation.None, "scp-upload-file" };
}
private static IEnumerable<object[]> ScpUploadFileStreamExistingFileData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-upload-file" };
}
private static IEnumerable<object[]> GetScpDownloadStreamFileDoesNotExistData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet", "scp-filedoesnotexist" };
}
private static IEnumerable<object[]> GetScpDownloadDirectoryInfoDirectoryDoesNotExistData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-download" };
}
private static IEnumerable<object[]> GetScpDownloadDirectoryInfoExistingFileData()
{
yield return new object[] { RemotePathTransformation.None, "scp-download" };
}
private static IEnumerable<object[]> GetScpDownloadDirectoryInfoExistingDirectoryData()
{
yield return new object[] { RemotePathTransformation.None, "scp-download" };
yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'space \\tab\tlf*?[#~=%" };
}
private static IEnumerable<object[]> GetScpDownloadFileInfoDirectoryDoesNotExistData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-directorydoesnotexist", "scp-file" };
}
private static IEnumerable<object[]> GetScpDownloadFileInfoFileDoesNotExistData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet", "scp-filedoesnotexist" };
}
private static IEnumerable<object[]> GetScpDownloadFileInfoExistingDirectoryData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-test" };
}
private static IEnumerable<object[]> GetScpDownloadFileInfoExistingFileData()
{
yield return new object[] { null, "", "file 123", 0 };
yield return new object[] { null, "", "file 123", 1024 };
yield return new object[] { RemotePathTransformation.ShellQuote, "", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 };
yield return new object[] { null, "/home/sshnet/scp test", "file 123", 1024 };
yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 1024 };
yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/scp-test", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 };
}
private static IEnumerable<object[]> GetScpDownloadStreamExistingDirectoryData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-test" };
}
private static IEnumerable<object[]> GetScpDownloadStreamExistingFileData()
{
yield return new object[] { null, "", "file 123", 0 };
yield return new object[] { null, "", "file 123", 1024 };
yield return new object[] { RemotePathTransformation.ShellQuote, "", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 };
yield return new object[] { null, "/home/sshnet/scp test", "file 123", 1024 };
yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 1024 };
yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/scp-test", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 };
}
private static IEnumerable<object[]> GetScpUploadFileStreamDirectoryDoesNotExistData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-issue289", "file123" };
}
private static IEnumerable<object[]> GetScpUploadFileStreamExistingDirectoryData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-issue286" };
}
private static IEnumerable<object[]> GetScpUploadFileInfoDirectoryDoesNotExistData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-issue289", "file123" };
}
private static IEnumerable<object[]> GetScpUploadFileInfoExistingDirectoryData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-issue286" };
}
private static IEnumerable<object[]> GetScpUploadFileInfoExistingFileData()
{
yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-upload-file" };
}
private static IEnumerable<object[]> GetScpUploadDirectoryInfoDirectoryDoesNotExistData()
{
yield return new object[] { RemotePathTransformation.None, "scp-directorydoesnotexist" };
}
/// <summary>
/// A recursive download must never write outside of the destination directory, even
/// when the server returns a file name containing a path separator. Backslash is a
/// legal byte in a Unix file name, so a stock OpenSSH server transmits it verbatim;
/// on a Windows client it would otherwise be interpreted as a directory separator.
/// </summary>
[TestMethod]
public void Scp_Download_DirectoryInfo_ServerNameWithSeparator_StaysInsideDestination()
{
var remoteDirectory = "/tmp/sshnet-scp-guard-" + Guid.NewGuid().ToString("N");
// Set up a remote directory containing a normal file and a file whose name
// contains a backslash (a single, valid Unix file name).
using (var client = new SshClient(_connectionInfoFactory.Create()))
{
client.Connect();
_ = client.RunCommand("mkdir -p '" + remoteDirectory + "'");
_ = client.RunCommand("printf '%s' good > '" + remoteDirectory + "/good.txt'");
_ = client.RunCommand("printf '%s' ESCAPED > '" + remoteDirectory + "/..\\owned.txt'");
}
var localRoot = Path.GetTempFileName();
File.Delete(localRoot);
_ = Directory.CreateDirectory(localRoot);
var destination = Path.Combine(localRoot, "download");
_ = Directory.CreateDirectory(destination);
// Where "..\owned.txt", combined with the destination, would land on a Windows client.
var escapedFile = Path.Combine(localRoot, "owned.txt");
try
{
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
client.Connect();
try
{
client.Download(remoteDirectory, new DirectoryInfo(destination));
}
catch (ScpException)
{
// Expected on platforms where '\' is a directory separator: the
// download is aborted rather than allowed to escape.
}
}
// The security invariant, asserted on every platform: nothing is written
// outside of the caller-supplied destination directory.
Assert.IsFalse(File.Exists(escapedFile),
"A file was written outside of the destination directory: " + escapedFile);
}
finally
{
using (var client = new SshClient(_connectionInfoFactory.Create()))
{
client.Connect();
_ = client.RunCommand("rm -rf '" + remoteDirectory + "'");
}
Directory.Delete(localRoot, recursive: true);
}
}
private static void CreateRemoteFile(ScpClient client, string remoteFile, int size)
{
var file = CreateTempFile(size);
try
{
using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
client.Upload(fs, remoteFile);
}
}
finally
{
File.Delete(file);
}
}
private static string GetAbsoluteRemotePath(SftpClient client, string directoryName, string fileName)
{
var absolutePath = string.Empty;
if (directoryName.Length == 0)
{
absolutePath += client.WorkingDirectory;
}
else
{
if (directoryName[0] != '/')
{
absolutePath += client.WorkingDirectory + "/" + directoryName;
}
else
{
absolutePath = directoryName;
}
}
return absolutePath + "/" + fileName;
}
private static string GetAbsoluteRemotePath(IConnectionInfoFactory connectionInfoFactory, string directoryName)
{
var absolutePath = string.Empty;
if (directoryName.Length == 0 || directoryName == ".")
{
using (var client = new SftpClient(connectionInfoFactory.Create()))
{
client.Connect();
absolutePath += client.WorkingDirectory;
}
}
else
{
if (directoryName[0] != '/')
{
using (var client = new SftpClient(connectionInfoFactory.Create()))
{
client.Connect();
absolutePath += client.WorkingDirectory + "/" + directoryName;
}
}
else
{
absolutePath = directoryName;
}
}
return absolutePath;
}
private static string CreateRemoteFileHash(SftpClient client, string remotePath)
{
using (var fs = client.OpenRead(remotePath))
{
return CreateHash(fs);
}
}
private static string CombinePaths(string path1, string path2)
{
if (path1.Length == 0)
{
return path2;
}
if (path2.Length == 0)
{
return path1;
}
return path1 + "/" + path2;
}
[TestMethod]
public async Task UploadWithUseDirectoryFlagFalse()
{
string remoteDirectory = "/home/sshnet/usedirectoryflagfalsetest";
// remote cleanup
using (var sftpClient = new SftpClient(_connectionInfoFactory.Create()))
{
await sftpClient.ConnectAsync(CancellationToken.None);
if (await sftpClient.ExistsAsync(remoteDirectory))
{
await sftpClient.DeleteDirectoryAsync(remoteDirectory);
}
await sftpClient.CreateDirectoryAsync(remoteDirectory);
}
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
client.UseDirectoryFlag = false;
await client.ConnectAsync(CancellationToken.None);
int tempFileSize = 1024;
string tempFilePath = CreateTempFile(tempFileSize);
MemoryStream downloadedStream = new();
// FileInfo overload
client.Upload(new FileInfo(tempFilePath), $"{remoteDirectory}/file1");
client.Download($"{remoteDirectory}/file1", downloadedStream);
Assert.AreEqual(tempFileSize, downloadedStream.Length);
// Stream overload
downloadedStream = new();
using (Stream stream = File.OpenRead(tempFilePath))
{
client.Upload(stream, $"{remoteDirectory}/file2");
client.Download($"{remoteDirectory}/file2", downloadedStream);
Assert.AreEqual(tempFileSize, downloadedStream.Length);
}
// DirectoryInfo overload
downloadedStream = new();
string tempDir = Path.Combine(Path.GetTempPath(), "SSH.NET_UploadWithUseDirectoryFlagFalseTest");
if (Directory.Exists(tempDir))
{
Directory.Delete(tempDir, true);
}
Directory.CreateDirectory(tempDir);
File.Move(tempFilePath, $"{tempDir}/file3");
client.Upload(new DirectoryInfo(tempDir), remoteDirectory);
client.Download($"{remoteDirectory}/file3", downloadedStream);
Assert.AreEqual(tempFileSize, downloadedStream.Length);
}
}
}
}