namespace DodoSSH.Client.Transfer.Tests; /// /// LocalDirectory.ChildPath: where a name another machine chose may land on this one. /// /// /// The assertion that matters is the same in every row — the result's parent is the directory asked about, /// or there is no result — because that is the property a download relies on, and it is checked against the /// resolved path rather than the string so that what the file system does to a name counts too. /// public sealed class LocalDirectoryTests { private static readonly string Directory = Path.Combine(Path.GetTempPath(), "dodossh-child-path"); [Theory] [InlineData("notes.txt")] [InlineData("backup:2026-10-02")] [InlineData(@"..\..\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\x.bat")] [InlineData(@"C:\Windows\evil.dll")] [InlineData("../../escaped.txt")] [InlineData("/etc/passwd")] [InlineData("...")] [InlineData(".hidden")] public void ANameThatCanBeAFileHere_LandsDirectlyInsideTheDirectory(string name) { var child = LocalDirectory.ChildPath(Directory, name); if (child is null) { // Only acceptable for "...", which Windows reduces to nothing; everywhere else it is a name. OperatingSystem.IsWindows().ShouldBeTrue($"'{name}' was refused"); name.ShouldBe("..."); return; } Path.GetDirectoryName(child).ShouldBe(Path.GetFullPath(Directory)); } [Theory] [InlineData("")] [InlineData(" ")] [InlineData(".")] [InlineData("..")] public void ANameThatIsOnlyHereOrAbove_IsRefused(string name) => LocalDirectory.ChildPath(Directory, name).ShouldBeNull(); /// /// Windows drops a trailing dot or space from a path component, so .. is .. by the time /// it reaches the disk — which a check of the string alone would have let through. /// [Fact] public void ATrailingSpaceOrDot_DoesNotSmuggleAParentReferencePastTheCheck() { foreach (var name in new[] { ".. ", "...", ".. .", ". " }) { var child = LocalDirectory.ChildPath(Directory, name); if (child is not null) { Path.GetDirectoryName(child).ShouldBe(Path.GetFullPath(Directory), name); } } } [Fact] public void AnOrdinaryName_IsKeptAsItIs() => Path.GetFileName(LocalDirectory.ChildPath(Directory, "nginx.conf")).ShouldBe("nginx.conf"); }