Skip to content

Commit 2545bfa

Browse files
committed
Merge pull request #47 from PowerShell/wildcard
Add wildcard support when include/exclude rules
2 parents 9cb7241 + 42b28c8 commit 2545bfa

File tree

2 files changed

+138
-16
lines changed

2 files changed

+138
-16
lines changed

Engine/Commands/InvokeScriptAnalyzerCommand.cs

Lines changed: 118 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// THE SOFTWARE.
1111
//
1212

13+
using System.Text.RegularExpressions;
1314
using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic;
1415
using System;
1516
using System.Collections.Generic;
@@ -272,7 +273,28 @@ private void AnalyzeFile(string filePath)
272273
IEnumerable<Ast> funcDefAsts;
273274

274275
// Use a List of KVP rather than dictionary, since for a script containing inline functions with same signature, keys clash
275-
List<KeyValuePair<CommandInfo, IScriptExtent>> cmdInfoTable = new List<KeyValuePair<CommandInfo, IScriptExtent>>();
276+
List<KeyValuePair<CommandInfo, IScriptExtent>> cmdInfoTable = new List<KeyValuePair<CommandInfo, IScriptExtent>>();
277+
278+
//Check wild card input for the Include/ExcludeRules and create regex match patterns
279+
List<Regex> includeRegexList = new List<Regex>();
280+
List<Regex> excludeRegexList = new List<Regex>();
281+
if (includeRule != null)
282+
{
283+
foreach (string rule in includeRule)
284+
{
285+
Regex includeRegex = new Regex(String.Format("^{0}$", Regex.Escape(rule).Replace(@"\*", ".*")), RegexOptions.IgnoreCase);
286+
includeRegexList.Add(includeRegex);
287+
}
288+
}
289+
if (excludeRule != null)
290+
{
291+
foreach (string rule in excludeRule)
292+
{
293+
Regex excludeRegex = new Regex(String.Format("^{0}$", Regex.Escape(rule).Replace(@"\*", ".*")), RegexOptions.IgnoreCase);
294+
excludeRegexList.Add(excludeRegex);
295+
}
296+
}
297+
276298

277299
//Parse the file
278300
if (File.Exists(filePath))
@@ -316,12 +338,30 @@ private void AnalyzeFile(string filePath)
316338
#region Run ScriptRules
317339
//Trim down to the leaf element of the filePath and pass it to Diagnostic Record
318340
string fileName = System.IO.Path.GetFileName(filePath);
341+
319342
if (ScriptAnalyzer.Instance.ScriptRules != null)
320343
{
321344
foreach (IScriptRule scriptRule in ScriptAnalyzer.Instance.ScriptRules)
322345
{
323-
if ((includeRule == null || includeRule.Contains(scriptRule.GetName(), StringComparer.OrdinalIgnoreCase)) &&
324-
(excludeRule == null || !excludeRule.Contains(scriptRule.GetName(), StringComparer.OrdinalIgnoreCase)))
346+
bool includeRegexMatch = false;
347+
bool excludeRegexMatch = false;
348+
foreach (Regex include in includeRegexList)
349+
{
350+
if (include.IsMatch(scriptRule.GetName()))
351+
{
352+
includeRegexMatch = true;
353+
break;
354+
}
355+
}
356+
foreach (Regex exclude in excludeRegexList)
357+
{
358+
if (exclude.IsMatch(scriptRule.GetName()))
359+
{
360+
excludeRegexMatch = true;
361+
break;
362+
}
363+
}
364+
if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch))
325365
{
326366
WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, scriptRule.GetName()));
327367

@@ -334,7 +374,6 @@ private void AnalyzeFile(string filePath)
334374
catch (Exception scriptRuleException)
335375
{
336376
WriteError(new ErrorRecord(scriptRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, filePath));
337-
continue;
338377
}
339378
}
340379
}
@@ -379,8 +418,25 @@ private void AnalyzeFile(string filePath)
379418
{
380419
foreach (ICommandRule commandRule in ScriptAnalyzer.Instance.CommandRules)
381420
{
382-
if ((includeRule == null || includeRule.Contains(commandRule.GetName(), StringComparer.OrdinalIgnoreCase)) &&
383-
(excludeRule == null || !excludeRule.Contains(commandRule.GetName(), StringComparer.OrdinalIgnoreCase)))
421+
bool includeRegexMatch = false;
422+
bool excludeRegexMatch = false;
423+
foreach (Regex include in includeRegexList)
424+
{
425+
if (include.IsMatch(commandRule.GetName()))
426+
{
427+
includeRegexMatch = true;
428+
break;
429+
}
430+
}
431+
foreach (Regex exclude in excludeRegexList)
432+
{
433+
if (exclude.IsMatch(commandRule.GetName()))
434+
{
435+
excludeRegexMatch = true;
436+
break;
437+
}
438+
}
439+
if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch))
384440
{
385441
foreach (KeyValuePair<CommandInfo, IScriptExtent> commandInfo in cmdInfoTable)
386442
{
@@ -395,7 +451,6 @@ private void AnalyzeFile(string filePath)
395451
catch (Exception commandRuleException)
396452
{
397453
WriteError(new ErrorRecord(commandRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, fileName));
398-
continue;
399454
}
400455
}
401456
}
@@ -410,8 +465,25 @@ private void AnalyzeFile(string filePath)
410465
{
411466
foreach (ITokenRule tokenRule in ScriptAnalyzer.Instance.TokenRules)
412467
{
413-
if ((includeRule == null || includeRule.Contains(tokenRule.GetName(), StringComparer.OrdinalIgnoreCase)) &&
414-
(excludeRule == null || !excludeRule.Contains(tokenRule.GetName(), StringComparer.OrdinalIgnoreCase)))
468+
bool includeRegexMatch = false;
469+
bool excludeRegexMatch = false;
470+
foreach (Regex include in includeRegexList)
471+
{
472+
if (include.IsMatch(tokenRule.GetName()))
473+
{
474+
includeRegexMatch = true;
475+
break;
476+
}
477+
}
478+
foreach (Regex exclude in excludeRegexList)
479+
{
480+
if (exclude.IsMatch(tokenRule.GetName()))
481+
{
482+
excludeRegexMatch = true;
483+
break;
484+
}
485+
}
486+
if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch))
415487
{
416488
WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, tokenRule.GetName()));
417489

