From 1063b29cc4abdd7f15c2049338b43c9197f846ca Mon Sep 17 00:00:00 2001 From: Raimund Andree Date: Thu, 8 Oct 2026 16:06:27 +0000 Subject: [PATCH] test: share the helpers for the administrative share From the security-reviewer pass over be04cb7..4ee01e5 (Nit 8): the probe for the administrative share and the conversion of a sandbox path to \\localhost\C$\... were copied into ItemCmdlets.Tests.ps1 and Links.Tests.ps1, and only the local path passed Assert-TestSandboxPath. TestHelpers.psm1 now has Test-AdminShareAvailable and ConvertTo-TestAdminSharePath, which checks the local path against the sandbox before it returns the share path, with tests of their own. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant --- Tests/ItemCmdlets.Tests.ps1 | 19 ++++------------ Tests/Links.Tests.ps1 | 15 +++---------- Tests/TestHelpers.Tests.ps1 | 36 +++++++++++++++++++++++++++++++ Tests/TestHelpers.psm1 | 43 ++++++++++++++++++++++++++++++++++++- 4 files changed, 85 insertions(+), 28 deletions(-) diff --git a/Tests/ItemCmdlets.Tests.ps1 b/Tests/ItemCmdlets.Tests.ps1 index 6183159..760d83b 100644 --- a/Tests/ItemCmdlets.Tests.ps1 +++ b/Tests/ItemCmdlets.Tests.ps1 @@ -10,9 +10,7 @@ BeforeDiscovery { Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force # A path on the administrative share of the drive of the sandboxes is another volume for Windows, like a share of a # file server. - $tempPath = [IO.Path]::GetTempPath() - $canUseAdminShare = (Test-IsElevated) -and - (Test-Path -LiteralPath ('\\localhost\{0}$\' -f $tempPath.Substring(0, 1)) -ErrorAction SilentlyContinue) + $canUseAdminShare = Test-AdminShareAvailable } BeforeAll { @@ -359,15 +357,6 @@ Describe 'Copy-Item2, Move-Item2, and Remove-Item2 with several paths' { } Describe 'Move-Item2' { - BeforeAll { - function ConvertTo-AdminSharePath { - # The path of a sandbox item on the administrative share of its drive, such as \\localhost\C$\... - param ([string] $Path) - - '\\localhost\{0}${1}' -f $Path.Substring(0, 1), $Path.Substring(2) - } - } - # Windows can't move a folder to another volume. Before 5.0.0-rc7, the cmdlet let AlphaFS emulate the move by # copying and deleting, which failed for a folder with files with an error that named a file of the source, and # which deleted an empty folder without creating it at the destination. @@ -382,7 +371,7 @@ Describe 'Move-Item2' { $destination = Join-Path -Path $sandbox -ChildPath ('Moved-{0}' -f (Split-Path -Path $source -Leaf)) Assert-TestSandboxPath -Sandbox $sandbox -Path $destination - Move-Item2 -Path $source -Destination (ConvertTo-AdminSharePath -Path $destination) -ErrorVariable moveErrors -ErrorAction SilentlyContinue + Move-Item2 -Path $source -Destination (ConvertTo-TestAdminSharePath -Sandbox $sandbox -Path $destination) -ErrorVariable moveErrors -ErrorAction SilentlyContinue $moveErrors | Should -HaveCount 1 $moveErrors[0].FullyQualifiedErrorId | Should -BeLike 'MoveError,*' @@ -398,7 +387,7 @@ Describe 'Move-Item2' { $destination = Join-Path -Path $sandbox -ChildPath ('Moved-{0}' -f (Split-Path -Path $source -Leaf)) Assert-TestSandboxPath -Sandbox $sandbox -Path $destination - Move-Item2 -Path $source -Destination (ConvertTo-AdminSharePath -Path $destination) -ErrorAction Stop + Move-Item2 -Path $source -Destination (ConvertTo-TestAdminSharePath -Sandbox $sandbox -Path $destination) -ErrorAction Stop $source | Should -Not -Exist Get-Content -LiteralPath $destination | Should -Be 'CrossVolumeFile' @@ -412,7 +401,7 @@ Describe 'Move-Item2' { $destination = Join-Path -Path $sandbox -ChildPath ('Moved-{0}' -f (Split-Path -Path $source -Leaf)) Assert-TestSandboxPath -Sandbox $sandbox -Path $destination - Move-Item2 -Path $source -Destination (ConvertTo-AdminSharePath -Path $destination) -Force -ErrorVariable moveErrors -ErrorAction SilentlyContinue + Move-Item2 -Path $source -Destination (ConvertTo-TestAdminSharePath -Sandbox $sandbox -Path $destination) -Force -ErrorVariable moveErrors -ErrorAction SilentlyContinue $moveErrors | Should -HaveCount 1 $moveErrors[0].FullyQualifiedErrorId | Should -BeLike 'MoveError,*' diff --git a/Tests/Links.Tests.ps1 b/Tests/Links.Tests.ps1 index 5054e53..5bc938b 100644 --- a/Tests/Links.Tests.ps1 +++ b/Tests/Links.Tests.ps1 @@ -10,9 +10,7 @@ BeforeDiscovery { Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force $canCreateSymbolicLinks = Test-PrivilegeHeld -Name 'SeCreateSymbolicLinkPrivilege' # The administrative share of the drive of the sandboxes reaches them over SMB, like a share of a file server. - $tempPath = [IO.Path]::GetTempPath() - $canUseAdminShare = (Test-IsElevated) -and - (Test-Path -LiteralPath ('\\localhost\{0}$\' -f $tempPath.Substring(0, 1)) -ErrorAction SilentlyContinue) + $canUseAdminShare = Test-AdminShareAvailable } BeforeAll { @@ -21,13 +19,6 @@ BeforeAll { Import-Module -Name $modulePath -Force -ErrorAction Stop $sandbox = New-TestSandbox -Name 'Links' Push-Location -LiteralPath $sandbox - - function ConvertTo-AdminSharePath { - # The path of a sandbox item on the administrative share of its drive, such as \\localhost\C$\... - param ([string] $Path) - - '\\localhost\{0}${1}' -f $Path.Substring(0, 1), $Path.Substring(2) - } } AfterAll { @@ -161,7 +152,7 @@ Describe 'New-NTFSHardLink' { $link = Join-Path -Path $sandbox -ChildPath 'ShareLink.txt' Assert-TestSandboxPath -Sandbox $sandbox -Path $link - $result = @(New-NTFSHardLink -Path (ConvertTo-AdminSharePath -Path $link) -Target (ConvertTo-AdminSharePath -Path $target) -PassThru -ErrorVariable linkErrors -ErrorAction SilentlyContinue) + $result = @(New-NTFSHardLink -Path (ConvertTo-TestAdminSharePath -Sandbox $sandbox -Path $link) -Target (ConvertTo-TestAdminSharePath -Sandbox $sandbox -Path $target) -PassThru -ErrorVariable linkErrors -ErrorAction SilentlyContinue) $link | Should -Exist $linkErrors | Should -HaveCount 1 @@ -237,7 +228,7 @@ Describe 'Get-NTFSHardLink' { It 'Should write an error for a file on a network share and continue with the next path' -Skip:(-not $canUseAdminShare) { $file = New-TestSandboxItem -Sandbox $sandbox -Name 'ShareFile' $other = New-TestSandboxItem -Sandbox $sandbox -Name 'AfterShare' - $sharePath = ConvertTo-AdminSharePath -Path $file + $sharePath = ConvertTo-TestAdminSharePath -Sandbox $sandbox -Path $file $result = @(Get-NTFSHardLink -Path $sharePath, $other -ErrorVariable linkErrors -ErrorAction SilentlyContinue) diff --git a/Tests/TestHelpers.Tests.ps1 b/Tests/TestHelpers.Tests.ps1 index 1daf2f4..713ef18 100644 --- a/Tests/TestHelpers.Tests.ps1 +++ b/Tests/TestHelpers.Tests.ps1 @@ -13,6 +13,8 @@ BeforeDiscovery { $canAssignAnyOwner = Test-PrivilegeHeld -Name 'SeRestorePrivilege' # Reading and writing audit entries needs the Security privilege. $holdsSecurityPrivilege = Test-PrivilegeHeld -Name 'SeSecurityPrivilege' + $isElevated = Test-IsElevated + $canUseAdminShare = Test-AdminShareAvailable } BeforeAll { @@ -268,4 +270,38 @@ Describe 'Test helpers' { Test-PrivilegeHeld -Name 'SeNoSuchPrivilege' | Should -BeFalse } } + + # The administrative share of a drive, such as \\localhost\C$, is a network path and another volume for Windows. + Context 'ConvertTo-TestAdminSharePath and Test-AdminShareAvailable' { + BeforeAll { + $sandbox = New-TestSandbox -Name 'Helpers' + } + + AfterAll { + Remove-TestSandbox -Sandbox $sandbox + } + + It 'Should return the path of an item in the sandbox on the administrative share of its drive' { + $path = Join-Path -Path $sandbox -ChildPath 'Folder\File.txt' + + ConvertTo-TestAdminSharePath -Sandbox $sandbox -Path $path | + Should -Be ('\\localhost\' + $path.Substring(0, 1) + '$' + $path.Substring(2)) + } + + It 'Should reach the same item through the administrative share' -Skip:(-not $canUseAdminShare) { + $file = New-TestSandboxItem -Sandbox $sandbox -Name 'AdminShare' + + Get-Content -LiteralPath (ConvertTo-TestAdminSharePath -Sandbox $sandbox -Path $file) | Should -Be 'AdminShare' + } + + It 'Should refuse a path outside the sandbox' { + { ConvertTo-TestAdminSharePath -Sandbox $sandbox -Path "$sandbox-Other\File.txt" } | + Should -Throw -ExpectedMessage 'Refusing to change*' + } + + # Only administrators can open the administrative shares. + It 'Should not offer the administrative share without elevation' -Skip:$isElevated { + Test-AdminShareAvailable | Should -BeFalse + } + } } diff --git a/Tests/TestHelpers.psm1 b/Tests/TestHelpers.psm1 index 6a15057..89c7ab9 100644 --- a/Tests/TestHelpers.psm1 +++ b/Tests/TestHelpers.psm1 @@ -381,6 +381,47 @@ function Test-PrivilegeHeld { Where-Object -Property Name -EQ -Value $Name) } +function Test-AdminShareAvailable { + <# + .SYNOPSIS + Returns $true when the process can open the administrative share of the drive of the sandboxes, such as + \\localhost\C$, which Windows treats as a network path and as another volume. Only administrators can. + #> + [CmdletBinding()] + [OutputType([bool])] + param () + + $drive = [IO.Path]::GetTempPath().Substring(0, 1) + (Test-IsElevated) -and [bool] (Test-Path -LiteralPath ('\\localhost\{0}$\' -f $drive) -ErrorAction SilentlyContinue) +} + +function ConvertTo-TestAdminSharePath { + <# + .SYNOPSIS + Returns the path of an item in a sandbox on the administrative share of its drive, such as + \\localhost\C$\Users\...\File.txt. It checks the local path with Assert-TestSandboxPath first, so that a test + reaches only items of its sandbox through the share. + .PARAMETER Sandbox + The sandbox folder that New-TestSandbox returned. + .PARAMETER Path + The full local path of the item. + #> + [CmdletBinding()] + [OutputType([string])] + param ( + [Parameter(Mandatory)] + [string] + $Sandbox, + + [Parameter(Mandatory)] + [string] + $Path + ) + + Assert-TestSandboxPath -Sandbox $Sandbox -Path $Path + '\\localhost\{0}${1}' -f $Path.Substring(0, 1), $Path.Substring(2) +} + Export-ModuleMember -Function New-TestSandbox, Assert-TestSandboxPath, Remove-TestSandbox, New-TestSandboxItem, Block-TestReadPermission, Block-TestWritePermission, Add-TestDenyRule, Set-TestOwner, Test-IsElevated, - Test-PrivilegeHeld + Test-PrivilegeHeld, Test-AdminShareAvailable, ConvertTo-TestAdminSharePath