diff --git a/Tests/ParameterSets.Audit.Tests.ps1 b/Tests/ParameterSets.Audit.Tests.ps1 index be9fe4f..ddd20dc 100644 --- a/Tests/ParameterSets.Audit.Tests.ps1 +++ b/Tests/ParameterSets.Audit.Tests.ps1 @@ -117,13 +117,16 @@ Describe 'Audit cmdlets with a security descriptor that has no audit section' { Describe 'Add-NTFSAudit' { Context 'Binding -Path from the pipeline by property name' { + # -AppliesTo is named for the Simple row, not piped: an object with an AppliesTo property binds the default set + # PathComplex and the property is dropped (the folders show it: the scope of the entry stays the default one), so + # only the named parameter selects PathSimple. It 'Should add the audit entry for the piped object that exists, write a ReadFileError for the one that does not, and leave the DACL unchanged' -ForEach @( - @{ Set = 'PathComplex'; ExtraProperties = @{ InheritanceFlags = 'None'; PropagationFlags = 'None' } } - @{ Set = 'PathSimple'; ExtraProperties = @{ AppliesTo = 'ThisFolderOnly' } } + @{ Set = 'PathComplex'; ExtraProperties = @{ InheritanceFlags = 'None'; PropagationFlags = 'None' }; Named = @{} } + @{ Set = 'PathSimple'; ExtraProperties = @{}; Named = @{ AppliesTo = 'ThisFolderOnly' } } ) -Skip:(-not $canReadAudit) { $missing = Join-Path -Path $sandbox -ChildPath ('AddPipeMissing-{0}' -f [guid]::NewGuid().ToString('N')) Assert-TestSandboxPath -Sandbox $sandbox -Path $missing - $good = New-TestSandboxItem -Sandbox $sandbox -Name "AddPipeGood$Set" + $good = New-TestSandboxItem -Sandbox $sandbox -Name "AddPipeGood$Set" -Directory $daclBefore = Get-TestDaclSddl -Path $good $items = @( @@ -131,7 +134,7 @@ Describe 'Add-NTFSAudit' { [pscustomobject] (@{ FullName = $good; Account = $probe; AccessRights = 'ReadData' } + $ExtraProperties) ) - $result = @($items | Add-NTFSAudit -PassThru -ErrorVariable auditErrors -ErrorAction SilentlyContinue) + $result = @($items | Add-NTFSAudit @Named -PassThru -ErrorVariable auditErrors -ErrorAction SilentlyContinue) $auditErrors | Should -HaveCount 1 $auditErrors[0].FullyQualifiedErrorId | Should -BeLike 'ReadFileError,*' @@ -139,7 +142,10 @@ Describe 'Add-NTFSAudit' { $auditErrors[0].TargetObject | Should -Be $missing $result | Should -HaveCount 1 $result[0].FullName | Should -Be $good - (Get-TestAccountAuditRule -Acl (Get-Acl -LiteralPath $good -Audit) -Account $probe) | Should -HaveCount 1 + # The default scope of a new entry is ContainerInherit and ObjectInherit; None shows that the scope was honored + $rule = Get-TestAccountAuditRule -Acl (Get-Acl -LiteralPath $good -Audit) -Account $probe + $rule | Should -HaveCount 1 + $rule[0].InheritanceFlags | Should -Be ([System.Security.AccessControl.InheritanceFlags]::None) Get-TestDaclSddl -Path $good | Should -BeExactly $daclBefore } } @@ -151,7 +157,9 @@ Describe 'Add-NTFSAudit' { ) -Skip:(-not $canReadAudit) { $badFile = New-TestSandboxItem -Sandbox $sandbox -Name "AddPipeSDBad$Set" $sdBad = Get-TestAccessOnlyDescriptor -Path $badFile - $goodFile = New-TestSandboxItem -Sandbox $sandbox -Name "AddPipeSDGood$Set" + # A folder, so that the scope of the entry is observable; for a descriptor the piped AppliesTo property does bind + # SDSimple (no default set competes: Path is not on the object), unlike for a path + $goodFile = New-TestSandboxItem -Sandbox $sandbox -Name "AddPipeSDGood$Set" -Directory $auditBefore = Get-TestAuditSddl -Path $goodFile $daclBefore = Get-TestDaclSddl -Path $goodFile $sdGood = Get-NTFSSecurityDescriptor -Path $goodFile -ErrorAction Stop @@ -168,11 +176,14 @@ Describe 'Add-NTFSAudit' { $auditErrors[0].TargetObject | Should -Be $sdBad $result | Should -HaveCount 1 $result[0].FullName | Should -Be $goodFile - (Get-TestAccountAuditRule -Acl $sdGood.SecurityDescriptor -Account $probe) | Should -HaveCount 1 + $rule = Get-TestAccountAuditRule -Acl $sdGood.SecurityDescriptor -Account $probe + $rule | Should -HaveCount 1 + $rule[0].InheritanceFlags | Should -Be ([System.Security.AccessControl.InheritanceFlags]::None) Get-TestAuditSddl -Path $goodFile | Should -BeExactly $auditBefore Set-NTFSSecurityDescriptor -SecurityDescriptor $sdGood -ErrorAction Stop + (Get-TestAccountAuditRule -Acl (Get-TestAuditAcl -Path $goodFile) -Account $probe) | Should -HaveCount 1 Get-TestAuditSddl -Path $goodFile | Should -Not -BeExactly $auditBefore Get-TestDaclSddl -Path $goodFile | Should -BeExactly $daclBefore } @@ -209,9 +220,11 @@ Describe 'Add-NTFSAudit' { Describe 'Remove-NTFSAudit' { Context 'Binding -Path from the pipeline by property name' { + # -AppliesTo is named for the Simple row, not piped: a piped AppliesTo property would be dropped for a path (the + # default set PathComplex wins), so only the named parameter selects PathSimple. It "Should remove the audit entry for the piped object that exists, write a ReadFileError for the one that does not, and keep the other account's entry" -ForEach @( - @{ Set = 'PathComplex'; ExtraProperties = @{ InheritanceFlags = 'None'; PropagationFlags = 'None' } } - @{ Set = 'PathSimple'; ExtraProperties = @{ AppliesTo = 'ThisFolderOnly' } } + @{ Set = 'PathComplex'; ExtraProperties = @{ InheritanceFlags = 'None'; PropagationFlags = 'None' }; Named = @{} } + @{ Set = 'PathSimple'; ExtraProperties = @{}; Named = @{ AppliesTo = 'ThisFolderOnly' } } ) -Skip:(-not $canReadAudit) { $missing = Join-Path -Path $sandbox -ChildPath ('RemovePipeMissing-{0}' -f [guid]::NewGuid().ToString('N')) Assert-TestSandboxPath -Sandbox $sandbox -Path $missing @@ -225,7 +238,7 @@ Describe 'Remove-NTFSAudit' { [pscustomobject] (@{ FullName = $good; Account = $probe; AccessRights = 'ReadData' } + $ExtraProperties) ) - $result = @($items | Remove-NTFSAudit -PassThru -ErrorVariable auditErrors -ErrorAction SilentlyContinue) + $result = @($items | Remove-NTFSAudit @Named -PassThru -ErrorVariable auditErrors -ErrorAction SilentlyContinue) $auditErrors | Should -HaveCount 1 $auditErrors[0].FullyQualifiedErrorId | Should -BeLike 'ReadFileError,*' diff --git a/Tests/ParameterSets.Items.Tests.ps1 b/Tests/ParameterSets.Items.Tests.ps1 index 3920cd1..6441a31 100644 --- a/Tests/ParameterSets.Items.Tests.ps1 +++ b/Tests/ParameterSets.Items.Tests.ps1 @@ -236,16 +236,27 @@ Describe 'Empty or null -Path or -Target is rejected instead of falling back to ) { $destination = New-TestSandboxItem -Sandbox $sandbox -Name 'EmptyPathDestination' -Directory $source = New-TestSandboxItem -Sandbox $sandbox -Name 'EmptyPathSource' + # An empty path resolves to the current location, so the rows run in a folder of their own: a guard that regressed + # would copy or move this empty folder and not the whole sandbox into itself. + $workFolder = New-TestSandboxItem -Sandbox $sandbox -Name 'EmptyPathWork' -Directory + # The comma keeps the array: a switch writes to the pipeline, which unrolls @('') into a plain empty string. $badPath = switch ($CaseName) { 'as an empty string' { '' } 'as $null' { $null } - 'as an array with an empty element' { @('') } + 'as an array with an empty element' { , @('') } } - { & $Command -Path $badPath -Destination $destination -ErrorAction Stop } | - Should -Throw -ErrorId "ParameterArgumentValidationError,$TypeName" + Push-Location -LiteralPath $workFolder + try { + { & $Command -Path $badPath -Destination $destination -ErrorAction Stop } | + Should -Throw -ErrorId "ParameterArgumentValidationError,$TypeName" + } + finally { + Pop-Location + } $source | Should -Exist + $workFolder | Should -Exist @(Get-ChildItem -LiteralPath $destination -Force) | Should -BeNullOrEmpty } @@ -501,9 +512,13 @@ Describe 'Get-Privileges compared with an independent source' { if ($whoamiEntry.Count -gt 0) { $libraryEntry | Should -HaveCount 1 -Because "Get-Privileges should list $name when whoami lists $whoamiName" $libraryEntry[0] | Should -BeOfType [ProcessPrivileges.PrivilegeAndAttributes] - $libraryEntry[0].PrivilegeState.ToString() | Should -Be $whoamiEntry[0].State - $isEnabledByAttributes = ($libraryEntry[0].PrivilegeAttributes -band [ProcessPrivileges.PrivilegeAttributes]::Enabled) -eq [ProcessPrivileges.PrivilegeAttributes]::Enabled - $isEnabledByAttributes | Should -Be ($whoamiEntry[0].State -eq 'Enabled') + # The State column is localized (a German Windows prints Aktiviert), so the states are compared only where + # whoami prints the English words; the privilege names above are not localized. + if ($whoamiEntry[0].State -in 'Enabled', 'Disabled') { + $libraryEntry[0].PrivilegeState.ToString() | Should -Be $whoamiEntry[0].State + $isEnabledByAttributes = ($libraryEntry[0].PrivilegeAttributes -band [ProcessPrivileges.PrivilegeAttributes]::Enabled) -eq [ProcessPrivileges.PrivilegeAttributes]::Enabled + $isEnabledByAttributes | Should -Be ($whoamiEntry[0].State -eq 'Enabled') + } } else { $libraryEntry | Should -BeNullOrEmpty -Because "Get-Privileges should not list $name when whoami does not list $whoamiName"