From 452213c833008bec139f571c238aad5857c70550 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 2 Apr 2024 09:42:40 -0700 Subject: [PATCH] Fix `Test-Path -IsValid` to check for invalid path and filename characters (#21358) --- .../namespaces/FileSystemProvider.cs | 15 +++++++ .../Test-Path.Tests.ps1 | 42 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 9337b466805..372e106ce61 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -1080,6 +1080,21 @@ protected override bool IsValidPath(string path) } } + // .NET introduced a change where invalid characters are accepted https://learn.microsoft.com/en-us/dotnet/core/compatibility/2.1#path-apis-dont-throw-an-exception-for-invalid-characters + // We need to check for invalid characters ourselves. `Path.GetInvalidFileNameChars()` is a supserset of `Path.GetInvalidPathChars()` + + // Remove drive root first + string pathWithoutDriveRoot = path.Substring(Path.GetPathRoot(path).Length); + char[] invalidFileChars = Path.GetInvalidFileNameChars(); + + foreach (string segment in pathWithoutDriveRoot.Split(Path.DirectorySeparatorChar)) + { + if (segment.IndexOfAny(invalidFileChars) != -1) + { + return false; + } + } + return true; } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Test-Path.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Test-Path.Tests.ps1 index edad763fe71..a2619877f6d 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Test-Path.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Test-Path.Tests.ps1 @@ -110,6 +110,48 @@ Describe "Test-Path" -Tags "CI" { Test-Path -Path $badPath -IsValid | Should -BeFalse } + It 'Windows paths should be valid: ' -skip:(!$IsWindows) -TestCases @( + @{ path = "C:\Program Files" } + @{ path = "C:\Program Files (x86)\" } + @{ path = "filesystem::z:\foo" } + @{ path = "filesystem::z:\foo\" } + @{ path = "variable::psversiontable" } + @{ path = "c:\windows\cmd.exe:test" } + ) { + param($path) + Test-Path -Path $path -IsValid | Should -BeTrue + } + + It 'Windows paths should be inavlid: ' -Skip:(!$IsWindows) -TestCases @( + @{ variant = "wildcard"; path = "C:\Program Files\foo*" } + @{ variant = "pipe symbol"; path = "C:\Program Files|p\foo" } + @{ variant = "null char"; path = "C:\Win`u{0000}dows\System32" } + ) { + param($path) + Test-Path -Path $path -IsValid | Should -BeFalse + } + + It 'Unix paths should be valid: ' -Skip:($IsWindows) -TestCases @( + @{ path = "/usr/bin" } + @{ path = "/usr/bin/" } + @{ path = "filesystem::/usr/bin" } + @{ path = "filesystem::/usr/bin/" } + @{ path = "variable::psversiontable" } + @{ path = "/usr/bi*n/test" } + @{ path = "/usr/bin/tes*t" } + ) { + param($path) + Test-Path -Path $path -IsValid | Should -BeTrue + } + + It 'Unix paths should be invalid: ' -Skip:($IsWindows) -TestCases @( + @{ variant = "null in path"; path = "/usr/bi`u{0000}n/test" } + @{ variant = "null in filename"; path = "/usr/bin/t`u{0000}est" } + ) { + param($path) + Test-Path -Path $path -IsValid | Should -BeFalse + } + It "Should return true on paths containing spaces when the path is surrounded in quotes" { Test-Path -Path "/totally a valid/path" -IsValid | Should -BeTrue }