Skip to content

Commit 2849549

Browse files
committed
Merge pull request #186 from PowerShell/development
Merge Development to Master
2 parents 368309d + e050eb3 commit 2849549

17 files changed

+632
-17
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#AvoidUsingDeprecatedManifestFields
2+
**Severity Level: Warning**
3+
4+
5+
##Description
6+
7+
PowerShell V5.0 introduced some new fields and replaced some old fields with in module manifest files (.psd1). Therefore, fields such as "ModuleToProcess" is replaced with "RootModule". Using the deprecated manifest fields will result in PSScriptAnalyzer warnings.
8+
9+
##How to Fix
10+
11+
To fix a violation of this, please replace "ModuleToProcess" with "RootModule".
12+
13+
##Example
14+
15+
Wrong:
16+
```
17+
ModuleToProcess ='psscriptanalyzer'
18+
19+
ModuleVersion = '1.0'
20+
```
21+
22+
Correct:
23+
```
24+
RootModule ='psscriptanalyzer'
25+
26+
ModuleVersion = '1.0'
27+
```
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#ProvideDefaultParameterValue
2+
**Severity Level: Warning**
3+
4+
5+
##Description
6+
Parameters must have a default value as uninitialized parameters will lead to potential bugs in the scripts.
7+
8+
##How to Fix
9+
10+
To fix a violation of this rule, please specify a default value for all parameters.
11+
12+
##Example
13+
14+
Wrong:
15+
16+
```
17+
function Test($Param1)
18+
{
19+
$Param1
20+
}
21+
```
22+
23+
Correct:
24+
25+
```
26+
function Test($Param1 = $null)
27+
{
28+
$Param1
29+
}
30+
```

Rules/AvoidUninitializedVariable.cs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,26 +52,24 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
5252

5353
IEnumerable<Ast> funcAsts = ast.FindAll(item => item is FunctionDefinitionAst, true);
5454
IEnumerable<Ast> funcMemberAsts = ast.FindAll(item => item is FunctionMemberAst, true);
55-
56-
// Checks whether this is a dsc resource file (we don't raise this rule for get, set and test-target resource
57-
bool isDscResourceFile = Helper.Instance.IsDscResourceModule(fileName);
58-
59-
List<string> targetResourcesFunctions = new List<string>( new string[] { "get-targetresource", "set-targetresource", "test-targetresource" });
60-
55+
6156
foreach (FunctionDefinitionAst funcAst in funcAsts)
6257
{
6358
// Finds all VariableExpressionAst.
6459
IEnumerable<Ast> varAsts = funcAst.FindAll(testAst => testAst is VariableExpressionAst, true);
6560

6661
HashSet<string> paramVariables = new HashSet<string>();
6762

68-
if (isDscResourceFile && targetResourcesFunctions.Contains(funcAst.Name, StringComparer.OrdinalIgnoreCase))
63+
// don't raise the rules for variables in the param block.
64+
if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null)
6965
{
70-
// don't raise the rules for variables in the param block.
71-
if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null)
72-
{
73-
paramVariables.UnionWith(funcAst.Body.ParamBlock.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath));
74-
}
66+
paramVariables.UnionWith(funcAst.Body.ParamBlock.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath));
67+
}
68+
69+
//don't raise the rules for parameters outside the param block
70+
if(funcAst.Parameters != null)
71+
{
72+
paramVariables.UnionWith(funcAst.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath));
7573
}
7674

