diff --git a/Engine/Commands/InvokeScriptAnalyzerCommand.cs b/Engine/Commands/InvokeScriptAnalyzerCommand.cs index cd04f621e..fb1272d04 100644 --- a/Engine/Commands/InvokeScriptAnalyzerCommand.cs +++ b/Engine/Commands/InvokeScriptAnalyzerCommand.cs @@ -10,6 +10,7 @@ // THE SOFTWARE. // +using System.Text.RegularExpressions; using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; using System; using System.Collections.Generic; @@ -272,7 +273,28 @@ private void AnalyzeFile(string filePath) IEnumerable funcDefAsts; // Use a List of KVP rather than dictionary, since for a script containing inline functions with same signature, keys clash - List> cmdInfoTable = new List>(); + List> cmdInfoTable = new List>(); + + //Check wild card input for the Include/ExcludeRules and create regex match patterns + List includeRegexList = new List(); + List excludeRegexList = new List(); + if (includeRule != null) + { + foreach (string rule in includeRule) + { + Regex includeRegex = new Regex(String.Format("^{0}$", Regex.Escape(rule).Replace(@"\*", ".*")), RegexOptions.IgnoreCase); + includeRegexList.Add(includeRegex); + } + } + if (excludeRule != null) + { + foreach (string rule in excludeRule) + { + Regex excludeRegex = new Regex(String.Format("^{0}$", Regex.Escape(rule).Replace(@"\*", ".*")), RegexOptions.IgnoreCase); + excludeRegexList.Add(excludeRegex); + } + } + //Parse the file if (File.Exists(filePath)) @@ -316,12 +338,30 @@ private void AnalyzeFile(string filePath) #region Run ScriptRules //Trim down to the leaf element of the filePath and pass it to Diagnostic Record string fileName = System.IO.Path.GetFileName(filePath); + if (ScriptAnalyzer.Instance.ScriptRules != null) { foreach (IScriptRule scriptRule in ScriptAnalyzer.Instance.ScriptRules) { - if ((includeRule == null || includeRule.Contains(scriptRule.GetName(), StringComparer.OrdinalIgnoreCase)) && - (excludeRule == null || !excludeRule.Contains(scriptRule.GetName(), StringComparer.OrdinalIgnoreCase))) + bool includeRegexMatch = false; + bool excludeRegexMatch = false; + foreach (Regex include in includeRegexList) + { + if (include.IsMatch(scriptRule.GetName())) + { + includeRegexMatch = true; + break; + } + } + foreach (Regex exclude in excludeRegexList) + { + if (exclude.IsMatch(scriptRule.GetName())) + { + excludeRegexMatch = true; + break; + } + } + if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch)) { WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, scriptRule.GetName())); @@ -334,7 +374,6 @@ private void AnalyzeFile(string filePath) catch (Exception scriptRuleException) { WriteError(new ErrorRecord(scriptRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, filePath)); - continue; } } } @@ -379,8 +418,25 @@ private void AnalyzeFile(string filePath) { foreach (ICommandRule commandRule in ScriptAnalyzer.Instance.CommandRules) { - if ((includeRule == null || includeRule.Contains(commandRule.GetName(), StringComparer.OrdinalIgnoreCase)) && - (excludeRule == null || !excludeRule.Contains(commandRule.GetName(), StringComparer.OrdinalIgnoreCase))) + bool includeRegexMatch = false; + bool excludeRegexMatch = false; + foreach (Regex include in includeRegexList) + { + if (include.IsMatch(commandRule.GetName())) + { + includeRegexMatch = true; + break; + } + } + foreach (Regex exclude in excludeRegexList) + { + if (exclude.IsMatch(commandRule.GetName())) + { + excludeRegexMatch = true; + break; + } + } + if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch)) { foreach (KeyValuePair commandInfo in cmdInfoTable) { @@ -395,7 +451,6 @@ private void AnalyzeFile(string filePath) catch (Exception commandRuleException) { WriteError(new ErrorRecord(commandRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, fileName)); - continue; } } } @@ -410,8 +465,25 @@ private void AnalyzeFile(string filePath) { foreach (ITokenRule tokenRule in ScriptAnalyzer.Instance.TokenRules) { - if ((includeRule == null || includeRule.Contains(tokenRule.GetName(), StringComparer.OrdinalIgnoreCase)) && - (excludeRule == null || !excludeRule.Contains(tokenRule.GetName(), StringComparer.OrdinalIgnoreCase))) + bool includeRegexMatch = false; + bool excludeRegexMatch = false; + foreach (Regex include in includeRegexList) + { + if (include.IsMatch(tokenRule.GetName())) + { + includeRegexMatch = true; + break; + } + } + foreach (Regex exclude in excludeRegexList) + { + if (exclude.IsMatch(tokenRule.GetName())) + { + excludeRegexMatch = true; + break; + } + } + if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch)) { WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, tokenRule.GetName())); @@ -424,7 +496,6 @@ private void AnalyzeFile(string filePath) catch (Exception tokenRuleException) { WriteError(new ErrorRecord(tokenRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, fileName)); - continue; } } } @@ -438,8 +509,25 @@ private void AnalyzeFile(string filePath) // Run DSC Class rule foreach (IDSCResourceRule dscResourceRule in ScriptAnalyzer.Instance.DSCResourceRules) { - if ((includeRule == null || includeRule.Contains(dscResourceRule.GetName(), StringComparer.OrdinalIgnoreCase)) && - (excludeRule == null || !excludeRule.Contains(dscResourceRule.GetName(), StringComparer.OrdinalIgnoreCase))) + bool includeRegexMatch = false; + bool excludeRegexMatch = false; + foreach (Regex include in includeRegexList) + { + if (include.IsMatch(dscResourceRule.GetName())) + { + includeRegexMatch = true; + break; + } + } + foreach (Regex exclude in excludeRegexList) + { + if (exclude.IsMatch(dscResourceRule.GetName())) + { + excludeRegexMatch = true; + break; + } + } + if ((includeRule == null || includeRegexMatch) && (excludeRule == null || excludeRegexMatch)) { WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, dscResourceRule.GetName())); @@ -452,7 +540,6 @@ private void AnalyzeFile(string filePath) catch (Exception dscResourceRuleException) { WriteError(new ErrorRecord(dscResourceRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, filePath)); - continue; } } } @@ -480,8 +567,24 @@ private void AnalyzeFile(string filePath) // Run all DSC Rules foreach (IDSCResourceRule dscResourceRule in ScriptAnalyzer.Instance.DSCResourceRules) { - if ((includeRule == null || includeRule.Contains(dscResourceRule.GetName(), StringComparer.OrdinalIgnoreCase)) && - (excludeRule == null || !excludeRule.Contains(dscResourceRule.GetName(), StringComparer.OrdinalIgnoreCase))) + bool includeRegexMatch = false; + bool excludeRegexMatch = false; + foreach (Regex include in includeRegexList) + { + if (include.IsMatch(dscResourceRule.GetName())) + { + includeRegexMatch = true; + break; + } + } + foreach (Regex exclude in excludeRegexList) + { + if (exclude.IsMatch(dscResourceRule.GetName())) + { + excludeRegexMatch = true; + } + } + if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch)) { WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, dscResourceRule.GetName())); @@ -494,7 +597,6 @@ private void AnalyzeFile(string filePath) catch (Exception dscResourceRuleException) { WriteError(new ErrorRecord(dscResourceRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, filePath)); - continue; } } } diff --git a/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 b/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 index 53ddb9a02..9cc6ccdbd 100644 --- a/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 +++ b/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 @@ -3,6 +3,8 @@ $sa = Get-Command Invoke-ScriptAnalyzer $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $singularNouns = "PSUseSingularNouns" $rules = $singularNouns, "PSUseApprovedVerbs" +$avoidRules = "PSAvoid*" +$useRules = "PSUse*" Describe "Test available parameters" { $params = $sa.Parameters @@ -103,6 +105,12 @@ Describe "Test ExcludeRule" { } } + Context "Support wild card" { + It "supports wild card exclusions of input rules"{ + $excludeWildCard = Invoke-ScriptAnalyzer $directory\..\Rules\BadCmdlet.ps1 -ExcludeRule $avoidRules | Where-Object {$_.RuleName -match $avoidRules} + } + } + } Describe "Test IncludeRule" { @@ -124,6 +132,18 @@ Describe "Test IncludeRule" { $wrongInclude.Count | Should Be 0 } } + + Context "IncludeRule supports wild card" { + It "includes 1 wildcard rule"{ + $includeWildcard = Invoke-ScriptAnalyzer $directory\..\Rules\BadCmdlet.ps1 -IncludeRule $avoidRules + $includeWildcard.Count | Should be 5 + } + + it "includes 2 wildcardrules" { + $includeWildcard = Invoke-ScriptAnalyzer $directory\..\Rules\BadCmdlet.ps1 -IncludeRule $avoidRules, $useRules + $includeWildcard.Count | Should be 7 + } + } } Describe "Test Exclude And Include" {