From 976eb87aa478797e1c62c7812202323d2492bb8a Mon Sep 17 00:00:00 2001 From: sinthu-14 Date: Fri, 25 Sep 2026 13:31:24 +0200 Subject: [PATCH 1/2] Fixes constructing invalid absolute paths. --- TruePath.Tests/AbsolutePathTests.cs | 111 ++++++++++++++++++++++++++-- TruePath/AbsolutePath.cs | 10 +++ 2 files changed, 116 insertions(+), 5 deletions(-) diff --git a/TruePath.Tests/AbsolutePathTests.cs b/TruePath.Tests/AbsolutePathTests.cs index fa1cea3..1369f40 100644 --- a/TruePath.Tests/AbsolutePathTests.cs +++ b/TruePath.Tests/AbsolutePathTests.cs @@ -19,18 +19,18 @@ public void ConstructionTest() [Fact] public void PathRootReturnsRoot() { - var root = new AbsolutePath(OperatingSystem.IsWindows() ? @"A:\" : "/"); - var path = root / "foo" / "bar"; + var root = new AbsolutePath(OperatingSystem.IsWindows() ? @"A:\" : "/"); + var path = root / "foo" / "bar"; - Assert.Equal(root, path.PathRoot()); + Assert.Equal(root, path.PathRoot()); } [Fact] public void PathRootOfRootReturnsItself() { - var root = new AbsolutePath(OperatingSystem.IsWindows() ? @"A:\" : "/"); + var root = new AbsolutePath(OperatingSystem.IsWindows() ? @"A:\" : "/"); - Assert.Equal(root, root.PathRoot()); + Assert.Equal(root, root.PathRoot()); } [Fact] @@ -261,6 +261,107 @@ public void PathIsNormalizedOnCreation() Assert.Equal(@"C:\Users\John Doe\Documents", absolutePath.Value); } + [Theory] + [InlineData(@"/.")] + [InlineData(@"/./.")] + [InlineData(@"/././.")] + public void ConstructorCreatesValidPathWithDotInUnix(string path) + { + if (OperatingSystem.IsWindows()) return; + const string expectedPath = @"/"; + + var absolutePath = new AbsolutePath(path); + + Assert.Equal(expectedPath, absolutePath.Value); + } + + + [Theory] + [InlineData(@"/...")] + [InlineData(@"/..SomeFolder")] + [InlineData(@"/..00")] + [InlineData(@"/..#")] + public void ConstructorCreatesValidPathCorrectlyInUnix(string path) + { + if (OperatingSystem.IsWindows()) return; + + var absolutePath = new AbsolutePath(path); + + Assert.Equal(path, absolutePath.Value); + } + + [Theory] + [InlineData(@"/../")] + [InlineData(@"/../..")] + [InlineData(@"/../../SomeFolder")] + [InlineData(@"/../.SomeFolder")] + [InlineData(@"/../SomeFolder")] + [InlineData(@"/../1123")] + [InlineData(@"/../()")] + [InlineData(@"/./..")] + [InlineData(@"/./../.")] + + public void ConstructorThrowsOnInvalidPathInUnix(string path) + { + if (OperatingSystem.IsWindows()) return; + string expectedMessage = $"Path \"{path}\" is not valid."; + + var ex = Assert.Throws(() => new AbsolutePath(path)); + + Assert.Equal(expectedMessage, ex.Message); + } + + [Theory] + [InlineData(@"C:\.")] + [InlineData(@"C:\.\.")] + [InlineData(@"C:\.\.\.")] + public void ConstructorCreatesValidPathWithDotInWindows(string path) + { + if (OperatingSystem.IsWindows() is false) return; + const string expectedPath = @"C:\"; + + var absolutePath = new AbsolutePath(path); + + Assert.Equal(expectedPath, absolutePath.Value); + } + + + [Theory] + [InlineData(@"C:\...")] + [InlineData(@"C:\..SomeFolder")] + [InlineData(@"C:\..00")] + [InlineData(@"C:\..#")] + + public void ConstructorCreatesValidPathCorrectlyInWindows(string path) + { + if (OperatingSystem.IsWindows() is false) return; + + var absolutePath = new AbsolutePath(path); + + Assert.Equal(path, absolutePath.Value); + } + + [Theory] + [InlineData(@"C:\..\")] + [InlineData(@"C:\..\..")] + [InlineData(@"C:\..\..SomeFolder")] + [InlineData(@"C:\..\.SomeFolder")] + [InlineData(@"C:\..\SomeFolder")] + [InlineData(@"C:\..\1123")] + [InlineData(@"C:\..\()")] + [InlineData(@"C:\.\..")] + [InlineData(@"C:\.\..\.")] + + public void ConstructorThrowsOnInvalidPathInWindows(string path) + { + if (OperatingSystem.IsWindows() is false) return; + string expectedMessage = $"Path \"{path}\" is not valid."; + + var ex = Assert.Throws(() => new AbsolutePath(path)); + + Assert.Equal(expectedMessage, ex.Message); + } + [Fact] public void ConstructorThrowsOnNonRootedPath() { diff --git a/TruePath/AbsolutePath.cs b/TruePath/AbsolutePath.cs index 873df39..8e24335 100644 --- a/TruePath/AbsolutePath.cs +++ b/TruePath/AbsolutePath.cs @@ -46,12 +46,22 @@ namespace TruePath; /// Path string to normalize. /// Flag indicating whether absoluteness of path should be checked /// Thrown if the passed string does not represent an absolute path.> + /// Thrown if the passed string dot-dot directory directly after root. E.g. C:/../ or /../> + private AbsolutePath(string value, bool checkAbsoluteness) { Underlying = new LocalPath(value); if (checkAbsoluteness && Underlying.IsAbsolute is false) throw new ArgumentException($"Path \"{value}\" is not absolute."); + + var pathRoot = Path.GetPathRoot(Underlying.Value)!; + var pathWithoutRoot = Underlying.Value[pathRoot.Length..]; + if (pathWithoutRoot.StartsWith("../") || pathWithoutRoot.StartsWith("..\\") || + pathWithoutRoot.Equals("..")) + { + throw new ArgumentException($"Path \"{value}\" is not valid."); + } } /// From 2201c55dc98a1587d5725a4045d1c81821d01b17 Mon Sep 17 00:00:00 2001 From: sinthu-14 Date: Fri, 25 Sep 2026 14:11:13 +0200 Subject: [PATCH 2/2] Fix formatting and spelling issues. --- TruePath.Tests/AbsolutePathTests.cs | 5 ----- TruePath/AbsolutePath.cs | 4 +++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/TruePath.Tests/AbsolutePathTests.cs b/TruePath.Tests/AbsolutePathTests.cs index 1369f40..0f989cb 100644 --- a/TruePath.Tests/AbsolutePathTests.cs +++ b/TruePath.Tests/AbsolutePathTests.cs @@ -275,7 +275,6 @@ public void ConstructorCreatesValidPathWithDotInUnix(string path) Assert.Equal(expectedPath, absolutePath.Value); } - [Theory] [InlineData(@"/...")] [InlineData(@"/..SomeFolder")] @@ -300,7 +299,6 @@ public void ConstructorCreatesValidPathCorrectlyInUnix(string path) [InlineData(@"/../()")] [InlineData(@"/./..")] [InlineData(@"/./../.")] - public void ConstructorThrowsOnInvalidPathInUnix(string path) { if (OperatingSystem.IsWindows()) return; @@ -325,13 +323,11 @@ public void ConstructorCreatesValidPathWithDotInWindows(string path) Assert.Equal(expectedPath, absolutePath.Value); } - [Theory] [InlineData(@"C:\...")] [InlineData(@"C:\..SomeFolder")] [InlineData(@"C:\..00")] [InlineData(@"C:\..#")] - public void ConstructorCreatesValidPathCorrectlyInWindows(string path) { if (OperatingSystem.IsWindows() is false) return; @@ -351,7 +347,6 @@ public void ConstructorCreatesValidPathCorrectlyInWindows(string path) [InlineData(@"C:\..\()")] [InlineData(@"C:\.\..")] [InlineData(@"C:\.\..\.")] - public void ConstructorThrowsOnInvalidPathInWindows(string path) { if (OperatingSystem.IsWindows() is false) return; diff --git a/TruePath/AbsolutePath.cs b/TruePath/AbsolutePath.cs index 8e24335..a5872d6 100644 --- a/TruePath/AbsolutePath.cs +++ b/TruePath/AbsolutePath.cs @@ -46,7 +46,9 @@ namespace TruePath; /// Path string to normalize. /// Flag indicating whether absoluteness of path should be checked /// Thrown if the passed string does not represent an absolute path.> - /// Thrown if the passed string dot-dot directory directly after root. E.g. C:/../ or /../> + /// + /// Thrown if the passed string matches the dot-dot directory directly after root (e.g. C:/../ or /../) + /// > private AbsolutePath(string value, bool checkAbsoluteness) {