From ff74100d1a6bda661498ad0cbe01a7c2d6a8ec5f Mon Sep 17 00:00:00 2001 From: Raimund Andree Date: Thu, 8 Oct 2026 08:28:27 +0000 Subject: [PATCH] test: cover Set-NTFSOwner Set-NTFSOwner ran in no test of its own. Cover the owner change with and without -PassThru, pipeline input, an owner that only the Restore privilege allows, a missing path, an owner that Windows refuses (1307), and the -SecurityDescriptor parameter set. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant --- Tests/Owner.Tests.ps1 | 116 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/Tests/Owner.Tests.ps1 b/Tests/Owner.Tests.ps1 index 6e169ac..0ddc8ae 100644 --- a/Tests/Owner.Tests.ps1 +++ b/Tests/Owner.Tests.ps1 @@ -13,6 +13,8 @@ BeforeDiscovery { Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force # With the Backup privilege, Windows may grant reading the owner despite a deny entry. $canBypassDeny = Test-PrivilegeHeld -Name 'SeBackupPrivilege' + # Assigning an owner other than the user or one of its groups needs the Restore privilege. + $canAssignAnyOwner = Test-PrivilegeHeld -Name 'SeRestorePrivilege' } BeforeAll { @@ -126,3 +128,117 @@ Describe 'File and folder objects as arguments' { $result.FullName | Should -Be $file } } + +Describe 'Set-NTFSOwner' { + BeforeAll { + $sidType = [System.Security.Principal.SecurityIdentifier] + $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value + # An owner that the user can assign only with the Restore privilege + $trustedInstaller = 'S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464' + $privateData = (Get-Module -Name NTFSSecurity).PrivateData + $enablePrivileges = $privateData['EnablePrivileges'] + + function Get-TestOwner { + param ([string] $Path) + + (Get-Acl -LiteralPath $Path).GetOwner($sidType).Value + } + } + + AfterAll { + $privateData['EnablePrivileges'] = $enablePrivileges + } + + It 'Should make the account the owner and write nothing without -PassThru' { + $file = New-TestSandboxItem -Sandbox $sandbox -Name 'SetOwner' + + $result = @(Set-NTFSOwner -Path $file -Account $currentUser -ErrorAction Stop) + + $result | Should -BeNullOrEmpty + Get-TestOwner -Path $file | Should -Be $currentUser + } + + It 'Should return the new owner of a folder with -PassThru' { + $folder = New-TestSandboxItem -Sandbox $sandbox -Name 'SetOwnerFolder' -Directory + + $result = @(Set-NTFSOwner -Path $folder -Account $currentUser -PassThru -ErrorAction Stop) + + $result | Should -HaveCount 1 + $result[0] | Should -BeOfType [Security2.FileSystemOwner] + $result[0].FullName | Should -Be $folder + $result[0].Owner.Sid | Should -Be $currentUser + Get-TestOwner -Path $folder | Should -Be $currentUser + } + + It 'Should take the items from the pipeline' { + $files = 1..2 | ForEach-Object -Process { New-TestSandboxItem -Sandbox $sandbox -Name "SetOwnerPiped$_" } + + $result = @(Get-Item2 -Path $files | Set-NTFSOwner -Account $currentUser -PassThru -ErrorAction Stop) + + ($result.FullName -join '|') | Should -Be ($files -join '|') + } + + It 'Should set an owner that only the Restore privilege allows' -Skip:(-not $canAssignAnyOwner) { + $file = New-TestSandboxItem -Sandbox $sandbox -Name 'SetOwnerRestore' + + Set-NTFSOwner -Path $file -Account $trustedInstaller -ErrorAction Stop + + Get-TestOwner -Path $file | Should -Be $trustedInstaller + } + + It 'Should write a read error for a path that does not exist and continue with the next path' { + $missing = Join-Path -Path $sandbox -ChildPath 'SetOwnerMissing.txt' + $file = New-TestSandboxItem -Sandbox $sandbox -Name 'SetOwnerAfterMissing' + + Set-NTFSOwner -Path $missing, $file -Account $currentUser -ErrorVariable ownerErrors -ErrorAction SilentlyContinue + + $ownerErrors | Should -HaveCount 1 + $ownerErrors[0].FullyQualifiedErrorId | Should -BeLike 'ReadFileError,*' + Get-TestOwner -Path $file | Should -Be $currentUser + } + + Context 'When Windows refuses the owner' { + BeforeAll { + $privateData['EnablePrivileges'] = $false + } + + AfterAll { + $privateData['EnablePrivileges'] = $enablePrivileges + } + + # Without the Restore privilege, Windows refuses an owner other than the user or one of its groups: (1307) This + # security ID may not be assigned as the owner of this object. + It 'Should write a SetOwnerError and keep the owner' { + $file = New-TestSandboxItem -Sandbox $sandbox -Name 'SetOwnerRefused' + $owner = Get-TestOwner -Path $file + (Get-Privileges | Where-Object -Property Privilege -EQ -Value 'Restore').PrivilegeState | Should -Not -Be 'Enabled' + + Set-NTFSOwner -Path $file -Account $trustedInstaller -ErrorVariable ownerErrors -ErrorAction SilentlyContinue + + $ownerErrors | Should -HaveCount 1 + $ownerErrors[0].FullyQualifiedErrorId | Should -BeLike 'SetOwnerError,*' + Get-TestOwner -Path $file | Should -Be $owner + } + } + + Context 'With -SecurityDescriptor' { + It 'Should change only the descriptor in memory until Set-NTFSSecurityDescriptor writes it' { + $file = New-TestSandboxItem -Sandbox $sandbox -Name 'SetOwnerDescriptor' + $owner = Get-TestOwner -Path $file + if ($owner -eq $currentUser) { + # Only an elevated session creates items that the Administrators group owns. + Set-ItResult -Skipped -Because 'the user owns new items, and no other owner can be set without privileges' + return + } + $sd = Get-NTFSSecurityDescriptor -Path $file + + $result = @(Set-NTFSOwner -SecurityDescriptor $sd -Account $currentUser -PassThru -ErrorAction Stop) + + $result | Should -HaveCount 1 + $result[0].Owner.Sid | Should -Be $currentUser + Get-TestOwner -Path $file | Should -Be $owner + Set-NTFSSecurityDescriptor -SecurityDescriptor $sd -ErrorAction Stop + Get-TestOwner -Path $file | Should -Be $currentUser + } + } +}