mirror of https://github.com/raandree/NTFSSecurity
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
9.2 KiB
189 lines
9.2 KiB
<#
|
|
Tests the audit cmdlets of the module built in NTFSSecurity\bin\Release on files in a sandbox folder. Reading
|
|
and changing audit entries needs the Security privilege; tests that need it skip without it and run in CI,
|
|
whose runners are elevated.
|
|
#>
|
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
|
|
'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Pester shares variables between blocks.'
|
|
)]
|
|
param ()
|
|
|
|
BeforeDiscovery {
|
|
Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force
|
|
$canReadAudit = Test-PrivilegeHeld -Name 'SeSecurityPrivilege'
|
|
}
|
|
|
|
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 'Audit'
|
|
Push-Location -LiteralPath $sandbox
|
|
}
|
|
|
|
AfterAll {
|
|
Pop-Location
|
|
Remove-TestSandbox -Sandbox $sandbox
|
|
Remove-Module -Name NTFSSecurity -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
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-TestSandboxItem -Sandbox $sandbox -Name 'NoPrivilege'
|
|
|
|
$entries = @(Get-NTFSAudit -Path $file -ErrorVariable auditErrors -ErrorAction SilentlyContinue)
|
|
|
|
$entries | Should -BeNullOrEmpty
|
|
$auditErrors | Should -HaveCount 1
|
|
$auditErrors[0].FullyQualifiedErrorId | Should -BeLike 'ReadSecurityError,*'
|
|
}
|
|
|
|
It 'Should write an error for a security descriptor that was read without the audit entries' {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'AccessOnly'
|
|
$sd = New-Object -TypeName 'Security2.FileSystemSecurity2' -ArgumentList (
|
|
(Get-Item2 -Path $file), [System.Security.AccessControl.AccessControlSections]::Access
|
|
)
|
|
|
|
$entries = @(Get-NTFSAudit -SecurityDescriptor $sd -ErrorVariable auditErrors -ErrorAction SilentlyContinue)
|
|
|
|
$entries | Should -BeNullOrEmpty
|
|
$auditErrors | Should -HaveCount 1
|
|
$auditErrors[0].FullyQualifiedErrorId | Should -BeLike 'ReadSecurityError,*'
|
|
}
|
|
}
|
|
|
|
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. In CI, the deny
|
|
# entry made the original implementation, which also read the DACL, fail for the second path. The current one
|
|
# reads only the SACL, which the deny entry doesn't block; Access.Tests.ps1 guards the same loop fix in
|
|
# Get-NTFSAccess with a read that fails without elevation.
|
|
It 'Should return the entries of the first item once' -Skip:(-not $canReadAudit) {
|
|
$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
|
|
Block-TestReadPermission -Sandbox $sandbox -Path $denied
|
|
|
|
$entries = @(Get-NTFSAudit -Path $folder, $denied -ExcludeInherited -ErrorAction SilentlyContinue)
|
|
|
|
@($entries | Where-Object -Property FullName -EQ -Value $folder) | Should -HaveCount 1
|
|
}
|
|
}
|
|
}
|
|
|
|
Describe 'Add-NTFSAudit' {
|
|
Context 'Positional parameters' {
|
|
It 'Should take -Account at position 2 and -AccessRights at position 3 in the <_> parameter set' -ForEach @(
|
|
'PathSimple', 'PathComplex', 'SDSimple', 'SDComplex'
|
|
) {
|
|
$parameterSet = (Get-Command -Name Add-NTFSAudit).ParameterSets | Where-Object -Property Name -EQ -Value $_
|
|
$positions = @{}
|
|
$parameterSet.Parameters | Where-Object -Property Position -GE -Value 0 | ForEach-Object -Process {
|
|
$positions[$_.Name] = $_.Position
|
|
}
|
|
|
|
$positions['Account'] | Should -Be 2
|
|
$positions['AccessRights'] | Should -Be 3
|
|
}
|
|
|
|
It 'Should bind an account and access rights that are passed by position' {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'Positional'
|
|
$sd = Get-NTFSSecurityDescriptor -Path $file
|
|
|
|
Add-NTFSAudit -SecurityDescriptor $sd 'Everyone' 'ReadData' -InheritanceFlags None -PropagationFlags None -ErrorAction Stop
|
|
|
|
$rules = $sd.SecurityDescriptor.GetAuditRules($true, $false, [System.Security.Principal.SecurityIdentifier])
|
|
@($rules | Where-Object -FilterScript { $_.IdentityReference.Value -eq 'S-1-1-0' }) | Should -HaveCount 1
|
|
}
|
|
}
|
|
|
|
Context 'With -PassThru' {
|
|
It 'Should return the audit entries of a security descriptor, not its access entries' {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'PassThru'
|
|
$sd = Get-NTFSSecurityDescriptor -Path $file
|
|
|
|
$result = @(Add-NTFSAudit -SecurityDescriptor $sd -Account 'Everyone' -AccessRights ReadData -InheritanceFlags None -PropagationFlags None -PassThru)
|
|
|
|
$result | Should -Not -BeNullOrEmpty
|
|
$result | ForEach-Object -Process { $_ | Should -BeOfType [Security2.FileSystemAuditRule2] }
|
|
@($result | Where-Object -FilterScript { $_.Account.Sid -eq 'S-1-1-0' }) | Should -HaveCount 1
|
|
}
|
|
|
|
It 'Should report the inheritance of the audit entries in InheritanceEnabled, not that of the access entries' {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'AuditProtected'
|
|
$sd = Get-NTFSSecurityDescriptor -Path $file
|
|
$sd.SecurityDescriptor.SetAuditRuleProtection($true, $false)
|
|
|
|
$result = @(Add-NTFSAudit -SecurityDescriptor $sd -Account 'Everyone' -AccessRights ReadData -InheritanceFlags None -PropagationFlags None -PassThru)
|
|
|
|
$sd.SecurityDescriptor.AreAccessRulesProtected | Should -BeFalse
|
|
$result | Should -Not -BeNullOrEmpty
|
|
$result | ForEach-Object -Process { $_.InheritanceEnabled | Should -BeFalse }
|
|
}
|
|
}
|
|
}
|
|
|
|
Describe 'Get-NTFSOrphanedAudit' {
|
|
BeforeAll {
|
|
$orphanedFile = New-TestSandboxItem -Sandbox $sandbox -Name 'OrphanedAudit'
|
|
}
|
|
|
|
# Before 5.0.0, the cmdlet wrote the entries of an item as one collection and ignored -Account.
|
|
It 'Should return one object per entry whose account cannot be resolved' -Skip:(-not $canReadAudit) {
|
|
foreach ($sid in 'S-1-5-21-1-2-3-1001', 'S-1-5-21-1-2-3-1002') {
|
|
Add-NTFSAudit -Path $orphanedFile -Account $sid -AccessRights ReadData -InheritanceFlags None -PropagationFlags None
|
|
}
|
|
|
|
$result = @(Get-NTFSOrphanedAudit -Path $orphanedFile)
|
|
|
|
$result | Should -HaveCount 2
|
|
$result | ForEach-Object -Process { $_ | Should -BeOfType [Security2.FileSystemAuditRule2] }
|
|
}
|
|
|
|
It 'Should return only the entries of -Account' -Skip:(-not $canReadAudit) {
|
|
$result = @(Get-NTFSOrphanedAudit -Path $orphanedFile -Account 'S-1-5-21-1-2-3-1002')
|
|
|
|
$result | Should -HaveCount 1
|
|
}
|
|
}
|
|
|
|
Describe 'Remove-NTFSAudit' {
|
|
Context 'With -RemoveSpecific' {
|
|
BeforeEach {
|
|
$removeFolder = New-TestSandboxItem -Sandbox $sandbox -Name 'RemoveSpecific' -Directory
|
|
$sd = Get-NTFSSecurityDescriptor -Path $removeFolder
|
|
Add-NTFSAudit -SecurityDescriptor $sd -Account 'Everyone' -AccessRights Modify
|
|
|
|
function Get-EveryoneAuditRule {
|
|
$sd.SecurityDescriptor.GetAuditRules($true, $false, [System.Security.Principal.SecurityIdentifier]) |
|
|
Where-Object -FilterScript { $_.IdentityReference.Value -eq 'S-1-1-0' }
|
|
}
|
|
}
|
|
|
|
It 'Should keep an audit entry that does not match exactly' {
|
|
Remove-NTFSAudit -SecurityDescriptor $sd -Account 'Everyone' -AccessRights ReadData -RemoveSpecific
|
|
|
|
(Get-EveryoneAuditRule).FileSystemRights.HasFlag([System.Security.AccessControl.FileSystemRights]::Modify) | Should -BeTrue
|
|
}
|
|
|
|
It 'Should remove an audit entry that matches exactly' {
|
|
Remove-NTFSAudit -SecurityDescriptor $sd -Account 'Everyone' -AccessRights Modify -RemoveSpecific
|
|
|
|
Get-EveryoneAuditRule | Should -BeNullOrEmpty
|
|
}
|
|
}
|
|
|
|
Context 'With -PassThru' {
|
|
It 'Should return the audit entries of the item, not its access entries' -Skip:(-not $canReadAudit) {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'PassThru'
|
|
Add-NTFSAudit -Path $file -Account 'Everyone' -AccessRights ReadData -InheritanceFlags None -PropagationFlags None
|
|
Add-NTFSAudit -Path $file -Account 'BUILTIN\Users' -AccessRights Delete -InheritanceFlags None -PropagationFlags None
|
|
|
|
$result = @(Remove-NTFSAudit -Path $file -Account 'Everyone' -AccessRights ReadData -InheritanceFlags None -PropagationFlags None -PassThru)
|
|
|
|
$result | Should -Not -BeNullOrEmpty
|
|
$result | ForEach-Object -Process { $_ | Should -BeOfType [Security2.FileSystemAuditRule2] }
|
|
@($result | Where-Object -FilterScript { $_.Account.Sid -eq 'S-1-5-32-545' }) | Should -HaveCount 1
|
|
}
|
|
}
|
|
}
|
|
|