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.

363 lines
16 KiB

<#
Tests the shared helpers in TestHelpers.psm1, which keep the tests that change files, links, and security
descriptors inside their sandbox folders. CI runs every *.Tests.ps1 file of this folder.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Pester shares variables between blocks.'
)]
param ()
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
BeforeDiscovery {
Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force
# Assigning an owner other than the user or one of its groups needs the Restore privilege.
$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
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
}
BeforeAll {
Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force
}
AfterAll {
Remove-Module -Name TestHelpers -Force -ErrorAction SilentlyContinue
}
Describe 'Test helpers' {
Context 'New-TestSandbox' {
BeforeAll {
$sandbox = New-TestSandbox -Name 'Helpers'
}
AfterAll {
Remove-TestSandbox -Sandbox $sandbox
}
It 'Should create an empty folder below $env:TEMP\NTFSSecurity.Tests' {
$sandbox | Should -Exist
Get-ChildItem -LiteralPath $sandbox -Force | Should -BeNullOrEmpty
$expectedParent = [IO.Path]::GetFullPath((Join-Path -Path ([IO.Path]::GetTempPath()) -ChildPath 'NTFSSecurity.Tests'))
Split-Path -Path $sandbox -Parent | Should -Be $expectedParent
Split-Path -Path $sandbox -Leaf | Should -BeLike 'Helpers-*'
}
}
Context 'Assert-TestSandboxPath' {
BeforeAll {
$sandbox = New-TestSandbox -Name 'Helpers'
Push-Location -LiteralPath $sandbox
}
AfterAll {
Pop-Location
Remove-TestSandbox -Sandbox $sandbox
}
It 'Should accept a full path inside the sandbox' {
{ Assert-TestSandboxPath -Sandbox $sandbox -Path (Join-Path -Path $sandbox -ChildPath 'Folder\File.txt') } |
Should -Not -Throw
}
It 'Should resolve a relative path against the current location' {
{ Assert-TestSandboxPath -Sandbox $sandbox -Path '.\File.txt', 'Folder\File.txt' } | Should -Not -Throw
}
It 'Should reject <_>' -ForEach @('..', '..\Other', 'C:\Windows', '\Windows') {
{ Assert-TestSandboxPath -Sandbox $sandbox -Path $_ } | Should -Throw -ExpectedMessage 'Refusing to change*'
}
It 'Should reject a sibling folder whose name starts with the name of the sandbox' {
{ Assert-TestSandboxPath -Sandbox $sandbox -Path "$sandbox-Other\File.txt" } |
Should -Throw -ExpectedMessage 'Refusing to change*'
}
It 'Should reject a sandbox that New-TestSandbox did not create' {
{ Assert-TestSandboxPath -Sandbox $env:TEMP -Path (Join-Path -Path $env:TEMP -ChildPath 'File.txt') } |
Should -Throw -ExpectedMessage '*is not a test sandbox*'
}
It 'Should reject a path below a link, which can point outside the sandbox' {
$otherSandbox = New-TestSandbox -Name 'Helpers'
try {
$link = Join-Path -Path $sandbox -ChildPath 'Link'
Assert-TestSandboxPath -Sandbox $sandbox -Path $link
New-Item -ItemType Junction -Path $link -Value $otherSandbox | Out-Null
{ Assert-TestSandboxPath -Sandbox $sandbox -Path (Join-Path -Path $link -ChildPath 'File.txt') } |
Should -Throw -ExpectedMessage '*is a link*'
}
finally {
Remove-TestSandbox -Sandbox $otherSandbox
}
}
}
Context 'Remove-TestSandbox' {
BeforeAll {
$sandbox = New-TestSandbox -Name 'Helpers'
$otherSandbox = New-TestSandbox -Name 'Helpers'
$target = Join-Path -Path $otherSandbox -ChildPath 'Target'
$link = Join-Path -Path $sandbox -ChildPath 'Link'
$locked = Join-Path -Path $sandbox -ChildPath 'Locked'
Assert-TestSandboxPath -Sandbox $otherSandbox -Path $target
New-Item -ItemType Directory -Path $target | Out-Null
Set-Content -LiteralPath (Join-Path -Path $target -ChildPath 'Keep.txt') -Value 'Keep'
# An explicit entry that a reset through the link would remove
$targetAcl = Get-Acl -LiteralPath $target
$targetAcl.AddAccessRule((New-Object -TypeName 'System.Security.AccessControl.FileSystemAccessRule' -ArgumentList (
(New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList 'S-1-1-0'),
[System.Security.AccessControl.FileSystemRights]::ReadData, [System.Security.AccessControl.AccessControlType]::Allow
)))
Set-Acl -LiteralPath $target -AclObject $targetAcl
$targetSddl = (Get-Acl -LiteralPath $target).Sddl
Assert-TestSandboxPath -Sandbox $sandbox -Path $link, $locked
New-Item -ItemType Junction -Path $link -Value $target | Out-Null
New-Item -ItemType Directory -Path $locked | Out-Null
Set-Content -LiteralPath (Join-Path -Path $locked -ChildPath 'File.txt') -Value 'Locked'
# An empty, protected DACL that grants nobody access, as some tests leave behind
& icacls.exe $locked /inheritance:r *> $null
Remove-TestSandbox -Sandbox $sandbox
}
AfterAll {
Remove-TestSandbox -Sandbox $otherSandbox
}
It 'Should remove the sandbox, even with a folder that denies access' {
$sandbox | Should -Not -Exist
}
It 'Should remove a junction without removing the files of its target' {
Join-Path -Path $target -ChildPath 'Keep.txt' | Should -Exist
}
It 'Should not change the ACL of the target of a junction' {
(Get-Acl -LiteralPath $target).Sddl | Should -BeExactly $targetSddl
}
# Before 5.0.0-rc6, the teardown couldn't remove paths longer than 260 characters in Windows PowerShell (#110).
It 'Should remove a sandbox with a path longer than 260 characters' {
$longSandbox = New-TestSandbox -Name 'Helpers'
$long = Join-Path -Path $longSandbox -ChildPath (('A' * 100), ('B' * 100), ('C' * 100) -join '\')
Assert-TestSandboxPath -Sandbox $longSandbox -Path $long
# The \\?\ prefix lets Windows PowerShell create the path.
[IO.Directory]::CreateDirectory('\\?\' + $long) | Out-Null
[IO.File]::WriteAllText(('\\?\' + $long + '\File.txt'), 'Long')
$long.Length | Should -BeGreaterThan 260
Remove-TestSandbox -Sandbox $longSandbox
$longSandbox | Should -Not -Exist
}
# Before 5.0.0-rc6, an AfterAll after a failed setup stopped with a binding error that hid the error of the setup.
It 'Should do nothing for a sandbox that a failed setup did not create: <_>' -ForEach @('$null', 'empty string') {
$value = if ($_ -eq '$null') { $null } else { '' }
{ Remove-TestSandbox -Sandbox $value } | Should -Not -Throw
}
}
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
Context 'Set-TestOwner' {
BeforeAll {
$sandbox = New-TestSandbox -Name 'Helpers'
$trustedInstaller = 'S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464'
}
AfterAll {
Remove-TestSandbox -Sandbox $sandbox
}
It 'Should make the account the owner of an item in the sandbox' -Skip:(-not $canAssignAnyOwner) {
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'Owner'
Set-TestOwner -Sandbox $sandbox -Path $file -Sid $trustedInstaller
(Get-Acl -LiteralPath $file).GetOwner([System.Security.Principal.SecurityIdentifier]).Value |
Should -Be $trustedInstaller
}
It 'Should refuse an item outside the sandbox' {
{ Set-TestOwner -Sandbox $sandbox -Path "$sandbox-Other\File.txt" -Sid $trustedInstaller } |
Should -Throw -ExpectedMessage 'Refusing to change*'
}
# icacls reports a failure on stderr, which Windows PowerShell turns into a terminating error of its own when
# the caller uses -ErrorAction Stop.
It 'Should throw its own error when icacls fails, also with -ErrorAction Stop' {
$missing = Join-Path -Path $sandbox -ChildPath 'Missing.txt'
{ Set-TestOwner -Sandbox $sandbox -Path $missing -Sid $trustedInstaller -ErrorAction Stop } |
Should -Throw -ExpectedMessage 'icacls could not make*'
}
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
}
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
Context 'Add-TestDenyRule' {
BeforeAll {
$sandbox = New-TestSandbox -Name 'Helpers'
}
AfterAll {
Remove-TestSandbox -Sandbox $sandbox
}
It 'Should add a deny entry to an item in the sandbox' {
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'Deny'
Add-TestDenyRule -Sandbox $sandbox -Path $file -Rights @{ 'S-1-5-32-546' = 'ReadData' }
$rules = @((Get-Acl -LiteralPath $file).GetAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier]) |
Where-Object -FilterScript { $_.IdentityReference.Value -eq 'S-1-5-32-546' })
$rules | Should -HaveCount 1
$rules[0].AccessControlType | Should -Be 'Deny'
}
# Set-Acl also writes the audit section of an item whose DACL is protected, which fails without the Security
# privilege.
It 'Should add a deny entry to an item whose DACL is protected' {
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'Protected'
& icacls.exe $file /inheritance:d *> $null
$LASTEXITCODE | Should -Be 0
Add-TestDenyRule -Sandbox $sandbox -Path $file -Rights @{ 'S-1-5-32-546' = 'ReadData' }
$acl = Get-Acl -LiteralPath $file
$acl.AreAccessRulesProtected | Should -BeTrue
$rules = @($acl.GetAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier]) |
Where-Object -FilterScript { $_.IdentityReference.Value -eq 'S-1-5-32-546' })
$rules | Should -HaveCount 1
$rules[0].AccessControlType | Should -Be 'Deny'
}
# With the Security privilege, Set-Acl writes all sections, so the audit entries of the item would be lost.
It 'Should keep the audit entries of the item' -Skip:(-not $holdsSecurityPrivilege) {
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'Audited'
$auditAcl = Get-Acl -LiteralPath $file -Audit
$auditAcl.AddAuditRule((New-Object -TypeName 'System.Security.AccessControl.FileSystemAuditRule' -ArgumentList (
(New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList 'S-1-1-0'),
[System.Security.AccessControl.FileSystemRights]::Delete,
[System.Security.AccessControl.AuditFlags]::Success
)))
Set-Acl -LiteralPath $file -AclObject $auditAcl
Add-TestDenyRule -Sandbox $sandbox -Path $file -Rights @{ 'S-1-5-32-546' = 'ReadData' }
$auditRules = @((Get-Acl -LiteralPath $file -Audit).GetAuditRules($true, $false, [System.Security.Principal.SecurityIdentifier]))
$auditRules | Should -HaveCount 1
$auditRules[0].IdentityReference.Value | Should -Be 'S-1-1-0'
}
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
It 'Should refuse an item outside the sandbox' {
{ Add-TestDenyRule -Sandbox $sandbox -Path "$sandbox-Other\File.txt" -Rights @{ 'S-1-5-32-546' = 'ReadData' } } |
Should -Throw -ExpectedMessage 'Refusing to change*'
}
}
Context 'Set-TestNullDacl' {
BeforeAll {
$sandbox = New-TestSandbox -Name 'Helpers'
}
AfterAll {
Remove-TestSandbox -Sandbox $sandbox
}
It 'Should replace the DACL of an item in the sandbox with a protected NULL DACL' {
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'NullDacl'
$before = [System.Security.AccessControl.RawSecurityDescriptor]::new((Get-Acl -LiteralPath $file).GetSecurityDescriptorBinaryForm(), 0)
$before.ControlFlags.HasFlag([System.Security.AccessControl.ControlFlags]::DiscretionaryAclPresent) | Should -BeTrue
Set-TestNullDacl -Sandbox $sandbox -Path $file
$after = [System.Security.AccessControl.RawSecurityDescriptor]::new((Get-Acl -LiteralPath $file).GetSecurityDescriptorBinaryForm(), 0)
$after.ControlFlags.HasFlag([System.Security.AccessControl.ControlFlags]::DiscretionaryAclPresent) | Should -BeFalse
$after.ControlFlags.HasFlag([System.Security.AccessControl.ControlFlags]::DiscretionaryAclProtected) | Should -BeTrue
}
It 'Should refuse an item outside the sandbox' {
{ Set-TestNullDacl -Sandbox $sandbox -Path "$sandbox-Other\File.txt" } |
Should -Throw -ExpectedMessage 'Refusing to change*'
}
It 'Should throw its own error when Windows refuses' {
$missing = Join-Path -Path $sandbox -ChildPath 'Missing.txt'
{ Set-TestNullDacl -Sandbox $sandbox -Path $missing } |
Should -Throw -ExpectedMessage 'SetNamedSecurityInfo could not set a NULL DACL*'
}
}
Context 'Test-IsElevated and Test-PrivilegeHeld' {
It 'Should tell whether the process is elevated' {
Test-IsElevated | Should -BeOfType [bool]
}
It 'Should find SeChangeNotifyPrivilege, which every access token holds' {
Test-PrivilegeHeld -Name 'SeChangeNotifyPrivilege' | Should -BeTrue
}
It 'Should not find a privilege that does not exist' {
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
}
}
fix: pass on what a later command raises, and return every item for -Filter *.* A throw in a later command, or an error with -ErrorAction Stop, reaches a cmdlet through its Write call as an ordinary exception. The catch for the failures of an item reported it as the error of that item and went on, so that Remove-Item2 -PassThru removed the next item after a throw, and the caller never saw the exception. The earlier check found only the end of the pipeline and a break or continue. BaseCmdlet now notes the exception that its WriteObject, WriteVerbose, and WriteDebug raised, and every catch that can enclose a write passes it on; Get-DiskSpace writes outside its try. Set-NTFSSecurityDescriptor and Get-FileHash2 also caught it at a verbose message. Get-ChildItem2 -Filter *.* returns every item, as Get-ChildItem does. The cmdlet compared each name with the pattern again and dropped the items without a dot, most folders among them; the dot stays an ordinary character in other patterns. The failed lookup of InheritedFrom frees its native buffer. The help paragraph of -Filter has no pair of asterisks, which platyPS turns into emphasis, and the page has an example for *.*. Review of the independent pass: the restored-owner test asserts that a plain write is denied, the drive-mapping helper has guard tests and takes letters that the no-volume tests do not, and the pipeline tests cover a throw, an error with -ErrorAction Stop, and the verbose and debug streams for every cmdlet that can reach the code. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
3 weeks ago
# subst maps a letter for the whole logon session, so the guard has to stop before it runs, for every configuration.
Context 'New-TestDriveMapping and Remove-TestDriveMapping' {
BeforeAll {
$sandbox = New-TestSandbox -Name 'Helpers'
}
AfterAll {
Remove-TestSandbox -Sandbox $sandbox
}
It 'Should refuse a folder outside the sandbox before it maps anything' {
{ New-TestDriveMapping -Sandbox $sandbox -Path "$sandbox-Other\Folder" } |
Should -Throw -ExpectedMessage 'Refusing to change*'
}
It 'Should refuse a value that is not the root of a drive' {
{ Remove-TestDriveMapping -Root 'C:\Windows' } |
Should -Throw -ErrorId 'ParameterArgumentValidationError,Remove-TestDriveMapping'
}
}
}