From 73a0a7eae619fc7e1bedbbf49f051d5f8f70e525 Mon Sep 17 00:00:00 2001 From: Raimund Andree Date: Fri, 9 Oct 2026 13:38:35 +0000 Subject: [PATCH] test: exercise remaining descriptor and comparison outputs Cover descriptor PassThru wiring, audit clearing with inheritance protection, generic simplified access masks and unequal value branches. Keep source-backed untestable paths distinct from reachable output behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant --- Tests/Audit.Tests.ps1 | 25 +++++++++++++++ Tests/Inheritance.Tests.ps1 | 46 ++++++++++++++++++++++++++++ Tests/ObjectApis.Tests.ps1 | 61 +++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) diff --git a/Tests/Audit.Tests.ps1 b/Tests/Audit.Tests.ps1 index e521adf..57eb3b3 100644 --- a/Tests/Audit.Tests.ps1 +++ b/Tests/Audit.Tests.ps1 @@ -613,3 +613,28 @@ Describe 'Audit changes with the Security privilege disabled' { } } } +Describe 'Clear-NTFSAudit descriptor inheritance' { + It 'Should clear and protect the descriptor SACL without writing the ' -Skip:(-not $canReadAudit) -ForEach @( + @{ Type = 'file' } + @{ Type = 'folder' } + ) { + $path = New-TestSandboxItem -Sandbox $sandbox -Name 'ClearAuditDescriptor' -Directory:($Type -eq 'folder') + Assert-TestSandboxPath -Sandbox $sandbox -Path $path + Add-NTFSAudit -Path $path -Account 'S-1-1-0' -AccessRights Delete -AuditFlags Success -AppliesTo ThisFolderOnly + $before = Get-NTFSSecurityDescriptor -Path $path + $auditBefore = $before.SecurityDescriptor.GetSecurityDescriptorSddlForm('Audit') + $daclBefore = (Get-Acl -LiteralPath $path).Sddl + $sd = Get-NTFSSecurityDescriptor -Path $path + + Clear-NTFSAudit -SecurityDescriptor $sd -DisableInheritance -ErrorAction Stop + + $sd.SecurityDescriptor.AreAuditRulesProtected | Should -BeTrue + @($sd.SecurityDescriptor.GetAuditRules($true, $true, $sidType)) | Should -BeNullOrEmpty + (Get-NTFSSecurityDescriptor -Path $path).SecurityDescriptor.GetSecurityDescriptorSddlForm('Audit') | + Should -BeExactly $auditBefore + Set-NTFSSecurityDescriptor -SecurityDescriptor $sd -ErrorAction Stop + (Get-NTFSInheritance -Path $path).AuditInheritanceEnabled | Should -BeFalse + @(Get-NTFSAudit -Path $path) | Should -BeNullOrEmpty + (Get-Acl -LiteralPath $path).Sddl | Should -BeExactly $daclBefore + } +} diff --git a/Tests/Inheritance.Tests.ps1 b/Tests/Inheritance.Tests.ps1 index bf473cc..e50746d 100644 --- a/Tests/Inheritance.Tests.ps1 +++ b/Tests/Inheritance.Tests.ps1 @@ -508,3 +508,49 @@ Describe 'Set-NTFSInheritance with an in-memory descriptor' { (Get-Acl -LiteralPath $path).Sddl | Should -BeExactly $before } } +Describe 'Dedicated inheritance descriptor output' { + It ' should return the changed descriptor state without writing the ' -ForEach @( + @{ Command = 'Enable-NTFSAccessInheritance'; Type = 'file'; Enable = $true } + @{ Command = 'Enable-NTFSAccessInheritance'; Type = 'folder'; Enable = $true } + @{ Command = 'Disable-NTFSAccessInheritance'; Type = 'file'; Enable = $false } + @{ Command = 'Disable-NTFSAccessInheritance'; Type = 'folder'; Enable = $false } + ) { + $path = New-TestSandboxItem -Sandbox $sandbox -Name 'DedicatedAccessDescriptor' -Directory:($Type -eq 'folder') + Assert-TestSandboxPath -Sandbox $sandbox -Path $path + Set-NTFSInheritance -Path $path -AccessInheritanceEnabled (-not $Enable) -ErrorAction Stop + $before = (Get-Acl -LiteralPath $path).Sddl + $sd = Get-NTFSSecurityDescriptor -Path $path + + $result = @(& $Command -SecurityDescriptor $sd -PassThru -ErrorAction Stop) + + $result | Should -HaveCount 1 + $result[0].FullName | Should -BeExactly $path + $result[0].AccessInheritanceEnabled | Should -Be $Enable + (Get-Acl -LiteralPath $path).Sddl | Should -BeExactly $before + Set-NTFSSecurityDescriptor -SecurityDescriptor $sd -ErrorAction Stop + (Get-Acl -LiteralPath $path).AreAccessRulesProtected | Should -Be (-not $Enable) + } + + It ' should return the changed audit state without writing the ' -Skip:(-not $canChangeAudit) -ForEach @( + @{ Command = 'Enable-NTFSAuditInheritance'; Type = 'file'; Enable = $true } + @{ Command = 'Enable-NTFSAuditInheritance'; Type = 'folder'; Enable = $true } + @{ Command = 'Disable-NTFSAuditInheritance'; Type = 'file'; Enable = $false } + @{ Command = 'Disable-NTFSAuditInheritance'; Type = 'folder'; Enable = $false } + ) { + $path = New-TestSandboxItem -Sandbox $sandbox -Name 'DedicatedAuditDescriptor' -Directory:($Type -eq 'folder') + Assert-TestSandboxPath -Sandbox $sandbox -Path $path + Set-NTFSInheritance -Path $path -AuditInheritanceEnabled (-not $Enable) -ErrorAction Stop + $before = (Get-NTFSSecurityDescriptor -Path $path).SecurityDescriptor.GetSecurityDescriptorSddlForm('Audit') + $sd = Get-NTFSSecurityDescriptor -Path $path + + $result = @(& $Command -SecurityDescriptor $sd -PassThru -ErrorAction Stop) + + $result | Should -HaveCount 1 + $result[0].FullName | Should -BeExactly $path + $result[0].AuditInheritanceEnabled | Should -Be $Enable + (Get-NTFSSecurityDescriptor -Path $path).SecurityDescriptor.GetSecurityDescriptorSddlForm('Audit') | + Should -BeExactly $before + Set-NTFSSecurityDescriptor -SecurityDescriptor $sd -ErrorAction Stop + (Get-NTFSInheritance -Path $path).AuditInheritanceEnabled | Should -Be $Enable + } +} diff --git a/Tests/ObjectApis.Tests.ps1 b/Tests/ObjectApis.Tests.ps1 index d5ea1dc..bf508ca 100644 --- a/Tests/ObjectApis.Tests.ps1 +++ b/Tests/ObjectApis.Tests.ps1 @@ -261,3 +261,64 @@ Describe 'Legacy effective-permission output objects' { } } } +Describe 'Simplified entry comparison branches' { + It 'Should distinguish identities, rights and types in entries and keep equal hashes consistent' -ForEach @( + @{ Kind = 'access' } + @{ Kind = 'audit' } + ) { + $typeName = if ($Kind -eq 'access') { 'Security2.SimpleFileSystemAccessRule' } else { 'Security2.SimpleFileSystemAuditRule' } + $arguments = @($objectPath, $identity, [Security2.FileSystemRights2]::Read) + if ($Kind -eq 'access') { $arguments += [System.Security.AccessControl.AccessControlType]::Allow } + $first = New-Object -TypeName $typeName -ArgumentList $arguments + $equal = New-Object -TypeName $typeName -ArgumentList $arguments + $arguments[1] = [Security2.IdentityReference2] 'S-1-5-32-546' + $differentIdentity = New-Object -TypeName $typeName -ArgumentList $arguments + $arguments[1] = $identity + $arguments[2] = [Security2.FileSystemRights2]::Delete + $differentRights = New-Object -TypeName $typeName -ArgumentList $arguments + + $first.Equals($equal) | Should -BeTrue + $first.GetHashCode() | Should -Be $equal.GetHashCode() + $first.Equals($differentIdentity) | Should -BeFalse + $first.Equals($differentRights) | Should -BeFalse + $first.Equals($null) | Should -BeFalse + $equal.AccessControlType = 'Deny' + $first.Equals($equal) | Should -BeFalse + $first.Name | Should -BeExactly 'Rule.txt' + } + + It 'Should reduce the generic mask in access entries' -ForEach @( + @{ Rights = 'GenericRead'; Expected = 'Read' } + @{ Rights = 'GenericWrite'; Expected = 'Write' } + @{ Rights = 'GenericExecute'; Expected = 'Read' } + @{ Rights = 'GenericAll'; Expected = 'Read, Write, Delete' } + ) { + $entry = New-Object -TypeName 'Security2.SimpleFileSystemAccessRule' -ArgumentList ( + $objectPath, $identity, [Security2.FileSystemRights2] $Rights, + [System.Security.AccessControl.AccessControlType]::Allow + ) + + $entry.AccessRights | Should -Be ([Security2.SimpleFileSystemAccessRights] $Expected) + } + + It 'Should convert an identity implicitly to its resolved display name and reject an unresolved name' { + $conversion = [Security2.IdentityReference2].GetMethods([Reflection.BindingFlags] 'Public, Static') | + Where-Object { $_.Name -eq 'op_Implicit' -and $_.ReturnType -eq [string] } + $conversion.Invoke($null, [object[]] @($identity.PSObject.BaseObject)) | Should -BeExactly $identity.ToString() + $unresolved = [Security2.IdentityReference2] 'S-1-5-21-1-2-3-1001' + $unresolved.Equals('Not-resolved') | Should -BeFalse + $identity.Equals($identity.AccountName) | Should -BeTrue + } + + It 'Should compare different privilege values as unequal' { + $constructor = [ProcessPrivileges.PrivilegeAndAttributes].GetConstructor( + [Reflection.BindingFlags] 'NonPublic, Instance', $null, + [type[]] @([ProcessPrivileges.Privilege], [ProcessPrivileges.PrivilegeAttributes]), $null + ) + $first = $constructor.Invoke(@([ProcessPrivileges.Privilege]::Backup, [ProcessPrivileges.PrivilegeAttributes]::Disabled)) + $different = $constructor.Invoke(@([ProcessPrivileges.Privilege]::Restore, [ProcessPrivileges.PrivilegeAttributes]::Disabled)) + + $first.Equals($different) | Should -BeFalse + [ProcessPrivileges.PrivilegeAndAttributes]::op_Inequality($first, $different) | Should -BeTrue + } +}