diff --git a/Tests/Audit.Tests.ps1 b/Tests/Audit.Tests.ps1 index 59293c2..77df1b5 100644 --- a/Tests/Audit.Tests.ps1 +++ b/Tests/Audit.Tests.ps1 @@ -19,37 +19,6 @@ BeforeAll { Import-Module -Name $modulePath -Force -ErrorAction Stop $sandbox = New-TestSandbox -Name 'Audit' Push-Location -LiteralPath $sandbox - - function New-SandboxItem { - param ( - [string] $Name, - [switch] $Directory - ) - - $path = Join-Path -Path $sandbox -ChildPath ('{0}-{1}' -f $Name, [guid]::NewGuid().ToString('N').Substring(0, 8)) - Assert-TestSandboxPath -Sandbox $sandbox -Path $path - if ($Directory) { - New-Item -ItemType Directory -Path $path | Out-Null - } - else { - Set-Content -LiteralPath $path -Value 'Audit test' - } - $path - } - - # Denies the owner, the current account, to read the security descriptor of the item. - function Deny-ReadPermission { - param ([string] $Path) - - Assert-TestSandboxPath -Sandbox $sandbox -Path $Path - $acl = Get-Acl -LiteralPath $Path - $ownerRights = New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList 'S-1-3-4' - $rule = New-Object -TypeName 'System.Security.AccessControl.FileSystemAccessRule' -ArgumentList ( - $ownerRights, [System.Security.AccessControl.FileSystemRights]::ReadPermissions, [System.Security.AccessControl.AccessControlType]::Deny - ) - $acl.AddAccessRule($rule) - Set-Acl -LiteralPath $Path -AclObject $acl - } } AfterAll { @@ -61,7 +30,7 @@ AfterAll { Describe 'Get-NTFSAudit' { Context 'When the audit entries cannot be read' { It 'Should write an error without the Security privilege instead of returning nothing' -Skip:$canReadAudit { - $file = New-SandboxItem -Name 'NoPrivilege' + $file = New-TestSandboxItem -Sandbox $sandbox -Name 'NoPrivilege' $entries = @(Get-NTFSAudit -Path $file -ErrorVariable auditErrors -ErrorAction SilentlyContinue) @@ -71,7 +40,7 @@ Describe 'Get-NTFSAudit' { } It 'Should write an error for a security descriptor that was read without the audit entries' { - $file = New-SandboxItem -Name 'AccessOnly' + $file = New-TestSandboxItem -Sandbox $sandbox -Name 'AccessOnly' $sd = New-Object -TypeName 'Security2.FileSystemSecurity2' -ArgumentList ( (Get-Item2 -Path $file), [System.Security.AccessControl.AccessControlSections]::Access ) @@ -87,10 +56,10 @@ Describe 'Get-NTFSAudit' { Context 'When a path fails after a path with audit entries' { # 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' -Skip:(-not $canReadAudit) { - $folder = New-SandboxItem -Name 'Audited' -Directory - $denied = New-SandboxItem -Name 'Denied' + $folder = New-TestSandboxItem -Sandbox $sandbox -Name 'Audited' -Directory + $denied = New-TestSandboxItem -Sandbox $sandbox -Name 'Denied' Add-NTFSAudit -Path $folder -Account 'Everyone' -AccessRights Delete -AuditFlags Success - Deny-ReadPermission -Path $denied + Block-TestReadPermission -Sandbox $sandbox -Path $denied $entries = @(Get-NTFSAudit -Path $folder, $denied -ExcludeInherited -ErrorAction SilentlyContinue) diff --git a/Tests/Inheritance.Tests.ps1 b/Tests/Inheritance.Tests.ps1 index f21b891..8503a53 100644 --- a/Tests/Inheritance.Tests.ps1 +++ b/Tests/Inheritance.Tests.ps1 @@ -18,13 +18,6 @@ BeforeAll { Import-Module -Name $modulePath -Force -ErrorAction Stop $sandbox = New-TestSandbox -Name 'Inheritance' Push-Location -LiteralPath $sandbox - - function New-SandboxFile { - $path = Join-Path -Path $sandbox -ChildPath ('File-{0}.txt' -f [guid]::NewGuid().ToString('N').Substring(0, 8)) - Assert-TestSandboxPath -Sandbox $sandbox -Path $path - Set-Content -LiteralPath $path -Value 'Inheritance test' - $path - } } AfterAll { @@ -36,7 +29,7 @@ AfterAll { Describe 'Set-NTFSInheritance' { Context 'When -AccessInheritanceEnabled or -AuditInheritanceEnabled is omitted' { BeforeEach { - $file = New-SandboxFile + $file = New-TestSandboxItem -Sandbox $sandbox -Name 'File' Assert-TestSandboxPath -Sandbox $sandbox -Path $file } diff --git a/Tests/TestHelpers.psm1 b/Tests/TestHelpers.psm1 index 3ac1f64..4d7656d 100644 --- a/Tests/TestHelpers.psm1 +++ b/Tests/TestHelpers.psm1 @@ -86,6 +86,10 @@ function Remove-TestSandbox { return } + # icacls reports items it cannot reset on stderr, which Windows PowerShell turns into a terminating error when + # the caller uses ErrorAction Stop. Such items can still be deleted through the rights on their folder. + $ErrorActionPreference = 'Continue' + # Windows PowerShell 5.1 follows directory links when it removes a folder recursively, so the links go first. & icacls.exe $Sandbox /reset /T /C /Q *> $null $pending = New-Object -TypeName 'System.Collections.Generic.Stack[string]' @@ -120,6 +124,70 @@ function Remove-TestSandbox { } } +function New-TestSandboxItem { + <# + .SYNOPSIS + Creates a file or folder with a unique name in the sandbox and returns its full path. + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Test helper that only writes to sandboxes.' + )] + [CmdletBinding()] + [OutputType([string])] + param ( + [Parameter(Mandatory)] + [string] + $Sandbox, + + [ValidatePattern('^[\w-]+$')] + [string] + $Name = 'Item', + + [switch] + $Directory + ) + + $path = Join-Path -Path $Sandbox -ChildPath ('{0}-{1}' -f $Name, [guid]::NewGuid().ToString('N').Substring(0, 8)) + Assert-TestSandboxPath -Sandbox $Sandbox -Path $path + if ($Directory) { + New-Item -ItemType Directory -Path $path | Out-Null + } + else { + Set-Content -LiteralPath $path -Value $Name + } + $path +} + +function Block-TestReadPermission { + <# + .SYNOPSIS + Denies the owner of an item in the sandbox to read its security descriptor, so that reading it fails. + .DESCRIPTION + Adds a deny entry for OWNER RIGHTS (S-1-3-4) with ReadPermissions, which replaces the implicit right of the + owner to read the security descriptor. Remove-TestSandbox resets it. + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string] + $Sandbox, + + [Parameter(Mandatory)] + [string] + $Path + ) + + Assert-TestSandboxPath -Sandbox $Sandbox -Path $Path + $acl = Get-Acl -LiteralPath $Path + $ownerRights = New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList 'S-1-3-4' + $rule = New-Object -TypeName 'System.Security.AccessControl.FileSystemAccessRule' -ArgumentList ( + $ownerRights, [System.Security.AccessControl.FileSystemRights]::ReadPermissions, + [System.Security.AccessControl.AccessControlType]::Deny + ) + $acl.AddAccessRule($rule) + Set-Acl -LiteralPath $Path -AclObject $acl +} + function Test-IsElevated { <# .SYNOPSIS @@ -160,4 +228,5 @@ function Test-PrivilegeHeld { Where-Object -Property Name -EQ -Value $Name) } -Export-ModuleMember -Function New-TestSandbox, Assert-TestSandboxPath, Remove-TestSandbox, Test-IsElevated, Test-PrivilegeHeld +Export-ModuleMember -Function New-TestSandbox, Assert-TestSandboxPath, Remove-TestSandbox, New-TestSandboxItem, + Block-TestReadPermission, Test-IsElevated, Test-PrivilegeHeld