Browse Source

fix: resolve relative paths that start with a dot

GetRelativePath treated every path that started with a dot as .\path and
dropped its first two characters, so a command on .gitignore read,
changed, or removed itignore in the same folder when that item existed;
Remove-Item2 -Path .RemoveMe removed emoveMe, and Copy-Item2 -Force with
the destination .CopyTarget overwrote opyTarget. Only .\ and ./ are now
stripped, and only .. and ..\ go up a folder; any other name is combined
with the current location. All path parameters resolve through this
method: -Path, -Destination, and -Target.

The coverage report of rc6 found the untested branch. The tests use a
decoy item with the shortened name and fail without the fix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
ai/release-5.0.0-rc6
Raimund Andree 3 days ago
parent
commit
0df248252a
  1. 4
      CHANGELOG.md
  2. 4
      Docs/FAQ.md
  3. 4
      NTFSSecurity/BaseCmdlets.cs
  4. 58
      Tests/Owner.Tests.ps1

4
CHANGELOG.md

@ -312,5 +312,9 @@ The format is based on
- Fix `Get-NTFSOrphanedAccess`, which reported an item that it couldn't
read as an `AddAceError`; it now writes a `ReadSecurityError`, like
`Get-NTFSAccess`
- Fix every cmdlet for a relative path that starts with a dot but not with
`.\`, such as `.gitignore`: the cmdlets dropped its first two characters
and read, changed, or removed the item with the shorter name, such as
`itignore`, when one existed
[Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD

4
Docs/FAQ.md

@ -71,5 +71,7 @@ No. The cmdlets read and write the file system directly through the AlphaFS
library, not through the PowerShell providers, so they don't know drives
that `New-PSDrive` created, or drives of other providers such as `HKLM:`.
Use the file system path instead, such as `C:\Data` or `\\server\share`. A
relative path is resolved against the current file system location. See
relative path is resolved against the current file system location, also a
name that starts with a dot, such as `.gitignore`; before 5.0.0-rc6, the
cmdlets dropped the first two characters of such a name. See
[Long paths](Concepts.md#long-paths).

4
NTFSSecurity/BaseCmdlets.cs

@ -112,14 +112,14 @@ namespace NTFSSecurity
{
path = GetCurrentLocation();
}
else if (path.StartsWith(".."))
else if (path == ".." || path.StartsWith("..\\"))
{
var currentLocation = GetCurrentLocation();
path = System.IO.Path.Combine(
string.Join("\\", currentLocation.Split('\\').Take(currentLocation.Split('\\').Count() - path.Split('\\').Count(s => s == "..")).ToArray()),
string.Join("\\", path.Split('\\').Where(e => e != "..").ToArray()));
}
else if (path.StartsWith("."))
else if (path.StartsWith(".\\") || path.StartsWith("./"))
{
//combine . and .\path\subpath
path = System.IO.Path.Combine(GetCurrentLocation(), path.Substring(2));

58
Tests/Owner.Tests.ps1

@ -106,6 +106,64 @@ Describe 'Current location' {
}
}
# Before 5.0.0-rc6, a relative path that started with a dot, but not with .\, lost its first two characters: a command
# on .Dotfile read or changed the item otfile in the same folder when one existed.
Describe 'Relative paths that start with a dot' {
BeforeAll {
$dotFolder = New-TestSandboxItem -Sandbox $sandbox -Name 'Dot' -Directory
Push-Location -LiteralPath $dotFolder
# The names that the defect made of the paths
foreach ($name in '.Dotfile', '..Dotfile', 'otfile', 'Dotfile') {
Assert-TestSandboxPath -Sandbox $sandbox -Path $name
Set-Content -LiteralPath $name -Value $name
}
}
AfterAll {
Pop-Location
}
It 'Should resolve <Path> to the item <Expected> of the current location' -ForEach @(
@{ Path = '.Dotfile'; Expected = '.Dotfile' }
@{ Path = '..Dotfile'; Expected = '..Dotfile' }
@{ Path = '.\.Dotfile'; Expected = '.Dotfile' }
@{ Path = './.Dotfile'; Expected = '.Dotfile' }
@{ Path = '..\{0}\.Dotfile'; Expected = '.Dotfile' }
) {
# {0} is the name of the current folder.
$relative = $Path -f (Split-Path -Path $dotFolder -Leaf)
$result = Get-NTFSOwner -Path $relative -ErrorAction Stop
$result.FullName | Should -Be (Join-Path -Path $dotFolder -ChildPath $Expected)
}
It 'Remove-Item2 should remove the item that the path names, not another item' {
foreach ($name in '.RemoveMe', 'emoveMe') {
Assert-TestSandboxPath -Sandbox $sandbox -Path $name
Set-Content -LiteralPath $name -Value $name
}
Remove-Item2 -Path '.RemoveMe' -ErrorAction Stop
Join-Path -Path $dotFolder -ChildPath '.RemoveMe' | Should -Not -Exist
Join-Path -Path $dotFolder -ChildPath 'emoveMe' | Should -Exist
}
It 'Copy-Item2 should copy to the destination that the path names, not over another item' {
foreach ($name in 'CopySource', 'opyTarget') {
Assert-TestSandboxPath -Sandbox $sandbox -Path $name
Set-Content -LiteralPath $name -Value $name
}
Assert-TestSandboxPath -Sandbox $sandbox -Path '.CopyTarget'
Copy-Item2 -Path 'CopySource' -Destination '.CopyTarget' -Force -ErrorAction Stop
Get-Content -LiteralPath (Join-Path -Path $dotFolder -ChildPath '.CopyTarget') | Should -Be 'CopySource'
Get-Content -LiteralPath (Join-Path -Path $dotFolder -ChildPath 'opyTarget') | Should -Be 'opyTarget'
}
}
Describe 'File and folder objects as arguments' {
# Before 5.0.0, Windows PowerShell bound a folder object that was passed by position as its name, which the
# cmdlets resolved against the current location (#88).

Loading…
Cancel
Save