7775
// Iterates all VariableExpressionAst and check the command name.
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
//
2+
// Copyright (c) Microsoft Corporation.
3+
//
4+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10+
// THE SOFTWARE.
11+
//
12+
13+
using System;
14+
using System.Collections.Generic;
15+
using System.Management.Automation.Language;
16+
using System.Management.Automation;
17+
using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic;
18+
using System.ComponentModel.Composition;
19+
using System.Globalization;
20+
21+
namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules
22+
{
23+
/// <summary>
24+
/// AvoidUsingDeprecatedManifestFields: Run Test Module Manifest to check that no deprecated fields are being used.
25+
/// </summary>
26+
[Export(typeof(IScriptRule))]
27+
public class AvoidUsingDeprecatedManifestFields : IScriptRule
28+
{
29+
/// <summary>
30+
/// AnalyzeScript: Run Test Module Manifest to check that no deprecated fields are being used.
31+
/// </summary>
32+
/// <param name="ast">The script's ast</param>
33+
/// <param name="fileName">The script's file name</param>
34+
/// <returns>A List of diagnostic results of this rule</returns>
35+
public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
36+
{
37+
if (ast == null)
38+
{
39+
throw new ArgumentNullException(Strings.NullAstErrorMessage);
40+
}
41+
42+
if (String.Equals(System.IO.Path.GetExtension(fileName), ".psd1", StringComparison.OrdinalIgnoreCase))
43+
{
44+
var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace);
45+
IEnumerable<PSObject> result = null;
46+
try
47+
{
48+
ps.AddCommand("Test-ModuleManifest");
49+
ps.AddParameter("Path", fileName);
50+
51+
// Suppress warnings emitted during the execution of Test-ModuleManifest
52+
// ModuleManifest rule must catch any violations (warnings/errors) and generate DiagnosticRecord(s)
53+
ps.AddParameter("WarningAction", ActionPreference.SilentlyContinue);
54+
ps.AddParameter("WarningVariable", "Message");
55+
ps.AddScript("$Message");
56+
result = ps.Invoke();
57+
58+
}
59+
catch
60+
{}
61+
62+
if (result != null)
63+
{
64+
foreach (var warning in result)
65+
{
66+
if (warning.BaseObject != null)
67+
{
68+
yield return
69+
new DiagnosticRecord(
70+
String.Format(CultureInfo.CurrentCulture, warning.BaseObject.ToString()), ast.Extent,
71+
GetName(), DiagnosticSeverity.Warning, fileName);
72+
}
73+
}
74+
}
75+
76+
}
77+
78+
}
79+
80+
/// <summary>
81+
/// GetName: Retrieves the name of this rule.
82+
/// </summary>
83+
/// <returns>The name of this rule</returns>
84+
public string GetName()
85+
{
86+
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.AvoidUsingDeprecatedManifestFieldsName);
87+
}
88+
89+
/// <summary>
90+
/// GetCommonName: Retrieves the common name of this rule.
91+
/// </summary>
92+
/// <returns>The common name of this rule</returns>
93+
public string GetCommonName()
94+
{
95+
return String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingDeprecatedManifestFieldsCommonName);
96+
}
97+
98+
/// <summary>
99+
/// GetDescription: Retrieves the description of this rule.
100+
/// </summary>
101+
/// <returns>The description of this rule</returns>
102+
public string GetDescription()
103+
{
104+
return String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingDeprecatedManifestFieldsDescription);
105+
}
106+
107+
/// <summary>
108+
/// Method: Retrieves the type of the rule: builtin, managed or module.
109+
/// </summary>
110+
public SourceType GetSourceType()
111+
{
112+
return SourceType.Builtin;
113+
}
114+
115+
/// <summary>
116+
/// GetSeverity: Retrieves the severity of the rule: error, warning of information.
117+
/// </summary>
118+
/// <returns></returns>
119+
public RuleSeverity GetSeverity()
120+
{
121+
return RuleSeverity.Warning;
122+
}
123+
124+
/// <summary>
125+
/// Method: Retrieves the module/assembly name the rule is from.
126+
/// </summary>
127+
public string GetSourceName()
128+
{
129+
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName);
130+
}
131+
}
132+
}

