Skip to content

Commit 48e2535

Browse files
author
Kapil Borle
committed
Merge pull request #365 from PowerShell/FixCustomizedRulePathTrailingBackslash
Renames CustomizedRulePath parameter and modifies its behavior.
2 parents c48f74b + dbe3143 commit 48e2535

11 files changed

+336
-33
lines changed

Engine/Commands/GetScriptAnalyzerRuleCommand.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,25 @@ public class GetScriptAnalyzerRuleCommand : PSCmdlet, IOutputWriter
3333
[Parameter(Mandatory = false)]
3434
[ValidateNotNullOrEmpty]
3535
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
36-
public string[] CustomizedRulePath
36+
[Alias("CustomizedRulePath")]
37+
public string CustomRulePath
3738
{
38-
get { return customizedRulePath; }
39-
set { customizedRulePath = value; }
39+
get { return customRulePath; }
40+
set { customRulePath = value; }
4041
}
41-
private string[] customizedRulePath;
42+
private string customRulePath;
43+
44+
/// <summary>
45+
/// RecurseCustomRulePath: Find rules within subfolders under the path
46+
/// </summary>
47+
[Parameter(Mandatory = false)]
48+
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
49+
public SwitchParameter RecurseCustomRulePath
50+
{
51+
get { return recurseCustomRulePath; }
52+
set { recurseCustomRulePath = value; }
53+
}
54+
private bool recurseCustomRulePath;
4255

4356
/// <summary>
4457
/// Name: The name of a specific rule to list.
@@ -76,7 +89,9 @@ public string[] Severity
7689
/// </summary>
7790
protected override void BeginProcessing()
7891
{
79-
ScriptAnalyzer.Instance.Initialize(this, customizedRulePath);
92+
string[] rulePaths = Helper.ProcessCustomRulePaths(customRulePath,
93+
this.SessionState, recurseCustomRulePath);
94+
ScriptAnalyzer.Instance.Initialize(this, rulePaths);
8095
}
8196

8297
/// <summary>

Engine/Commands/InvokeScriptAnalyzerCommand.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,25 @@ public string ScriptDefinition
7373
[Parameter(Mandatory = false)]
7474
[ValidateNotNull]
7575
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
76-
public string[] CustomizedRulePath
76+
[Alias("CustomizedRulePath")]
77+
public string CustomRulePath
7778
{
78-
get { return customizedRulePath; }
79-
set { customizedRulePath = value; }
79+
get { return customRulePath; }
80+
set { customRulePath = value; }
8081
}
81-
private string[] customizedRulePath;
82+
private string customRulePath;
83+
84+
/// <summary>
85+
/// RecurseCustomRulePath: Find rules within subfolders under the path
86+
/// </summary>
87+
[Parameter(Mandatory = false)]
88+
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
89+
public SwitchParameter RecurseCustomRulePath
90+
{
91+
get { return recurseCustomRulePath; }
92+
set { recurseCustomRulePath = value; }
93+
}
94+
private bool recurseCustomRulePath;
8295

