What's wrong
RelativeDirectoryPath.Normalize() (Semantics.Paths/Implementations/RelativeDirectoryPath.cs, around line 218) normalizes a relative path in three steps:
- Combine it with a dummy root (
/ or C:\).
- Call
Path.GetFullPath.
- Take
GetRelativePath back from the dummy root.
A .. at the filesystem root resolves to the root itself, so every .. that climbs above the starting point is silently discarded.
Failure scenario (reproduced)
| Input |
Normalize() returns |
Expected |
"../sibling" |
"sibling" |
"../sibling" |
"a/../../b" |
"b" |
"../b" |
"../../x/y" |
"x/y" |
"../../x/y" |
Take "../sibling" resolved from /home/user/project. Before normalizing it names /home/user/sibling. After normalizing it names /home/user/project/sibling. Normalization must never change which directory a path refers to. Here it can turn a path that points outside the base into one that points inside it, which affects file operations and makes any containment or sandbox check built on it unreliable.
The existing test Normalize_WithOnlyDots_ResolvesCorrectly (./../folder) only asserts that the result contains "folder", so it passes despite the bug.
Suggested fix
Resolve segments lexically with a stack:
- skip
.
- a
.. pops the previous segment when there is one that isn't itself ..
- otherwise keep the
..
Then join with the platform separator. This needs no dummy base and gives the same result on every OS.
Acceptance criteria
- The three inputs in the table above normalize to the expected column.
"a/./b/../c" → "a/c".
Normalize_WithOnlyDots_ResolvesCorrectly asserts the exact value ("../folder").
What's wrong
RelativeDirectoryPath.Normalize()(Semantics.Paths/Implementations/RelativeDirectoryPath.cs, around line 218) normalizes a relative path in three steps:/orC:\).Path.GetFullPath.GetRelativePathback from the dummy root.A
..at the filesystem root resolves to the root itself, so every..that climbs above the starting point is silently discarded.Failure scenario (reproduced)
Normalize()returns"../sibling""sibling""../sibling""a/../../b""b""../b""../../x/y""x/y""../../x/y"Take
"../sibling"resolved from/home/user/project. Before normalizing it names/home/user/sibling. After normalizing it names/home/user/project/sibling. Normalization must never change which directory a path refers to. Here it can turn a path that points outside the base into one that points inside it, which affects file operations and makes any containment or sandbox check built on it unreliable.The existing test
Normalize_WithOnlyDots_ResolvesCorrectly(./../folder) only asserts that the result contains "folder", so it passes despite the bug.Suggested fix
Resolve segments lexically with a stack:
...pops the previous segment when there is one that isn't itself....Then join with the platform separator. This needs no dummy base and gives the same result on every OS.
Acceptance criteria
"a/./b/../c"→"a/c".Normalize_WithOnlyDots_ResolvesCorrectlyasserts the exact value ("../folder").