Browse Source

fix: don't repeat the previous item's entries in Get-NTFSAccess

Found while fixing defect 4: Get-NTFSAccess has the same pattern as
Get-NTFSAudit. It kept the entries of the previous item and wrote them in
a finally block, so a path whose ACL failed to read returned the previous
item's entries again, next to the error. Each item now starts empty, and
entries are written only after a successful read.

Tests/Access.Tests.ps1 (new): 1 test; it failed before the fix with 6
entries instead of 3.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
pull/100/head
Raimund Andree 1 week ago
parent
commit
3a13e49d42
  1. 2
      CHANGELOG.md
  2. 2
      Docs/Cmdlets/Get-NTFSAccess.md
  3. 39
      NTFSSecurity/AccessCmdlets/GetAccess.cs
  4. 1
      NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml
  5. 37
      Tests/Access.Tests.ps1

2
CHANGELOG.md

@ -62,5 +62,7 @@ The format is based on
- Fix `Get-NTFSAudit`, which returned nothing without the Security privilege - Fix `Get-NTFSAudit`, which returned nothing without the Security privilege
instead of an error, and which returned the entries of the previous item instead of an error, and which returned the entries of the previous item
again after a path whose security descriptor it couldn't read 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 [Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD

2
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. 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 ## RELATED LINKS
[Add-NTFSAccess](Add-NTFSAccess.md) [Add-NTFSAccess](Add-NTFSAccess.md)

39
NTFSSecurity/AccessCmdlets/GetAccess.cs

@ -79,13 +79,13 @@ namespace NTFSSecurity
protected override void ProcessRecord() protected override void ProcessRecord()
{ {
IEnumerable<FileSystemAccessRule2> acl = null;
FileSystemInfo item = null;
if (ParameterSetName == "Path") if (ParameterSetName == "Path")
{ {
foreach (var path in paths) foreach (var path in paths)
{ {
FileSystemInfo item = null;
IEnumerable<FileSystemAccessRule2> acl = null;
try try
{ {
item = GetFileSystemInfo2(path); item = GetFileSystemInfo2(path);
@ -122,34 +122,27 @@ namespace NTFSSecurity
WriteError(new ErrorRecord(ex, "ReadSecurityError", ErrorCategory.OpenError, path)); WriteError(new ErrorRecord(ex, "ReadSecurityError", ErrorCategory.OpenError, path));
continue; continue;
} }
finally
{
if (acl != null)
{
if (account != null)
{
acl = acl.Where(ace => ace.Account == account);
}
acl.ForEach(ace => WriteObject(ace)); WriteAccessRules(acl);
}
}
} }
} }
else else
{ {
foreach (var sd in securityDescriptors) foreach (var sd in securityDescriptors)
{ {
acl = FileSystemAccessRule2.GetFileSystemAccessRules(sd, !excludeExplicit, !excludeInherited, getInheritedFrom); WriteAccessRules(FileSystemAccessRule2.GetFileSystemAccessRules(sd, !excludeExplicit, !excludeInherited, getInheritedFrom));
if (account != null)
{
acl = acl.Where(ace => ace.Account == account);
}
acl.ForEach(ace => WriteObject(ace));
} }
} }
} }
private void WriteAccessRules(IEnumerable<FileSystemAccessRule2> acl)
{
if (account != null)
{
acl = acl.Where(ace => ace.Account == account);
}
acl.ForEach(ace => WriteObject(ace));
}
} }
} }

1
NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml

@ -4560,6 +4560,7 @@ PS C:\&gt; Disable-Privileges</dev:code>
<maml:para>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.</maml:para> <maml:para>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.</maml:para>
<maml:para>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.</maml:para> <maml:para>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.</maml:para>
<maml:para>Entries whose account cannot be translated into a name are returned with their SID. Use `Get-NTFSOrphanedAccess` to list only those entries.</maml:para> <maml:para>Entries whose account cannot be translated into a name are returned with their SID. Use `Get-NTFSOrphanedAccess` to list only those entries.</maml:para>
<maml:para>Before 5.0.0, after a path whose ACL could not be read, the cmdlet returned the entries of the previous item again.</maml:para>
</maml:alert> </maml:alert>
</maml:alertSet> </maml:alertSet>
<command:examples> <command:examples>

37
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
}
}
}
Loading…
Cancel
Save