@@ -424,7 +496,6 @@ private void AnalyzeFile(string filePath)
424496
catch (Exception tokenRuleException)
425497
{
426498
WriteError(new ErrorRecord(tokenRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, fileName));
427-
continue;
428499
}
429500
}
430501
}
@@ -438,8 +509,25 @@ private void AnalyzeFile(string filePath)
438509
// Run DSC Class rule
439510
foreach (IDSCResourceRule dscResourceRule in ScriptAnalyzer.Instance.DSCResourceRules)
440511
{
441-
if ((includeRule == null || includeRule.Contains(dscResourceRule.GetName(), StringComparer.OrdinalIgnoreCase)) &&
442-
(excludeRule == null || !excludeRule.Contains(dscResourceRule.GetName(), StringComparer.OrdinalIgnoreCase)))
512+
bool includeRegexMatch = false;
513+
bool excludeRegexMatch = false;
514+
foreach (Regex include in includeRegexList)
515+
{
516+
if (include.IsMatch(dscResourceRule.GetName()))
517+
{
518+
includeRegexMatch = true;
519+
break;
520+
}
521+
}
522+
foreach (Regex exclude in excludeRegexList)
523+
{
524+
if (exclude.IsMatch(dscResourceRule.GetName()))
525+
{
526+
excludeRegexMatch = true;
527+
break;
528+
}
529+
}
530+
if ((includeRule == null || includeRegexMatch) && (excludeRule == null || excludeRegexMatch))
443531
{
444532
WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, dscResourceRule.GetName()));
445533

@@ -452,7 +540,6 @@ private void AnalyzeFile(string filePath)
452540
catch (Exception dscResourceRuleException)
453541
{
454542
WriteError(new ErrorRecord(dscResourceRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, filePath));
455-
continue;
456543
}
457544
}
458545
}
@@ -480,8 +567,24 @@ private void AnalyzeFile(string filePath)
480567
// Run all DSC Rules
481568
foreach (IDSCResourceRule dscResourceRule in ScriptAnalyzer.Instance.DSCResourceRules)
482569
{
483-
if ((includeRule == null || includeRule.Contains(dscResourceRule.GetName(), StringComparer.OrdinalIgnoreCase)) &&
484-
(excludeRule == null || !excludeRule.Contains(dscResourceRule.GetName(), StringComparer.OrdinalIgnoreCase)))
570+
bool includeRegexMatch = false;
571+
bool excludeRegexMatch = false;
572+
foreach (Regex include in includeRegexList)
573+
{
574+
if (include.IsMatch(dscResourceRule.GetName()))
575+
{
576+
includeRegexMatch = true;
577+
break;
578+
}
579+
}
580+
foreach (Regex exclude in excludeRegexList)
581+
{
582+
if (exclude.IsMatch(dscResourceRule.GetName()))
583+
{
584+
excludeRegexMatch = true;
585+
}
586+
}
587+
if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch))
485588
{
486589
WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, dscResourceRule.GetName()));
487590

@@ -494,7 +597,6 @@ private void AnalyzeFile(string filePath)
494597
catch (Exception dscResourceRuleException)
495598
{
496599
WriteError(new ErrorRecord(dscResourceRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, filePath));
497-
continue;
498600
}
499601
}
500602
}

Tests/Engine/InvokeScriptAnalyzer.tests.ps1

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ $sa = Get-Command Invoke-ScriptAnalyzer
33
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
44
$singularNouns = "PSUseSingularNouns"
55
$rules = $singularNouns, "PSUseApprovedVerbs"
6+
$avoidRules = "PSAvoid*"
7+
$useRules = "PSUse*"
68

79
Describe "Test available parameters" {
810
$params = $sa.Parameters
@@ -103,6 +105,12 @@ Describe "Test ExcludeRule" {
103105
}
104106
}
105107

108+
Context "Support wild card" {
109+
It "supports wild card exclusions of input rules"{
110+
$excludeWildCard = Invoke-ScriptAnalyzer $directory\..\Rules\BadCmdlet.ps1 -ExcludeRule $avoidRules | Where-Object {$_.RuleName -match $avoidRules}
111+
}
112+
}
113+
106114
}
107115

108116
Describe "Test IncludeRule" {
@@ -124,6 +132,18 @@ Describe "Test IncludeRule" {
124132
$wrongInclude.Count | Should Be 0
125133
}
126134
}
135+
136+
Context "IncludeRule supports wild card" {
137+
It "includes 1 wildcard rule"{
138+
$includeWildcard = Invoke-ScriptAnalyzer $directory\..\Rules\BadCmdlet.ps1 -IncludeRule $avoidRules
139+
$includeWildcard.Count | Should be 5
140+
}
141+
142+
it "includes 2 wildcardrules" {
143+
$includeWildcard = Invoke-ScriptAnalyzer $directory\..\Rules\BadCmdlet.ps1 -IncludeRule $avoidRules, $useRules
144+
$includeWildcard.Count | Should be 7
145+
}
146+
}
127147
}
128148

129149
Describe "Test Exclude And Include" {

0 commit comments

Comments
 (0)