From 41e20d723da04f05e6b222a31575ce9a5088cd7f Mon Sep 17 00:00:00 2001 From: kwkam Date: Sat, 7 Apr 2018 23:33:24 +0800 Subject: [PATCH] [Feature] CommandSearcher: treat invalid wildcard path as literal Do not glob path with invalid wildcard pattern (eg. "./[.ps1"). Merge the path resolving code from GetNextFromPath into ResolvePSPath. Add test for previous change engine/regex: add MayBeWildcardPattern CommandSearcher: use MayBeWildcardPattern [Feature] Fix FunctionProvider test [Feature] Revert "[Feature] Fix FunctionProvider test" This reverts commit d7ab0d7d538df8943bf156f098866cbf7b38bc64. [Feature] Remove 'resolvedPath != null' [Feature] Redo efe93252c5 and b300130e59 [Feature] fixup Revert "[Feature] fixup" Revert "[Feature] Redo Rename MayBeWildcardPattern Fix ContainsValidWildcardPattern --- .../engine/CommandSearcher.cs | 2 +- .../engine/GetCommandCommand.cs | 4 +- .../engine/regex.cs | 64 +++++++++++++++++++ ...ster.Commands.Cmdlets.GetCommand.Tests.ps1 | 7 ++ 4 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandSearcher.cs b/src/System.Management.Automation/engine/CommandSearcher.cs index 3d6918dcf12..8de90766698 100644 --- a/src/System.Management.Automation/engine/CommandSearcher.cs +++ b/src/System.Management.Automation/engine/CommandSearcher.cs @@ -1142,7 +1142,7 @@ private string ResolvePSPath(string path) resolvedPath = GetNextLiteralPathThatExists(path, out provider); } - if (WildcardPattern.ContainsWildcardCharacters(path) && + if (WildcardPattern.ContainsValidWildcardPattern(path) && ((resolvedPath == null) || (provider == null))) { // Let PowerShell resolve relative path with wildcards. diff --git a/src/System.Management.Automation/engine/GetCommandCommand.cs b/src/System.Management.Automation/engine/GetCommandCommand.cs index 8414986a256..eb90f261eb7 100644 --- a/src/System.Management.Automation/engine/GetCommandCommand.cs +++ b/src/System.Management.Automation/engine/GetCommandCommand.cs @@ -57,7 +57,7 @@ public string[] Name { foreach (string commandName in value) { - if (WildcardPattern.ContainsWildcardCharacters(commandName)) + if (WildcardPattern.ContainsValidWildcardPattern(commandName)) { _nameContainsWildcard = true; break; @@ -746,7 +746,7 @@ private void AccumulateMatchingCommands(IEnumerable commandNames) moduleName = this.Module[0]; } - bool isPattern = WildcardPattern.ContainsWildcardCharacters(plainCommandName) || UseAbbreviationExpansion || UseFuzzyMatching; + bool isPattern = WildcardPattern.ContainsValidWildcardPattern(plainCommandName) || UseAbbreviationExpansion || UseFuzzyMatching; if (isPattern) { options |= SearchResolutionOptions.CommandNameIsPattern; diff --git a/src/System.Management.Automation/engine/regex.cs b/src/System.Management.Automation/engine/regex.cs index 238cf2cbc17..fbcca41ab5a 100644 --- a/src/System.Management.Automation/engine/regex.cs +++ b/src/System.Management.Automation/engine/regex.cs @@ -278,6 +278,70 @@ public static bool ContainsWildcardCharacters(string pattern) return result; } + /// FIXME + /// For invalid wildcard pattern, it currently only check if there is + /// unclosed bracket. Need to find a better solution and rewrite this. + /// + /// + /// Checks to see if the given string might contains wildcard pattern. + /// + /// + /// String which needs to be checked + /// + /// + /// true if the string does not contain invalid wildcard pattern, + /// false otherwise. + /// + /// + /// Currently string contains { '*', '?' } or both { '[', ']' } is + /// considered to be properly a wildcard pattern and + /// '`' is the escape character. + /// + internal static bool ContainsValidWildcardPattern(string pattern) + { + if (string.IsNullOrEmpty(pattern)) + { + return false; + } + + bool hasWildcard = false; + bool openBracket = false; + + for (int index = 0; index < pattern.Length; ++index) + { + switch (pattern[index]) + { + case '*': + case '?': + hasWildcard = true; + break; + + case '[': + hasWildcard = true; + openBracket = true; + break; + + case ']': + // ']' is wildcard only if '[' exists + // so we do not set hasWildcard here + openBracket = false; + break; + + // If it is an escape character then advance past + // the next character + case escapeChar: + ++index; + break; + } + } + + if (openBracket) { + return false; + } + + return hasWildcard; + } + /// /// Unescapes any escaped characters in the input string. /// diff --git a/test/powershell/Modules/Microsoft.PowerShell.Core/Pester.Commands.Cmdlets.GetCommand.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Core/Pester.Commands.Cmdlets.GetCommand.Tests.ps1 index d17df2af3df..ec0ba4b6ab7 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Core/Pester.Commands.Cmdlets.GetCommand.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Core/Pester.Commands.Cmdlets.GetCommand.Tests.ps1 @@ -7,11 +7,13 @@ Describe "Tests Get-Command with relative paths and wildcards" -Tag "CI" { # Create temporary EXE command files $file1 = Setup -f WildCardCommandA.exe -pass $file2 = Setup -f WildCardCommand[B].exe -pass + $file3 = Setup -f [.exe -pass #$null = New-Item -ItemType File -Path (Join-Path $TestDrive WildCardCommandA.exe) -ErrorAction Ignore #$null = New-Item -ItemType File -Path (Join-Path $TestDRive WildCardCommand[B].exe) -ErrorAction Ignore if ( $IsLinux -or $IsMacOS ) { /bin/chmod 777 "$file1" /bin/chmod 777 "$file2" + /bin/chmod 777 "$file3" } $commandInfo = Get-Command Get-Date -ShowCommandInfo } @@ -54,6 +56,11 @@ Describe "Tests Get-Command with relative paths and wildcards" -Tag "CI" { $result | Should -Not -BeNullOrEmpty $result | Should -Be WildCardCommand[B].exe + # This should find the file [.exe + $result = Get-Command -Name .\[.exe -Type Application + $result | Should -Not -BeNullOrEmpty + $result | Should -BeExactly [.exe + Pop-Location }