8396
/// <summary>
8497
/// ExcludeRule: Array of names of rules to be disabled.
@@ -164,9 +177,12 @@ public string Profile
164177
/// </summary>
165178
protected override void BeginProcessing()
166179
{
180+
string[] rulePaths = Helper.ProcessCustomRulePaths(customRulePath,
181+
this.SessionState, recurseCustomRulePath);
182+
167183
ScriptAnalyzer.Instance.Initialize(
168184
this,
169-
customizedRulePath,
185+
rulePaths,
170186
this.includeRule,
171187
this.excludeRule,
172188
this.severity,

Engine/Helper.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
using System;
1414
using System.Collections.Generic;
15+
using System.Collections.ObjectModel;
1516
using System.IO;
1617
using System.Linq;
1718
using System.Management.Automation;
@@ -983,9 +984,45 @@ public Tuple<List<SuppressedRecord>, List<DiagnosticRecord>> SuppressRule(string
983984
return result;
984985
}
985986

987+
public static string[] ProcessCustomRulePaths(string rulePath, SessionState sessionState, bool recurse = false)
988+
{
989+
//if directory is given, list all the psd1 files
990+
List<string> outPaths = new List<string>();
991+
if (rulePath == null)
992+
{
993+
return null;
994+
}
995+
try
996+
{
997+
Collection<PathInfo> pathInfo = sessionState.Path.GetResolvedPSPathFromPSPath(rulePath);
998+
foreach (PathInfo pinfo in pathInfo)
999+
{
1000+
string path = pinfo.Path;
1001+
if (Directory.Exists(path))
1002+
{
1003+
path = path.TrimEnd('\\');
1004+
if (recurse)
1005+
{
1006+
outPaths.AddRange(Directory.GetDirectories(pinfo.Path, "*", SearchOption.AllDirectories));
1007+
}
1008+
}
1009+
outPaths.Add(path);
1010+
}
1011+
return outPaths.ToArray();
1012+
}
1013+
catch (Exception ex)
1014+
{
1015+
// need to do this as the path validation takes place later in the hierarchy.
1016+
outPaths.Add(rulePath);
1017+
return outPaths.ToArray();
1018+
}
1019+
}
1020+
1021+
9861022
#endregion
9871023
}
9881024

1025+
9891026
internal class TupleComparer : IComparer<Tuple<int, int>>
9901027
{
9911028
public int Compare(Tuple<int, int> t1, Tuple<int, int> t2)

Engine/ScriptAnalyzer.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ internal void Initialize<TCmdlet>(
108108
{
109109
throw new ArgumentNullException("cmdlet");
110110
}
111-
111+
112112
this.Initialize(
113113
cmdlet,
114114
cmdlet.SessionState.Path,
@@ -188,7 +188,7 @@ private void Initialize(
188188
if (!String.IsNullOrWhiteSpace(profile))
189189
{
190190
try
191-
{
191+
{
192192
profile = path.GetResolvedPSPathFromPSPath(profile).First().Path;
193193
}
194194
catch
@@ -784,7 +784,7 @@ public Dictionary<string, List<string>> CheckRuleExtension(string[] path, PathIn
784784
// We have to identify the childPath is really a directory or just a module name.
785785
// You can also consider following two commands.
786786
// Get-ScriptAnalyzerRule -RuleExtension "ContosoAnalyzerRules"
787-
// Get-ScriptAnalyzerRule -RuleExtension "%USERPROFILE%\WindowsPowerShell\Modules\ContosoAnalyzerRules"
787+
// Get-ScriptAnalyzerRule -RuleExtension "%USERPROFILE%\WindowsPowerShell\Modules\ContosoAnalyzerRules"
788788
if (Path.GetDirectoryName(childPath) == string.Empty)
789789
{
790790
resolvedPath = childPath;
@@ -797,14 +797,14 @@ public Dictionary<string, List<string>> CheckRuleExtension(string[] path, PathIn
797797

798798
using (System.Management.Automation.PowerShell posh =
799799
System.Management.Automation.PowerShell.Create())
800-
{
800+
{
801801
posh.AddCommand("Get-Module").AddParameter("Name", resolvedPath).AddParameter("ListAvailable");
802802
PSModuleInfo moduleInfo = posh.Invoke<PSModuleInfo>().First();
803803

804804
// Adds original path, otherwise path.Except<string>(validModPaths) will fail.
805805
// It's possible that user can provide something like this:
806806
// "..\..\..\ScriptAnalyzer.UnitTest\modules\CommunityAnalyzerRules\CommunityAnalyzerRules.psd1"
807-
if (moduleInfo.ExportedFunctions.Count > 0) validModPaths.Add(childPath);
807+
if (moduleInfo.ExportedFunctions.Count > 0) validModPaths.Add(resolvedPath);
808808
}
809809
}
810810
catch

Tests/Engine/CustomizedRule.tests.ps1

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,83 @@ Describe "Test importing correct customized rules" {
5151
}
5252

5353
Context "Test Get-ScriptAnalyzer with customized rules" {
54-
It "will show the customized rule" {
54+
It "will show the custom rule" {
5555
$customizedRulePath = Get-ScriptAnalyzerRule -CustomizedRulePath $directory\samplerule\samplerule.psm1 | Where-Object {$_.RuleName -eq $measure}
5656
$customizedRulePath.Count | Should Be 1
5757
}
58-
58+
59+
It "will show the custom rule when given a rule folder path" {
60+
$customizedRulePath = Get-ScriptAnalyzerRule -CustomizedRulePath $directory\samplerule | Where-Object {$_.RuleName -eq $measure}
61+
$customizedRulePath.Count | Should Be 1
62+
}
63+
64+
if (!$testingLibraryUsage)
65+
{
66+
It "will show the custom rule when given a rule folder path with trailing backslash" {
67+
$customizedRulePath = Get-ScriptAnalyzerRule -CustomizedRulePath $directory\samplerule\ | Where-Object {$_.RuleName -eq $measure}
68+
$customizedRulePath.Count | Should Be 1
69+
}
70+
71+
It "will show the custom rules when given a glob" {
72+
$customizedRulePath = Get-ScriptAnalyzerRule -CustomizedRulePath $directory\samplerule\samplerule* | Where-Object {$_.RuleName -match $measure}
73+
$customizedRulePath.Count | Should be 4
74+
}
75+
76+
It "will show the custom rules when given recurse switch" {
77+
$customizedRulePath = Get-ScriptAnalyzerRule -RecurseCustomRulePath -CustomizedRulePath $directory\samplerule | Where-Object {$_.RuleName -eq $measure}
78+
$customizedRulePath.Count | Should be 3
79+
}
80+
81+
it "will show the custom rules when given glob with recurse switch" {
82+
$customizedRulePath = Get-ScriptAnalyzerRule -RecurseCustomRulePath -CustomizedRulePath $directory\samplerule\samplerule* | Where-Object {$_.RuleName -eq $measure}
83+
$customizedRulePath.Count | Should be 5
84+
}
85+
86+
it "will show the custom rules when given glob with recurse switch" {
87+
$customizedRulePath = Get-ScriptAnalyzerRule -RecurseCustomRulePath -CustomizedRulePath $directory\samplerule* | Where-Object {$_.RuleName -eq $measure}
88+
$customizedRulePath.Count | Should be 3
89+
}
90+
}
5991
}
6092

6193
Context "Test Invoke-ScriptAnalyzer with customized rules" {
62-
It "will show the customized rule in the results" {
94+
It "will show the custom rule in the results" {
6395
$customizedRulePath = Invoke-ScriptAnalyzer $directory\TestScript.ps1 -CustomizedRulePath $directory\samplerule\samplerule.psm1 | Where-Object {$_.Message -eq $message}
6496
$customizedRulePath.Count | Should Be 1
6597
}
98+
99+
It "will show the custom rule in the results when given a rule folder path" {
100+
$customizedRulePath = Invoke-ScriptAnalyzer $directory\TestScript.ps1 -CustomizedRulePath $directory\samplerule | Where-Object {$_.Message -eq $message}
101+
$customizedRulePath.Count | Should Be 1
102+
}
103+
104+
if (!$testingLibraryUsage)
105+
{
106+
It "will show the custom rule in the results when given a rule folder path with trailing backslash" {
107+
$customizedRulePath = Invoke-ScriptAnalyzer $directory\TestScript.ps1 -CustomizedRulePath $directory\samplerule\ | Where-Object {$_.Message -eq $message}
108+
$customizedRulePath.Count | Should Be 1
109+
}
110+
111+
It "will show the custom rules when given a glob" {
112+
$customizedRulePath = Invoke-ScriptAnalyzer $directory\TestScript.ps1 -CustomizedRulePath $directory\samplerule\samplerule* | Where-Object {$_.Message -eq $message}
113+
$customizedRulePath.Count | Should be 3
114+
}
115+
116+
It "will show the custom rules when given recurse switch" {
117+
$customizedRulePath = Invoke-ScriptAnalyzer $directory\TestScript.ps1 -RecurseCustomRulePath -CustomizedRulePath $directory\samplerule | Where-Object {$_.Message -eq $message}
118+
$customizedRulePath.Count | Should be 3
119+
}
120+
121+
it "will show the custom rules when given glob with recurse switch" {
122+
$customizedRulePath = Invoke-ScriptAnalyzer $directory\TestScript.ps1 -RecurseCustomRulePath -CustomizedRulePath $directory\samplerule\samplerule* | Where-Object {$_.Message -eq $message}
123+
$customizedRulePath.Count | Should be 4
124+
}
125+
126+
it "will show the custom rules when given glob with recurse switch" {
127+
$customizedRulePath = Invoke-ScriptAnalyzer $directory\TestScript.ps1 -RecurseCustomRulePath -CustomizedRulePath $directory\samplerule* | Where-Object {$_.Message -eq $message}
128+
$customizedRulePath.Count | Should be 3
129+
}
130+
}
66131
}
132+
}
67133

68-
}

Tests/Engine/GetScriptAnalyzerRule.tests.ps1

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@ Describe "Test available parameters" {
1919

2020
Context "RuleExtension parameters" {
2121
It "has a RuleExtension parameter" {
22-
$params.ContainsKey("CustomizedRulePath") | Should Be $true
22+
$params.ContainsKey("CustomRulePath") | Should Be $true
2323
}
2424

2525
It "accepts string array" {
26-
$params["CustomizedRulePath"].ParameterType.FullName | Should Be "System.String[]"
26+
$params["CustomRulePath"].ParameterType.FullName | Should Be "System.String"
2727
}
28+
29+
It "takes CustomizedRulePath parameter as an alias of CustomRulePath paramter" {
30+
$params.CustomRulePath.Aliases.Contains("CustomizedRulePath") | Should be $true
31+
}
2832
}
2933

3034
}

Tests/Engine/InvokeScriptAnalyzer.tests.ps1

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,30 @@ Describe "Test available parameters" {
3030
$params.ContainsKey("ScriptDefinition") | Should Be $true
3131
}
3232

33-
It "accepts string" {
33+
It "accepts string" {
3434
$params["ScriptDefinition"].ParameterType.FullName | Should Be "System.String"
3535
}
3636
}
3737

38-
Context "CustomizedRulePath parameters" {
39-
It "has a CustomizedRulePath parameter" {
40-
$params.ContainsKey("CustomizedRulePath") | Should Be $true
38+
Context "CustomRulePath parameters" {
39+
It "has a CustomRulePath parameter" {
40+
$params.ContainsKey("CustomRulePath") | Should Be $true
4141
}
4242

43-
It "accepts string array" {
44-
$params["CustomizedRulePath"].ParameterType.FullName | Should Be "System.String[]"
43+
It "accepts a string" {
44+
if ($testingLibraryUsage)
45+
{
46+
$params["CustomRulePath"].ParameterType.FullName | Should Be "System.String[]"
47+
}
48+
else
49+
{
50+
$params["CustomRulePath"].ParameterType.FullName | Should Be "System.String"
51+
}
4552
}
53+
54+
It "has a CustomizedRulePath alias"{
55+
$params.CustomRulePath.Aliases.Contains("CustomizedRulePath") | Should be $true
56+
}
4657
}
4758

4859
Context "IncludeRule parameters" {

Tests/Engine/LibraryUsage.tests.ps1

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ function Invoke-ScriptAnalyzer {
1616
[string] $ScriptDefinition,
1717

1818
[Parameter(Mandatory = $false)]
19-
[string[]] $CustomizedRulePath = $null,
19+
[Alias("CustomizedRulePath")]
20+
[string[]] $CustomRulePath = $null,
21+
22+
[Parameter(Mandatory = $false)]
23+
[switch] $RecurseCustomRulePath,
2024

2125
[Parameter(Mandatory=$false)]
2226
[string[]] $ExcludeRule = $null,
@@ -32,18 +36,28 @@ function Invoke-ScriptAnalyzer {
3236
[switch] $Recurse,
3337

3438
[Parameter(Mandatory = $false)]
35-
[switch] $SuppressedOnly
36-
)
39+
[switch] $SuppressedOnly,
3740

38-
$scriptAnalyzer = New-Object "Microsoft.Windows.PowerShell.ScriptAnalyzer.ScriptAnalyzer"
41+
[Parameter(Mandatory = $false)]
42+
[string] $Profile = $null
43+
)
44+
# There is an inconsistency between this implementation and c# implementation of the cmdlet.
45+
# The CustomRulePath parameter here is of "string[]" type whereas in the c# implementation it is of "string" type.
46+
# If we set the CustomRulePath parameter here to "string[]", then the library usage test fails when run as an administrator.
47+
# We want to note that the library usage test doesn't fail when run as a non-admin user.
48+
# The following is the error statement when the test runs as an administrator.
49+
# Assert failed on "Initialize" with "7" argument(s): "Test failed due to terminating error: The module was expected to contain an assembly manifest. (Exception from HRESULT: 0x80131018)"
50+
51+
$scriptAnalyzer = New-Object "Microsoft.Windows.PowerShell.ScriptAnalyzer.ScriptAnalyzer";
3952
$scriptAnalyzer.Initialize(
4053
$runspace,
4154
$testOutputWriter,
42-
$CustomizedRulePath,
55+
$CustomRulePath,
4356
$IncludeRule,
4457
$ExcludeRule,
4558
$Severity,
46-
$SuppressedOnly.IsPresent
59+
$SuppressedOnly.IsPresent,
60+
$Profile
4761
);
4862

4963
if ($PSCmdlet.ParameterSetName -eq "File") {

0 commit comments

Comments
 (0)