From 344219441b4c50dc91e2ace0634cf636b800f2c7 Mon Sep 17 00:00:00 2001 From: Raimund Andree Date: Fri, 9 Oct 2026 09:01:50 +0000 Subject: [PATCH] test: guard forced moves and descriptor write failures Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant --- Tests/ItemCmdlets.Tests.ps1 | 19 +++++++++++ Tests/SecurityDescriptor.Tests.ps1 | 51 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/Tests/ItemCmdlets.Tests.ps1 b/Tests/ItemCmdlets.Tests.ps1 index 3b565f2..d21eea2 100644 --- a/Tests/ItemCmdlets.Tests.ps1 +++ b/Tests/ItemCmdlets.Tests.ps1 @@ -425,6 +425,25 @@ Describe 'Copy-Item2, Move-Item2, and Remove-Item2 with several paths' { Join-Path -Path $sourceFolder -ChildPath 'A.txt' | Should -Exist } + It 'Move-Item2 -Force should replace an existing file with PassThru=<_>' -ForEach @($false, $true) { + $target = Join-Path -Path $destination -ChildPath 'First.txt' + Assert-TestSandboxPath -Sandbox $sandbox -Path $target + Set-Content -LiteralPath $target -Value 'Previous' + $expected = Get-Content -LiteralPath $first -Raw + + $result = @(Move-Item2 -Path $first -Destination $destination -Force -PassThru $_ -ErrorAction Stop) + + $first | Should -Not -Exist + Get-Content -LiteralPath $target -Raw | Should -BeExactly $expected + if ($_) { + $result | Should -HaveCount 1 + $result[0].FullName | Should -Be $target + } + else { + $result | Should -BeNullOrEmpty + } + } + It 'Copy-Item2 -Force should copy a folder into an existing folder of the same name and replace the files in both' { $sourceFolder = Join-Path -Path $folder -ChildPath 'Merge' $existingFolder = Join-Path -Path $destination -ChildPath 'Merge' diff --git a/Tests/SecurityDescriptor.Tests.ps1 b/Tests/SecurityDescriptor.Tests.ps1 index 6be3eeb..d60cb25 100644 --- a/Tests/SecurityDescriptor.Tests.ps1 +++ b/Tests/SecurityDescriptor.Tests.ps1 @@ -227,6 +227,57 @@ Describe 'Set-NTFSSecurityDescriptor' { } } + Context 'A descriptor that cannot be written' { + BeforeEach { + $savedPrivileges = $privateData['EnablePrivileges'] + $privateData['EnablePrivileges'] = $false + } + + AfterEach { + $privateData['EnablePrivileges'] = $savedPrivileges + } + + It 'Should report a denied write, return no failed item, and write the next descriptor' { + $blocked = New-TestSandboxItem -Sandbox $sandbox -Name 'DescriptorWriteDenied' + $next = New-TestSandboxItem -Sandbox $sandbox -Name 'DescriptorWriteNext' + Block-TestWritePermission -Sandbox $sandbox -Path $blocked + $before = (Get-Acl -LiteralPath $blocked).Sddl + $descriptors = @(Get-NTFSSecurityDescriptor -Path $blocked, $next) + $descriptors | Should -HaveCount 2 + Add-NTFSAccess -SecurityDescriptor $descriptors -Account 'S-1-1-0' -AccessRights ReadData -ErrorAction Stop + + $result = @($descriptors | Set-NTFSSecurityDescriptor -PassThru -ErrorVariable setErrors -ErrorAction SilentlyContinue) + + $setErrors | Should -HaveCount 1 + $setErrors[0].FullyQualifiedErrorId | Should -BeLike 'WriteSdError,*' + $setErrors[0].CategoryInfo.Category | Should -Be 'WriteError' + $setErrors[0].TargetObject.FullName | Should -Be $blocked + (Get-Acl -LiteralPath $blocked).Sddl | Should -BeExactly $before + $result | Should -HaveCount 1 + $result[0].FullName | Should -Be $next + @(Get-EveryoneRule -Path $next) | Should -HaveCount 1 + } + + It 'Should report a deleted target and still write the next descriptor' { + $deleted = New-TestSandboxItem -Sandbox $sandbox -Name 'DescriptorDeleted' + $next = New-TestSandboxItem -Sandbox $sandbox -Name 'DescriptorAfterDeleted' + $descriptors = @(Get-NTFSSecurityDescriptor -Path $deleted, $next) + Add-NTFSAccess -SecurityDescriptor $descriptors -Account 'S-1-1-0' -AccessRights ReadData -ErrorAction Stop + Assert-TestSandboxPath -Sandbox $sandbox -Path $deleted + Remove-Item -LiteralPath $deleted + + $result = @(Set-NTFSSecurityDescriptor -SecurityDescriptor $descriptors -PassThru -ErrorVariable setErrors -ErrorAction SilentlyContinue) + + $setErrors | Should -HaveCount 1 + $setErrors[0].FullyQualifiedErrorId | Should -BeLike 'WriteSdError,*' + $setErrors[0].TargetObject.FullName | Should -Be $deleted + $deleted | Should -Not -Exist + $result | Should -HaveCount 1 + $result[0].FullName | Should -Be $next + @(Get-EveryoneRule -Path $next) | Should -HaveCount 1 + } + } + Context 'When the written descriptor denies reading it again' { BeforeAll { $privateData['EnablePrivileges'] = $false