Files
SSH.NET/test/Renci.SshNet.Tests/Classes/ScpClientTest_ResolveChild_RootDirectory.cs
Rob Hague 7d54d88b65 Harden directory escape in SCP recursive download
An E record received before any D record made the directory depth
counter negative and moved the current directory to the parent of the
destination, so that a following C record wrote outside it; each further
E record climbed another level. On Windows, a D record named "...", ".. "
or " .." (legal names on a Unix server) normalizes to the current
directory, so a D and E pair climbed one level with a balanced counter.

An unmatched E record is now rejected before it is acknowledged. Every C
and D record name goes through ResolveChild, which requires the full path
to be a direct child of the current directory with exactly the name that
was sent. This also rejects Windows device names such as "CON", and names
that Windows would silently change, such as a trailing dot, unless the
destination uses the \\?\ prefix. Rejected names are reported to the
server and thrown as ScpException. ResolveChild replaces
EnsureValidLocalName.
2026-10-14 09:40:28 +02:00

36 lines
1.2 KiB
C#

using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Renci.SshNet.Tests.Classes
{
/// <summary>
/// Tests for <see cref="ScpClient.ResolveChild(string, string)"/> and
/// <see cref="ScpClient.NormalizeDirectory(string)"/> with a root directory as the destination,
/// which a download test cannot use. Other cases are covered by
/// <see cref="ScpClientTest_Download_PathAndDirectoryInfo_Names"/>.
/// </summary>
[TestClass]
public class ScpClientTest_ResolveChild_RootDirectory
{
[TestMethod]
public void RootDirectory_ShouldReturnDirectChild()
{
var root = ScpClient.NormalizeDirectory(Path.GetPathRoot(Path.GetTempPath()));
Assert.AreEqual(Path.Combine(root, "file.txt"), ScpClient.ResolveChild(root, "file.txt"));
}
[TestMethod]
[DataRow(".")]
[DataRow("..")]
public void RootDirectory_DotOrDotDot_ShouldBeRejected(string name)
{
// "." and ".." resolve to the root itself, which has no parent directory.
var root = ScpClient.NormalizeDirectory(Path.GetPathRoot(Path.GetTempPath()));
Assert.IsNull(ScpClient.ResolveChild(root, name));
}
}
}