diff --git a/src/Renci.SshNet.IntegrationTests/.editorconfig b/src/Renci.SshNet.IntegrationTests/.editorconfig new file mode 100644 index 000000000..b94e29112 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/.editorconfig @@ -0,0 +1,32 @@ +[*.cs] + +#### SYSLIB diagnostics #### + +# SYSLIB1045: Use 'GeneratedRegexAttribute' to generate the regular expression implementation at compile-time +# +# TODO: Remove this when https://github.com/sshnet/SSH.NET/issues/1131 is implemented. +dotnet_diagnostic.SYSLIB1045.severity = none + +### StyleCop Analyzers rules ### + +#### .NET Compiler Platform analysers rules #### + +# IDE0007: Use var instead of explicit type +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0007 +dotnet_diagnostic.IDE0007.severity = suggestion + +# IDE0028: Use collection initializers +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0028 +dotnet_diagnostic.IDE0028.severity = suggestion + +# IDE0058: Remove unnecessary expression value +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0058 +dotnet_diagnostic.IDE0058.severity = suggestion + +# IDE0059: Remove unnecessary value assignment +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0059 +dotnet_diagnostic.IDE0059.severity = suggestion + +# IDE0230: Use UTF-8 string literal +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0230 +dotnet_diagnostic.IDE0230.severity = suggestion diff --git a/src/Renci.SshNet.IntegrationTests/Dockerfile b/src/Renci.SshNet.IntegrationTests/Dockerfile new file mode 100644 index 000000000..811d51543 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Dockerfile @@ -0,0 +1,49 @@ +FROM alpine:latest + +COPY --chown=root:root server/ssh /etc/ssh/ +COPY --chown=root:root server/script /opt/sshnet +COPY user/sshnet /home/sshnet/.ssh + +RUN apk update && apk upgrade --no-cache && \ + apk add --no-cache syslog-ng && \ + # install and configure sshd + apk add --no-cache openssh && \ + # install openssh-server-pam to allow for keyboard-interactive authentication + apk add --no-cache openssh-server-pam && \ + dos2unix /etc/ssh/* && \ + chmod 400 /etc/ssh/ssh*key && \ + sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config && \ + sed -i 's/#LogLevel\s*INFO/LogLevel DEBUG3/' /etc/ssh/sshd_config && \ + echo 'PubkeyAcceptedAlgorithms ssh-rsa' >> /etc/ssh/sshd_config && \ + chmod 646 /etc/ssh/sshd_config && \ + # install and configure sudo + apk add --no-cache sudo && \ + addgroup sudo && \ + # allow root to run any command + echo 'root ALL=(ALL) ALL' > /etc/sudoers && \ + # allow everyone in the 'sudo' group to run any command without a password + echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \ + # add user to run most of the integration tests + adduser -D sshnet && \ + passwd -u sshnet && \ + echo 'sshnet:ssh4ever' | chpasswd && \ + dos2unix /home/sshnet/.ssh/* && \ + chown -R sshnet:sshnet /home/sshnet && \ + chmod -R 700 /home/sshnet/.ssh && \ + chmod -R 644 /home/sshnet/.ssh/authorized_keys && \ + # add user to administer container (update configs, restart sshd) + adduser -D sshnetadm && \ + passwd -u sshnetadm && \ + echo 'sshnetadm:ssh4ever' | chpasswd && \ + addgroup sshnetadm sudo && \ + dos2unix /opt/sshnet/* && \ + # install shadow package; we use chage command in this package to expire/unexpire password of the sshnet user + apk add --no-cache shadow && \ + # allow us to use telnet command; we use this in the remote port forwarding tests + apk --no-cache add busybox-extras && \ + # install full-fledged ps command + apk add --no-cache procps + +EXPOSE 22 22 + +ENTRYPOINT ["/opt/sshnet/start.sh"] \ No newline at end of file diff --git a/src/Renci.SshNet.IntegrationTests/IntegrationTests.csproj b/src/Renci.SshNet.IntegrationTests/IntegrationTests.csproj new file mode 100644 index 000000000..244ee6f3a --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/IntegrationTests.csproj @@ -0,0 +1,47 @@ + + + + net7.0 + enable + enable + + false + true + true + + $(NoWarn);CS1591 + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + LocalSshNet + + + + + + PreserveNewest + + + + diff --git a/src/Renci.SshNet.IntegrationTests/ScpClientTests.cs b/src/Renci.SshNet.IntegrationTests/ScpClientTests.cs new file mode 100644 index 000000000..a03b04a3a --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/ScpClientTests.cs @@ -0,0 +1,41 @@ +namespace IntegrationTests +{ + /// + /// The SCP client integration tests + /// + [TestClass] + public class ScpClientTests : IntegrationTestBase, IDisposable + { + private readonly ScpClient _scpClient; + + public ScpClientTests() + { + _scpClient = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password); + _scpClient.Connect(); + } + + [TestMethod] + + public void Upload_And_Download_FileStream() + { + var file = $"/tmp/{Guid.NewGuid()}.txt"; + var fileContent = "File content !@#$%^&*()_+{}:,./<>[];'\\|"; + + using var uploadStream = new MemoryStream(Encoding.UTF8.GetBytes(fileContent)); + _scpClient.Upload(uploadStream, file); + + using var downloadStream = new MemoryStream(); + _scpClient.Download(file, downloadStream); + + var result = Encoding.UTF8.GetString(downloadStream.ToArray()); + + Assert.AreEqual(fileContent, result); + } + + public void Dispose() + { + _scpClient.Disconnect(); + _scpClient.Dispose(); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/SftpClientTests.cs b/src/Renci.SshNet.IntegrationTests/SftpClientTests.cs new file mode 100644 index 000000000..dfb406475 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/SftpClientTests.cs @@ -0,0 +1,100 @@ +namespace IntegrationTests +{ + /// + /// The SFTP client integration tests + /// + [TestClass] + public class SftpClientTests : IntegrationTestBase, IDisposable + { + private readonly SftpClient _sftpClient; + + public SftpClientTests() + { + _sftpClient = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password); + _sftpClient.Connect(); + } + + [TestMethod] + public void Create_directory_with_contents_and_list_it() + { + var testDirectory = "/home/sshnet/sshnet-test"; + var testFileName = "test-file.txt"; + var testFilePath = $"{testDirectory}/{testFileName}"; + var testContent = "file content"; + + // Create new directory and check if it exists + _sftpClient.CreateDirectory(testDirectory); + Assert.IsTrue(_sftpClient.Exists(testDirectory)); + + // Upload file and check if it exists + using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent)); + _sftpClient.UploadFile(fileStream, testFilePath); + Assert.IsTrue(_sftpClient.Exists(testFilePath)); + + // Check if ListDirectory works + var files = _sftpClient.ListDirectory(testDirectory); + + _sftpClient.DeleteFile(testFilePath); + _sftpClient.DeleteDirectory(testDirectory); + + var builder = new StringBuilder(); + foreach (var file in files) + { + builder.AppendLine($"{file.FullName} {file.IsRegularFile} {file.IsDirectory}"); + } + + Assert.AreEqual(@"/home/sshnet/sshnet-test/. False True +/home/sshnet/sshnet-test/.. False True +/home/sshnet/sshnet-test/test-file.txt True False +", builder.ToString()); + } + + [TestMethod] + public async Task Create_directory_with_contents_and_list_it_async() + { + var testDirectory = "/home/sshnet/sshnet-test"; + var testFileName = "test-file.txt"; + var testFilePath = $"{testDirectory}/{testFileName}"; + var testContent = "file content"; + + // Create new directory and check if it exists + _sftpClient.CreateDirectory(testDirectory); + Assert.IsTrue(_sftpClient.Exists(testDirectory)); + + // Upload file and check if it exists + using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent)); + _sftpClient.UploadFile(fileStream, testFilePath); + Assert.IsTrue(_sftpClient.Exists(testFilePath)); + + // Check if ListDirectory works + var files = await _sftpClient.ListDirectoryAsync(testDirectory, CancellationToken.None); + + _sftpClient.DeleteFile(testFilePath); + _sftpClient.DeleteDirectory(testDirectory); + + var builder = new StringBuilder(); + foreach (var file in files) + { + builder.AppendLine($"{file.FullName} {file.IsRegularFile} {file.IsDirectory}"); + } + + Assert.AreEqual(@"/home/sshnet/sshnet-test/. False True +/home/sshnet/sshnet-test/.. False True +/home/sshnet/sshnet-test/test-file.txt True False +", builder.ToString()); + } + + [TestMethod] + [ExpectedException(typeof(SftpPermissionDeniedException), "Permission denied")] + public void Test_Sftp_ListDirectory_Permission_Denied() + { + _sftpClient.ListDirectory("/root"); + } + + public void Dispose() + { + _sftpClient.Disconnect(); + _sftpClient.Dispose(); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/SshClientTests.cs b/src/Renci.SshNet.IntegrationTests/SshClientTests.cs new file mode 100644 index 000000000..33a90d13e --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/SshClientTests.cs @@ -0,0 +1,32 @@ +namespace IntegrationTests +{ + /// + /// The SSH client integration tests + /// + [TestClass] + public class SshClientTests : IntegrationTestBase, IDisposable + { + private readonly SshClient _sshClient; + + public SshClientTests() + { + _sshClient = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password); + _sshClient.Connect(); + } + + [TestMethod] + public void Echo_Command_with_all_characters() + { + var builder = new StringBuilder(); + var response = _sshClient.RunCommand("echo $'test !@#$%^&*()_+{}:,./<>[];\\|'"); + + Assert.AreEqual("test !@#$%^&*()_+{}:,./<>[];\\|\n", response.Result); + } + + public void Dispose() + { + _sshClient.Disconnect(); + _sshClient.Dispose(); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/TestInitializer.cs b/src/Renci.SshNet.IntegrationTests/TestInitializer.cs new file mode 100644 index 000000000..16b0a3eca --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/TestInitializer.cs @@ -0,0 +1,19 @@ +namespace IntegrationTests +{ + [TestClass] + public class TestInitializer + { + [AssemblyInitialize] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "MSTests requires context parameter")] + public static async Task Initialize(TestContext context) + { + await InfrastructureFixture.Instance.InitializeAsync(); + } + + [AssemblyCleanup] + public static async Task Cleanup() + { + await InfrastructureFixture.Instance.DisposeAsync(); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs b/src/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs new file mode 100644 index 000000000..63e6c70ce --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs @@ -0,0 +1,76 @@ +using DotNet.Testcontainers.Images; +using DotNet.Testcontainers.Builders; +using DotNet.Testcontainers.Containers; + +namespace IntegrationTests.TestsFixtures +{ + public sealed class InfrastructureFixture : IDisposable + { + private InfrastructureFixture() + { + } + + private static readonly Lazy InstanceLazy = new Lazy(() => new InfrastructureFixture()); + + public static InfrastructureFixture Instance + { + get + { + return InstanceLazy.Value; + } + } + + private IContainer? _sshServer; + + private IFutureDockerImage? _sshServerImage; + + public string? SshServerHostName { get; set; } + + public ushort SshServerPort { get; set; } + + public SshUser AdminUser = new SshUser("sshnetadm", "ssh4ever"); + + public SshUser User = new SshUser("sshnet", "ssh4ever"); + + public async Task InitializeAsync() + { + _sshServerImage = new ImageFromDockerfileBuilder() + .WithName("renci-ssh-tests-server-image") + .WithDockerfileDirectory(CommonDirectoryPath.GetSolutionDirectory(), "Renci.SshNet.IntegrationTests") + .WithDockerfile("Dockerfile") + .WithDeleteIfExists(true) + + .Build(); + + await _sshServerImage.CreateAsync(); + + _sshServer = new ContainerBuilder() + .WithHostname("renci-ssh-tests-server") + .WithImage(_sshServerImage) + .WithPortBinding(22, true) + .Build(); + + await _sshServer.StartAsync(); + + SshServerPort = _sshServer.GetMappedPublicPort(22); + SshServerHostName = _sshServer.Hostname; + } + + public async Task DisposeAsync() + { + if (_sshServer != null) + { + await _sshServer.DisposeAsync(); + } + + if (_sshServerImage != null) + { + await _sshServerImage.DisposeAsync(); + } + } + + public void Dispose() + { + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs b/src/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs new file mode 100644 index 000000000..521f947cc --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs @@ -0,0 +1,66 @@ +namespace IntegrationTests.TestsFixtures +{ + /// + /// The base class for integration tests + /// + public abstract class IntegrationTestBase + { + private readonly InfrastructureFixture _infrastructureFixture; + + /// + /// The SSH Server host name. + /// + public string? SshServerHostName + { + get + { + return _infrastructureFixture.SshServerHostName; + } + } + + /// + /// The SSH Server host name + /// + public ushort SshServerPort + { + get + { + return _infrastructureFixture.SshServerPort; + } + } + + /// + /// The admin user that can use SSH Server. + /// + public SshUser AdminUser + { + get + { + return _infrastructureFixture.AdminUser; + } + } + + /// + /// The normal user that can use SSH Server. + /// + public SshUser User + { + get + { + return _infrastructureFixture.User; + } + } + + protected IntegrationTestBase() + { + _infrastructureFixture = InfrastructureFixture.Instance; + ShowInfrastructureInformation(); + } + + private void ShowInfrastructureInformation() + { + Console.WriteLine($"SSH Server host name: {_infrastructureFixture.SshServerHostName}"); + Console.WriteLine($"SSH Server port: {_infrastructureFixture.SshServerPort}"); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/TestsFixtures/SshUser.cs b/src/Renci.SshNet.IntegrationTests/TestsFixtures/SshUser.cs new file mode 100644 index 000000000..5f2ee4d56 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/TestsFixtures/SshUser.cs @@ -0,0 +1,16 @@ +namespace IntegrationTests.TestsFixtures +{ + public class SshUser + { + public string UserName { get; } + + public string Password { get; } + + public SshUser(string userName, string password) + { + UserName = userName; + Password = password; + } + } +} + diff --git a/src/Renci.SshNet.IntegrationTests/Usings.cs b/src/Renci.SshNet.IntegrationTests/Usings.cs new file mode 100644 index 000000000..bc21218d4 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Usings.cs @@ -0,0 +1,36 @@ +#pragma warning disable IDE0005 + +extern alias LocalSshNet; + +global using System.Text; + +global using Microsoft.VisualStudio.TestTools.UnitTesting; + +global using IntegrationTests.TestsFixtures; + +// The testcontainers library uses SSH.NET, so we have two versions of SSH.NET in the project. +// We need to explicitly choose which version we want to test. +// To avoid problems, we import all namespaces. +global using LocalSshNet::Renci.SshNet; +global using LocalSshNet::Renci.SshNet.Abstractions; +global using LocalSshNet::Renci.SshNet.Channels; +global using LocalSshNet::Renci.SshNet.Common; +global using LocalSshNet::Renci.SshNet.Compression; +global using LocalSshNet::Renci.SshNet.Connection; +global using LocalSshNet::Renci.SshNet.Messages; +global using LocalSshNet::Renci.SshNet.Messages.Authentication; +global using LocalSshNet::Renci.SshNet.Messages.Connection; +global using LocalSshNet::Renci.SshNet.Messages.Transport; +global using LocalSshNet::Renci.SshNet.NetConf; +global using LocalSshNet::Renci.SshNet.Security; +global using LocalSshNet::Renci.SshNet.Security.Chaos; +global using LocalSshNet::Renci.SshNet.Security.Chaos.NaCl; +global using LocalSshNet::Renci.SshNet.Security.Chaos.NaCl.Internal; +global using LocalSshNet::Renci.SshNet.Security.Cryptography; +global using LocalSshNet::Renci.SshNet.Security.Cryptography.Ciphers; +global using LocalSshNet::Renci.SshNet.Security.Org; +global using LocalSshNet::Renci.SshNet.Security.Org.BouncyCastle; + +global using LocalSshNet::Renci.SshNet.Sftp; + + diff --git a/src/Renci.SshNet.IntegrationTests/app.config b/src/Renci.SshNet.IntegrationTests/app.config new file mode 100644 index 000000000..3571058ce --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/src/Renci.SshNet.IntegrationTests/server/script/start.sh b/src/Renci.SshNet.IntegrationTests/server/script/start.sh new file mode 100644 index 000000000..e7e9f758c --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/server/script/start.sh @@ -0,0 +1,10 @@ +#!/bin/ash +/usr/sbin/syslog-ng + +# allow us to make changes to /etc/hosts; we need this for the port forwarding tests +chmod 777 /etc/hosts + +# start PAM-enabled ssh daemon as we also want keyboard-interactive authentication to work +/usr/sbin/sshd.pam + +tail -f < /var/log/auth.log diff --git a/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ecdsa_key b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ecdsa_key new file mode 100644 index 000000000..5739844cf --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ecdsa_key @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTtDoci0CaEgyR+p+ersiYltKUSqZx/ +MffWpnEPfGgnFI81huQw0D9e/SqABbeHtrzcSWskSZc0f2jjFxyqVkliAAAAsDrEln06xJ +Z9AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBO0OhyLQJoSDJH6n +56uyJiW0pRKpnH8x99amcQ98aCcUjzWG5DDQP179KoAFt4e2vNxJayRJlzR/aOMXHKpWSW +IAAAAgYdRMomjDSquRMSYTvEIzX7cReJ2grVIWsxIOLyhJnw0AAAAWcm9vdEBVYnVudHUx +OTEwRGVza3RvcAEC +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ed25519_key b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ed25519_key new file mode 100644 index 000000000..8e45178e6 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ed25519_key @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACCCMW3VnuKfj6AxBQ7gJ4qAfEgw/YJl9q3AXyelCw+OdwAAAKDKcLC0ynCw +tAAAAAtzc2gtZWQyNTUxOQAAACCCMW3VnuKfj6AxBQ7gJ4qAfEgw/YJl9q3AXyelCw+Odw +AAAED9TRnDkG0tzdZv5oPJCXwzqrkxut7y33A8Wi8AzusJL4IxbdWe4p+PoDEFDuAnioB8 +SDD9gmX2rcBfJ6ULD453AAAAFnJvb3RAVWJ1bnR1MTkxMERlc2t0b3ABAgMEBQYH +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_rsa_key b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_rsa_key new file mode 100644 index 000000000..edb94f341 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_rsa_key @@ -0,0 +1,38 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +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 +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys b/src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys new file mode 100644 index 000000000..484a0bdcc --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys @@ -0,0 +1,4 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4N0T6VopnwPSYQUw0waQNhBjz0DYFQwvkv4OwWYSf//fJF3M6bH42Tn2J+IlQ+4/fCFnE3m99seV5T1myEj7fsupNteY2sKFGXENLGtAD/76FM0iBmXx76xlSTyZSSmNDIRU4xUR23cfc+al84F5mO2lEk+5Zr3Qn5JUpucBfis4vtu0sMDgZ4w1d0tcuXkT/MEJn2iX2cnxbSy5qNAPHu7b+LEfXBv2OrMDqPrx/X6QREgi3w5RxL5kz7bvitWsIwIvb3ST2ARAArBwb8pEyp2A/w5p22rkQtL+3ibZ8fkmpgn33f31AZPgtM++iJPBmPKFjArcWEJ9fIVB/6DAj +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPzzrPpItEjNG7tU0DpJJ4pkI01E9d6K61OKTVPdFQSyGCdMj9XdP93lC6sJA+9/ahvf5F3gWEKxUJL2CKUiFWw= +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLSsu/HNKiaALhQ26UDv+N0AFdMb26fMVrOKe866CGu6ajSf9HUOhJFdjhseihB2rTalMPr8MrcXNLufii4mL8u4l9fUQXFgwnM/ZpiVPSs6C+8i4u/ZDg7Nx2NXybNIgQ== +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACB4WgRgGBRo6Uk+cRgg8tJPCbEtGURRWlUA7PDDerXR+P9O6mm3L99Etxsyh5XNYqXyaMNtH5c51ooMajrFwcayAHIhPPb8X3CsTwEfIUQ96aDyHQMotbRfnkn6uefeUTRrSNcqeAndUtVyAqBdqbsq2mgJYXHrz2NUKlPFYgauQi+WQ== \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj index 88693e5aa..94a4e8015 100644 --- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj +++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj @@ -64,9 +64,9 @@ - - - + + + diff --git a/src/Renci.SshNet.sln b/src/Renci.SshNet.sln index 09953551d..be2686bee 100644 --- a/src/Renci.SshNet.sln +++ b/src/Renci.SshNet.sln @@ -42,6 +42,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D21A4D03-0 ..\test\Directory.Build.props = ..\test\Directory.Build.props EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "Renci.SshNet.IntegrationTests\IntegrationTests.csproj", "{EEF98046-729C-419E-932D-4E569073C8CC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -84,6 +86,26 @@ Global {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Mixed Platforms.Build.0 = Release|Any CPU {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|x64.ActiveCfg = Release|Any CPU {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|x86.ActiveCfg = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|ARM.ActiveCfg = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|ARM.Build.0 = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|x64.ActiveCfg = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|x64.Build.0 = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|x86.ActiveCfg = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|x86.Build.0 = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|Any CPU.Build.0 = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|ARM.ActiveCfg = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|ARM.Build.0 = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|x64.ActiveCfg = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|x64.Build.0 = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|x86.ActiveCfg = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs b/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs index 0425d1fba..d99338feb 100644 --- a/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs +++ b/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs @@ -5,13 +5,13 @@ [assembly: AssemblyDescription("SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism.")] [assembly: AssemblyCompany("Renci")] [assembly: AssemblyProduct("SSH.NET")] -[assembly: AssemblyCopyright("Copyright � Renci 2010-2017")] +[assembly: AssemblyCopyright("Copyright � Renci 2010-2023")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("2017.0.0")] -[assembly: AssemblyFileVersion("2017.0.0")] -[assembly: AssemblyInformationalVersion("2017.0.0-beta1")] +[assembly: AssemblyVersion("2023.0.0")] +[assembly: AssemblyFileVersion("2023.0.0")] +[assembly: AssemblyInformationalVersion("2023.0.0")] [assembly: CLSCompliant(false)] // Setting ComVisible to false makes the types in this assembly not visible @@ -24,4 +24,4 @@ [assembly: AssemblyConfiguration("Debug")] #else [assembly: AssemblyConfiguration("Release")] -#endif \ No newline at end of file +#endif