Rules/ProvideDefaultParameterValue.cs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//
2+
// Copyright (c) Microsoft Corporation.
3+
//
4+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10+
// THE SOFTWARE.
11+
//
12+
13+
using System;
14+
using System.Linq;
15+
using System.Collections.Generic;
16+
using System.Management.Automation.Language;
17+
using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic;
18+
using System.ComponentModel.Composition;
19+
using System.Globalization;
20+
21+
namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules
22+
{
23+
/// <summary>
24+
/// ProvideDefaultParameterValue: Check if any uninitialized variable is used.
25+
/// </summary>
26+
[Export(typeof(IScriptRule))]
27+
public class ProvideDefaultParameterValue : IScriptRule
28+
{
29+
/// <summary>
30+
/// AnalyzeScript: Check if any uninitialized variable is used.
31+
/// </summary>
32+
public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
33+
{
34+
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);
35+
36+
// Finds all functionAst
37+
IEnumerable<Ast> functionAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true);
38+
39+
// Checks whether this is a dsc resource file (we don't raise this rule for get, set and test-target resource
40+
bool isDscResourceFile = Helper.Instance.IsDscResourceModule(fileName);
41+
42+
List<string> targetResourcesFunctions = new List<string>(new string[] { "get-targetresource", "set-targetresource", "test-targetresource" });
43+
44+
45+
foreach (FunctionDefinitionAst funcAst in functionAsts)
46+
{
47+
// Finds all ParamAsts.
48+
IEnumerable<Ast> varAsts = funcAst.FindAll(testAst => testAst is VariableExpressionAst, true);
49+
50+
// Iterrates all ParamAsts and check if their names are on the list.
51+
52+
HashSet<string> dscVariables = new HashSet<string>();
53+
if (isDscResourceFile && targetResourcesFunctions.Contains(funcAst.Name, StringComparer.OrdinalIgnoreCase))
54+
{
55+
// don't raise the rules for variables in the param block.
56+
if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null)
57+
{
58+
dscVariables.UnionWith(funcAst.Body.ParamBlock.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath));
59+
}
60+
}
61+
// only raise the rules for variables in the param block.
62+
if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null)
63+
{
64+
foreach (var paramAst in funcAst.Body.ParamBlock.Parameters)
65+
{
66+
if (Helper.Instance.IsUninitialized(paramAst.Name, funcAst) && !dscVariables.Contains(paramAst.Name.VariablePath.UserPath))
67+
{
68+
yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueError, paramAst.Name.VariablePath.UserPath),
69+
paramAst.Name.Extent, GetName(), DiagnosticSeverity.Warning, fileName, paramAst.Name.VariablePath.UserPath);
70+
}
71+
}
72+
}
73+
74+
if (funcAst.Parameters != null)
75+
{
76+
foreach (var paramAst in funcAst.Parameters)
77+
{
78+
if (Helper.Instance.IsUninitialized(paramAst.Name, funcAst) && !dscVariables.Contains(paramAst.Name.VariablePath.UserPath))
79+
{
80+
yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueError, paramAst.Name.VariablePath.UserPath),
81+
paramAst.Name.Extent, GetName(), DiagnosticSeverity.Warning, fileName, paramAst.Name.VariablePath.UserPath);
82+
}
83+
}
84+
}
85+
}
86+
}
87+
88+
/// <summary>
89+
/// GetName: Retrieves the name of this rule.
90+
/// </summary>
91+
/// <returns>The name of this rule</returns>
92+
public string GetName()
93+
{
94+
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.ProvideDefaultParameterValueName);
95+
}
96+
97+
/// <summary>
98+
/// GetCommonName: Retrieves the common name of this rule.
99+
/// </summary>
100+
/// <returns>The common name of this rule</returns>
101+
public string GetCommonName()
102+
{
103+
return string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueCommonName);
104+
}
105+
106+
/// <summary>
107+
/// GetDescription: Retrieves the description of this rule.
108+
/// </summary>
109+
/// <returns>The description of this rule</returns>
110+
public string GetDescription()
111+
{
112+
return string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueDescription);
113+
}
114+
115+
/// <summary>
116+
/// Method: Retrieves the type of the rule: builtin, managed or module.
117+
/// </summary>
118+
public SourceType GetSourceType()
119+
{
120+
return SourceType.Builtin;
121+
}
122+
123+
/// <summary>
124+
/// GetSeverity: Retrieves the severity of the rule: error, warning of information.
125+
/// </summary>
126+
/// <returns></returns>
127+
public RuleSeverity GetSeverity()
128+
{
129+
return RuleSeverity.Warning;
130+
}
131+
132+
/// <summary>
133+
/// Method: Retrieves the module/assembly name the rule is from.
134+
/// </summary>
135+
public string GetSourceName()
136+
{
137+
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName);
138+
}
139+
}
140+
}

Rules/ScriptAnalyzerBuiltinRules.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
<Compile Include="AvoidReservedParams.cs" />
6060
<Compile Include="AvoidShouldContinueWithoutForce.cs" />
6161
<Compile Include="AvoidTrapStatement.cs" />
62+
<Compile Include="AvoidUsingDeprecatedManifestFields.cs" />
63+
<Compile Include="ProvideDefaultParameterValue.cs" />
6264
<Compile Include="AvoidUninitializedVariable.cs" />
6365
<Compile Include="AvoidUsernameAndPasswordParams.cs" />
6466
<Compile Include="AvoidUsingComputerNameHardcoded.cs" />

0 commit comments

Comments
 (0)