From 18345f307d3da4695ed6bdc4d41b7d6d95a7fb8c Mon Sep 17 00:00:00 2001 From: Raimund Andree Date: Thu, 8 Oct 2026 16:02:09 +0000 Subject: [PATCH] test(access): cover parent folders in another case in Get-NTFSSimpleAccess From the security-reviewer pass over be04cb7..4ee01e5 (Minor 3 and 4): ea2f6df compares the paths of folders without regard to case, which no test covered; a parent folder in another case counted as not reported before, so the folder was left out. The test of a drive root now expects all of its entries instead of any. The page and the changelog say that paths that differ only in case name the same folder. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant --- CHANGELOG.md | 5 +++-- Docs/Cmdlets/Get-NTFSSimpleAccess.md | 4 ++-- NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml | 4 ++-- Tests/Access.Tests.ps1 | 17 +++++++++++++++-- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9de9dd3..d461077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -347,8 +347,9 @@ The format is based on - Fix `Get-NTFSSimpleAccess`, which left out a folder whose parent folder it hadn't reported, and with it all of its subfolders, and which compared a drive root with the parent folder of the folder before it; such folders - are now reported with all of their entries. A folder that came after its - parent folder a second time failed with a `ReadError` + are now reported with all of their entries, and a parent folder is found + also when its path differs in case. A folder that came after its parent + folder a second time failed with a `ReadError` - Fix `Move-Item2` for a folder on another volume, which Windows can't move: the cmdlet copied and deleted it instead, so that an empty folder was deleted without being created at the destination, and a folder with diff --git a/Docs/Cmdlets/Get-NTFSSimpleAccess.md b/Docs/Cmdlets/Get-NTFSSimpleAccess.md index b2e6925..57835b5 100644 --- a/Docs/Cmdlets/Get-NTFSSimpleAccess.md +++ b/Docs/Cmdlets/Get-NTFSSimpleAccess.md @@ -29,7 +29,7 @@ Get-NTFSSimpleAccess [-IncludeRootFolder] [-SecurityDescriptor] Reads the access control entries of folders and writes them as `Security2.SimpleFileSystemAccessRule` objects whose rights are reduced to the three values `Read`, `Write`, and `Delete`. Reading rights such as `ReadData`, which on a folder is the right to list it (`ListDirectory`), `ReadAttributes`, or `Traverse` become `Read`, changing rights such as `CreateFiles`, `WriteAttributes`, `ChangePermissions`, or `TakeOwnership` become `Write`, and `Delete` and `DeleteSubdirectoriesAndFiles` become `Delete`; `FullControl` becomes all three. The result answers who may read, change, or delete in a folder without the detail of the full ACL. - The second simplification is that repetitions are left out. The first folder the cmdlet processes is reported with all of its entries, and so is every folder whose parent folder the cmdlet didn't report before, such as a drive root. For a folder whose parent folder it reported, only the entries are reported that the parent folder does not already cover. An entry is covered when the parent has an entry for the same account and access type that includes at least the same simple rights. This makes a recursive listing show where permissions actually change instead of repeating the inherited ones on every level, and it requires the parent folder to be processed before its children, which `Get-ChildItem`, `Get-ChildItem2`, and `Get-Item2` do by default. + The second simplification is that repetitions are left out. The first folder the cmdlet processes is reported with all of its entries, and so is every folder whose parent folder the cmdlet didn't report before, such as a drive root; paths that differ only in case name the same folder. For a folder whose parent folder it reported, only the entries are reported that the parent folder does not already cover. An entry is covered when the parent has an entry for the same account and access type that includes at least the same simple rights. This makes a recursive listing show where permissions actually change instead of repeating the inherited ones on every level, and it requires the parent folder to be processed before its children, which `Get-ChildItem`, `Get-ChildItem2`, and `Get-Item2` do by default. `-IncludeRootFolder` is on by default and adds the parent folder of the first path as the baseline for the comparison, which is why the first result usually belongs to the folder above the one that was asked for. Use `-IncludeRootFolder:$false` to start the comparison at the first path itself. The cmdlet only processes folders; a path that points to a file is skipped silently, while the security descriptor of a file is reported. Relative paths are resolved against the current location, and the current location is used when `-Path` is omitted. `-ExcludeInherited`, `-ExcludeExplicit`, and `-Account` work as in `Get-NTFSAccess`. With `-SecurityDescriptor`, the cmdlet reports the entries of a `Security2.FileSystemSecurity2` object that `Get-NTFSSecurityDescriptor` returned, without comparing them with a parent folder. @@ -6630,7 +6630,7 @@ PS C:\Data> Get-NTFSSecurityDescriptor When the module setting `EnablePrivileges` is `$true` (the default in the `PrivateData` section of NTFSSecurity.psd1), this cmdlet tries to enable the Backup, Restore, Take Ownership, and Security privileges while it runs and disables the privileges it enabled when it finishes. These privileges are only available in an elevated session of an account that holds them, such as a member of the local Administrators group. If a privilege cannot be enabled, the cmdlet continues without it and writes a debug message. The simplified rights hide which exact rights an account holds. Use `Get-NTFSAccess` when you need the full access control entry, and `Get-NTFSEffectiveAccess` when you need the rights that result from all entries together. Before 5.0.0, the cmdlet ignored `-Account` and `-SecurityDescriptor`, and its output had no table view. It also showed no rights for an entry that grants only `ReadData`, which other tools than .NET create, and it left out the parent folder of a relative path with a single folder name, such as `Data`. - Before 5.0.0-rc7, the cmdlet left out a folder whose parent folder it hadn't reported, unless it was the first folder, and with it all of its subfolders. A drive root was left out as well, or compared with the parent folder of the folder before it, and a folder that came after its parent folder a second time failed with a `ReadError`. + Before 5.0.0-rc7, the cmdlet left out a folder whose parent folder it hadn't reported, unless it was the first folder, and with it all of its subfolders; a parent folder whose path differed in case counted as not reported. A drive root was left out as well, or compared with the parent folder of the folder before it, and a folder that came after its parent folder a second time failed with a `ReadError`. diff --git a/Tests/Access.Tests.ps1 b/Tests/Access.Tests.ps1 index f92bea4..0f7bf78 100644 --- a/Tests/Access.Tests.ps1 +++ b/Tests/Access.Tests.ps1 @@ -353,14 +353,27 @@ Describe 'Get-NTFSSimpleAccess' { } # Before 5.0.0-rc7, a drive root after the first path was left out as well, because it has no parent folder; - # after another folder whose parent was reported, it was compared with that unrelated parent. + # after another folder whose parent was reported, it was compared with that unrelated parent. The test reads the + # entries of the drive root and changes nothing there. It 'Should report all entries of a drive root, which has no parent folder' { $root = [IO.Path]::GetPathRoot($child) + $rootAlone = @(Get-NTFSSimpleAccess -Path $root -IncludeRootFolder:$false -ErrorAction Stop) + $rootAlone | Should -Not -BeNullOrEmpty $result = @(Get-NTFSSimpleAccess -Path $child, $root -IncludeRootFolder:$false -ErrorVariable simpleErrors -ErrorAction SilentlyContinue) $simpleErrors | Should -BeNullOrEmpty - @($result | Where-Object -Property FullName -EQ -Value $root) | Should -Not -BeNullOrEmpty + @($result | Where-Object -Property FullName -EQ -Value $root) | Should -HaveCount $rootAlone.Count + } + + # Windows doesn't distinguish paths by case. Before 5.0.0-rc7, the cmdlet didn't recognize the parent folder of a + # folder whose path differed from it in case, and left the folder out. + It 'Should compare a folder with its parent folder also when their paths differ in case' { + $result = @(Get-NTFSSimpleAccess -Path $parent, $child.ToUpperInvariant() -IncludeRootFolder:$false -ErrorAction Stop) + + $childEntries = @($result | Where-Object -Property FullName -EQ -Value $child) + $childEntries | Should -HaveCount 1 + $childEntries[0].Identity.Sid | Should -Be 'S-1-5-21-1-2-3-3101' } It 'Should report the parent folder of the first path first by default' {