diff --git a/Tests/TestHelpers.Tests.ps1 b/Tests/TestHelpers.Tests.ps1 index 0969633..1daf2f4 100644 --- a/Tests/TestHelpers.Tests.ps1 +++ b/Tests/TestHelpers.Tests.ps1 @@ -11,6 +11,8 @@ BeforeDiscovery { Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force # Assigning an owner other than the user or one of its groups needs the Restore privilege. $canAssignAnyOwner = Test-PrivilegeHeld -Name 'SeRestorePrivilege' + # Reading and writing audit entries needs the Security privilege. + $holdsSecurityPrivilege = Test-PrivilegeHeld -Name 'SeSecurityPrivilege' } BeforeAll { @@ -212,6 +214,41 @@ Describe 'Test helpers' { $rules[0].AccessControlType | Should -Be 'Deny' } + # Set-Acl also writes the audit section of an item whose DACL is protected, which fails without the Security + # privilege. + It 'Should add a deny entry to an item whose DACL is protected' { + $file = New-TestSandboxItem -Sandbox $sandbox -Name 'Protected' + & icacls.exe $file /inheritance:d *> $null + $LASTEXITCODE | Should -Be 0 + + Add-TestDenyRule -Sandbox $sandbox -Path $file -Rights @{ 'S-1-5-32-546' = 'ReadData' } + + $acl = Get-Acl -LiteralPath $file + $acl.AreAccessRulesProtected | Should -BeTrue + $rules = @($acl.GetAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier]) | + Where-Object -FilterScript { $_.IdentityReference.Value -eq 'S-1-5-32-546' }) + $rules | Should -HaveCount 1 + $rules[0].AccessControlType | Should -Be 'Deny' + } + + # With the Security privilege, Set-Acl writes all sections, so the audit entries of the item would be lost. + It 'Should keep the audit entries of the item' -Skip:(-not $holdsSecurityPrivilege) { + $file = New-TestSandboxItem -Sandbox $sandbox -Name 'Audited' + $auditAcl = Get-Acl -LiteralPath $file -Audit + $auditAcl.AddAuditRule((New-Object -TypeName 'System.Security.AccessControl.FileSystemAuditRule' -ArgumentList ( + (New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList 'S-1-1-0'), + [System.Security.AccessControl.FileSystemRights]::Delete, + [System.Security.AccessControl.AuditFlags]::Success + ))) + Set-Acl -LiteralPath $file -AclObject $auditAcl + + Add-TestDenyRule -Sandbox $sandbox -Path $file -Rights @{ 'S-1-5-32-546' = 'ReadData' } + + $auditRules = @((Get-Acl -LiteralPath $file -Audit).GetAuditRules($true, $false, [System.Security.Principal.SecurityIdentifier])) + $auditRules | Should -HaveCount 1 + $auditRules[0].IdentityReference.Value | Should -Be 'S-1-1-0' + } + It 'Should refuse an item outside the sandbox' { { Add-TestDenyRule -Sandbox $sandbox -Path "$sandbox-Other\File.txt" -Rights @{ 'S-1-5-32-546' = 'ReadData' } } | Should -Throw -ExpectedMessage 'Refusing to change*' diff --git a/Tests/TestHelpers.psm1 b/Tests/TestHelpers.psm1 index 277a485..6a15057 100644 --- a/Tests/TestHelpers.psm1 +++ b/Tests/TestHelpers.psm1 @@ -277,6 +277,7 @@ function Add-TestDenyRule { ) Assert-TestSandboxPath -Sandbox $Sandbox -Path $Path + $item = Get-Item -LiteralPath $Path -Force -ErrorAction Stop $acl = Get-Acl -LiteralPath $Path foreach ($sid in $Rights.Keys) { $identity = New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList $sid @@ -286,7 +287,15 @@ function Add-TestDenyRule { ) $acl.AddAccessRule($rule) } - Set-Acl -LiteralPath $Path -AclObject $acl + # Not Set-Acl: it compares AreAuditRulesProtected with AreAccessRulesProtected, so it also writes the audit + # section of an item whose DACL is protected, which fails without the Security privilege. With the privilege, it + # writes all sections and drops the audit entries. SetAccessControl writes only the DACL, the section that changed. + if ($PSVersionTable.PSEdition -eq 'Core') { + [System.IO.FileSystemAclExtensions]::SetAccessControl($item, $acl) + } + else { + $item.SetAccessControl($acl) + } } function Set-TestOwner {