diff --git a/CHANGELOG.md b/CHANGELOG.md index f69ca96..2934782 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,5 +62,7 @@ The format is based on - Fix `Get-NTFSAudit`, which returned nothing without the Security privilege instead of an error, and which returned the entries of the previous item again after a path whose security descriptor it couldn't read +- Fix `Get-NTFSAccess`, which returned the entries of the previous item again + after a path whose ACL it couldn't read [Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD diff --git a/Docs/Cmdlets/Get-NTFSAccess.md b/Docs/Cmdlets/Get-NTFSAccess.md index 8036d44..b765835 100644 --- a/Docs/Cmdlets/Get-NTFSAccess.md +++ b/Docs/Cmdlets/Get-NTFSAccess.md @@ -184,6 +184,8 @@ If the ACL of an item cannot be read because access is denied, the cmdlet tries Entries whose account cannot be translated into a name are returned with their SID. Use `Get-NTFSOrphanedAccess` to list only those entries. +Before 5.0.0, after a path whose ACL could not be read, the cmdlet returned the entries of the previous item again. + ## RELATED LINKS [Add-NTFSAccess](Add-NTFSAccess.md) diff --git a/NTFSSecurity/AccessCmdlets/GetAccess.cs b/NTFSSecurity/AccessCmdlets/GetAccess.cs index 7013487..6b9339a 100644 --- a/NTFSSecurity/AccessCmdlets/GetAccess.cs +++ b/NTFSSecurity/AccessCmdlets/GetAccess.cs @@ -79,13 +79,13 @@ namespace NTFSSecurity protected override void ProcessRecord() { - IEnumerable acl = null; - FileSystemInfo item = null; - if (ParameterSetName == "Path") { foreach (var path in paths) { + FileSystemInfo item = null; + IEnumerable acl = null; + try { item = GetFileSystemInfo2(path); @@ -122,34 +122,27 @@ namespace NTFSSecurity WriteError(new ErrorRecord(ex, "ReadSecurityError", ErrorCategory.OpenError, path)); continue; } - finally - { - if (acl != null) - { - if (account != null) - { - acl = acl.Where(ace => ace.Account == account); - } - acl.ForEach(ace => WriteObject(ace)); - } - } + WriteAccessRules(acl); } } else { foreach (var sd in securityDescriptors) { - acl = FileSystemAccessRule2.GetFileSystemAccessRules(sd, !excludeExplicit, !excludeInherited, getInheritedFrom); - - if (account != null) - { - acl = acl.Where(ace => ace.Account == account); - } - - acl.ForEach(ace => WriteObject(ace)); + WriteAccessRules(FileSystemAccessRule2.GetFileSystemAccessRules(sd, !excludeExplicit, !excludeInherited, getInheritedFrom)); } } } + + private void WriteAccessRules(IEnumerable acl) + { + if (account != null) + { + acl = acl.Where(ace => ace.Account == account); + } + + acl.ForEach(ace => WriteObject(ace)); + } } -} +} \ No newline at end of file diff --git a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml index c93e5ab..b9dff19 100644 --- a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml +++ b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml @@ -4560,6 +4560,7 @@ PS C:\> Disable-Privileges 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. If the ACL of an item cannot be read because access is denied, the cmdlet tries once more after making the current account the owner of the item, and restores the previous owner afterwards. Changing the owner of an item requires the Take Ownership and Restore privileges, so this fallback only succeeds in an elevated session of an account that holds them. Entries whose account cannot be translated into a name are returned with their SID. Use `Get-NTFSOrphanedAccess` to list only those entries. + Before 5.0.0, after a path whose ACL could not be read, the cmdlet returned the entries of the previous item again. diff --git a/Tests/Access.Tests.ps1 b/Tests/Access.Tests.ps1 new file mode 100644 index 0000000..51f60e6 --- /dev/null +++ b/Tests/Access.Tests.ps1 @@ -0,0 +1,37 @@ +<# + Tests the access cmdlets of the module built in NTFSSecurity\bin\Release on files in a sandbox folder. +#> +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Pester shares variables between blocks.' +)] +param () + +BeforeAll { + Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force + $modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\NTFSSecurity\bin\Release\NTFSSecurity.psd1' + Import-Module -Name $modulePath -Force -ErrorAction Stop + $sandbox = New-TestSandbox -Name 'Access' + Push-Location -LiteralPath $sandbox +} + +AfterAll { + Pop-Location + Remove-TestSandbox -Sandbox $sandbox + Remove-Module -Name NTFSSecurity -Force -ErrorAction SilentlyContinue +} + +Describe 'Get-NTFSAccess' { + Context 'When a path fails after a readable path' { + # Before 5.0.0, the cmdlet wrote the entries of the previous item again for the failing path. + It 'Should return the entries of the first item once' { + $folder = New-TestSandboxItem -Sandbox $sandbox -Name 'Readable' -Directory + $denied = New-TestSandboxItem -Sandbox $sandbox -Name 'Denied' + Block-TestReadPermission -Sandbox $sandbox -Path $denied + $expected = @(Get-NTFSAccess -Path $folder).Count + + $entries = @(Get-NTFSAccess -Path $folder, $denied -ErrorAction SilentlyContinue) + + @($entries | Where-Object -Property FullName -EQ -Value $folder) | Should -HaveCount $expected + } + } +}