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 4 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 - Fix `Get-NTFSOrphanedAccess`, which reported an item that it couldn't
read as an `AddAceError`; it now writes a `ReadSecurityError`, like read as an `AddAceError`; it now writes a `ReadSecurityError`, like
`Get-NTFSAccess` `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 [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 library, not through the PowerShell providers, so they don't know drives
that `New-PSDrive` created, or drives of other providers such as `HKLM:`. 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 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). [Long paths](Concepts.md#long-paths).

4
NTFSSecurity/BaseCmdlets.cs

@ -112,14 +112,14 @@ namespace NTFSSecurity
{ {
path = GetCurrentLocation(); path = GetCurrentLocation();
} }
else if (path.StartsWith("..")) else if (path == ".." || path.StartsWith("..\\"))
{ {
var currentLocation = GetCurrentLocation(); var currentLocation = GetCurrentLocation();
path = System.IO.Path.Combine( path = System.IO.Path.Combine(
string.Join("\\", currentLocation.Split('\\').Take(currentLocation.Split('\\').Count() - path.Split('\\').Count(s => s == "..")).ToArray()), string.Join("\\", currentLocation.Split('\\').Take(currentLocation.Split('\\').Count() - path.Split('\\').Count(s => s == "..")).ToArray()),
string.Join("\\", path.Split('\\').Where(e => e != "..").ToArray())); string.Join("\\", path.Split('\\').Where(e => e != "..").ToArray()));
} }
else if (path.StartsWith(".")) else if (path.StartsWith(".\\") || path.StartsWith("./"))
{ {
//combine . and .\path\subpath //combine . and .\path\subpath
path = System.IO.Path.Combine(GetCurrentLocation(), path.Substring(2)); 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' { 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 # 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). # cmdlets resolved against the current location (#88).

Loading…
Cancel
Save