Skip to content

Commit 0bade57

Browse files
committed
Merge pull request #106 from KarolKaczmarek/DscTestsPresentRule
Adding DscTestsPresent rule
2 parents 3f6ebbe + 5892a67 commit 0bade57

33 files changed

+966
-182
lines changed

CustomizedRuleDocumentation.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
##Documentation for Customized Rules in PowerShell Scripts
2+
PSScriptAnalyzer uses MEF(Managed Extensibility Framework) to import all rules defined in the assembly. It can also consume rules written in PowerShell scripts. When calling Invoke-ScriptAnalyzer, users can pass customized rules using parameter -CustomizedRulePath to apply rule checkings on the scripts.
3+
4+
This documentation serves as a basic guideline on how to define customized rules.
5+
6+
###Basics
7+
- Functions should have comment-based help. Make sure .DESCRIPTION field is there, as it will be consumed as rule description for the customized rule.
8+
```
9+
<#
10+
.SYNOPSIS
11+
Name of your rule.
12+
.DESCRIPTION
13+
This would be the description of your rule. Please refer to Rule Documentation for consistent rule messages.
14+
.EXAMPLE
15+
.INPUTS
16+
.OUTPUTS
17+
.NOTES
18+
#>
19+
```
20+
21+
- Output type should be DiagnosticRecord:
22+
```
23+
[OutputType([Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]])]
24+
```
25+
26+
- Make sure each function takes either a Token or an Ast as a parameter
27+
```
28+
Param
29+
(
30+
[Parameter(Mandatory = $true)]
31+
[ValidateNotNullOrEmpty()]
32+
[System.Management.Automation.Language.ScriptBlockAst]
33+
$testAst
34+
)
35+
```
36+
37+
- DiagnosticRecord should have four properties: Message, Extent, RuleName and Severity
38+
```
39+
$result = [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]]@{"Message" = "This is a sample rule";
40+
"Extent" = $ast.Extent;
41+
"RuleName" = $PSCmdlet.MyInvocation.InvocationName;
42+
"Severity" = "Warning"}
43+
```
44+
45+
- Make sure you export the function(s) at the end of the script using Export-ModuleMember
46+
```
47+
Export-ModuleMember -Function (FunctionName)
48+
```
49+
50+
###Example
51+
```
52+
<#
53+
.SYNOPSIS
54+
Uses #Requires -RunAsAdministrator instead of your own methods.
55+
.DESCRIPTION
56+
The #Requires statement prevents a script from running unless the Windows PowerShell version, modules, snap-ins, and module and snap-in version prerequisites are met.
57+
From Windows PowerShell 4.0, the #Requires statement let script developers require that sessions be run with elevated user rights (run as Administrator).
58+
Script developers does not need to write their own methods any more.
59+
To fix a violation of this rule, please consider to use #Requires -RunAsAdministrator instead of your own methods.
60+
.EXAMPLE
61+
Measure-RequiresRunAsAdministrator -ScriptBlockAst $ScriptBlockAst
62+
.INPUTS
63+
[System.Management.Automation.Language.ScriptBlockAst]
64+
.OUTPUTS
65+
[Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]]
66+
.NOTES
67+
None
68+
#>
69+
function Measure-RequiresRunAsAdministrator
70+
{
71+
[CmdletBinding()]
72+
[OutputType([Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]])]
73+
Param
74+
(
75+
[Parameter(Mandatory = $true)]
76+
[ValidateNotNullOrEmpty()]
77+
[System.Management.Automation.Language.ScriptBlockAst]
78+
$ScriptBlockAst
79+
)
80+
81+
Process
82+
{
83+
$results = @()
84+
try
85+
{
86+
#region Define predicates to find ASTs.
87+
# Finds specific method, IsInRole.
88+
[ScriptBlock]$predicate1 = {
89+
param ([System.Management.Automation.Language.Ast]$Ast)
90+
[bool]$returnValue = $false
91+
if ($Ast -is [System.Management.Automation.Language.MemberExpressionAst])
92+
{
93+
[System.Management.Automation.Language.MemberExpressionAst]$meAst = $ast;
94+
if ($meAst.Member -is [System.Management.Automation.Language.StringConstantExpressionAst])
95+
{
96+
[System.Management.Automation.Language.StringConstantExpressionAst]$sceAst = $meAst.Member;
97+
if ($sceAst.Value -eq "isinrole")
98+
{
99+
$returnValue = $true;
100+
}
101+
}
102+
}
103+
return $returnValue
104+
}
105+
106+
# Finds specific value, [system.security.principal.windowsbuiltinrole]::administrator.
107+
[ScriptBlock]$predicate2 = {
108+
param ([System.Management.Automation.Language.Ast]$Ast)
109+
[bool]$returnValue = $false
110+
if ($ast -is [System.Management.Automation.Language.AssignmentStatementAst])
111+
{
112+
[System.Management.Automation.Language.AssignmentStatementAst]$asAst = $Ast;
113+
if ($asAst.Right.ToString().ToLower() -eq "[system.security.principal.windowsbuiltinrole]::administrator")
114+
{
115+
$returnValue = $true
116+
}
117+
}
118+
return $returnValue
119+
}
120+
#endregion
121+
#region Finds ASTs that match the predicates.
122+
123+
[System.Management.Automation.Language.Ast[]]$methodAst = $ScriptBlockAst.FindAll($predicate1, $true)
124+
[System.Management.Automation.Language.Ast[]]$assignmentAst = $ScriptBlockAst.FindAll($predicate2, $true)
125+
if ($null -ne $ScriptBlockAst.ScriptRequirements)
126+
{
127+
if ((!$ScriptBlockAst.ScriptRequirements.IsElevationRequired) -and
128+
($methodAst.Count -ne 0) -and ($assignmentAst.Count -ne 0))
129+
{
130+
$result = [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord]@{"Message" = $Messages.MeasureRequiresRunAsAdministrator;
131+
"Extent" = $assignmentAst.Extent;
132+
"RuleName" = $PSCmdlet.MyInvocation.InvocationName;
133+
"Severity" = "Information"}
134+
$results += $result
135+
}
136+
}
137+
else
138+
{
139+
if (($methodAst.Count -ne 0) -and ($assignmentAst.Count -ne 0))
140+
{
141+
$result = [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord]@{"Message" = $Messages.MeasureRequiresRunAsAdministrator;
142+
"Extent" = $assignmentAst.Extent;
143+
"RuleName" = $PSCmdlet.MyInvocation.InvocationName;
144+
"Severity" = "Information"}
145+
$results += $result
146+
}
147+
}
148+
return $results
149+
#endregion
150+
}
151+
catch
152+
{
153+
$PSCmdlet.ThrowTerminatingError($PSItem)
154+
}
155+
}
156+
}
157+
```
158+
More examples can be found in *Tests\Engine\CommunityRules*

Engine/Generic/ExternalRule.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ internal class ExternalRule : IExternalRule
2828
string param = string.Empty;
2929
string srcName = string.Empty;
3030
string modPath = string.Empty;
31-
31+
string paramType = string.Empty;
3232

3333
public string GetName()
3434
{
@@ -55,6 +55,11 @@ public SourceType GetSourceType()
5555
return SourceType.Module;
5656
}
5757

58+
public string GetParameterType()
59+
{
60+
return this.paramType;
61+
}
62+
5863
//Set the community rule level as warning as the current implementation does not require user to specify rule severity when defining their functions in PS scripts
5964
public RuleSeverity GetSeverity()
6065
{
@@ -80,14 +85,15 @@ public ExternalRule()
8085

8186
}
8287

83-
public ExternalRule(string name, string commonName, string desc, string param, string srcName, string modPath)
88+
public ExternalRule(string name, string commonName, string desc, string param, string paramType, string srcName, string modPath)
8489
{
8590
this.name = name;
8691
this.commonName = commonName;
8792
this.desc = desc;
8893
this.param = param;
8994
this.srcName = srcName;
9095
this.modPath = modPath;
96+
this.paramType = paramType;
9197
}
9298

9399
#endregion

Engine/Generic/RuleInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class RuleInfo
3535
/// Name: The name of the rule.
3636
/// </summary>
3737
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
38-
public string Name
38+
public string RuleName
3939
{
4040
get { return name; }
4141
private set { name = value; }
@@ -102,7 +102,7 @@ public RuleSeverity Severity
102102
/// <param name="sourceName">Source name of the rule.</param>
103103
public RuleInfo(string name, string commonName, string description, SourceType sourceType, string sourceName, RuleSeverity severity)
104104
{
105-
Name = name;
105+
RuleName = name;
106106
CommonName = commonName;
107107
Description = description;
108108
SourceType = sourceType;

Engine/PSScriptAnalyzer.psd1

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
#
44

55
@{
6+
7+
# Author of this module
8+
Author = 'Microsoft Corporation'
69

710
# Script module or binary module file associated with this manifest.
811
RootModule = 'Microsoft.Windows.Powershell.ScriptAnalyzer.dll'
@@ -14,10 +17,10 @@ ModuleVersion = '1.0'
1417
GUID = '324fc715-36bf-4aee-8e58-72e9b4a08ad9'
1518

1619
# Company or vendor of this module
17-
CompanyName = 'Microsoft'
20+
CompanyName = 'Microsoft Corporation'
1821

1922
# Copyright statement for this module
20-
Copyright = '(c) 2015. All rights reserved.'
23+
Copyright = '(c) Microsoft Corporation 2015. All rights reserved.'
2124

2225
# Description of the functionality provided by this module
2326
Description = 'PSScriptAnalyzer provides script analysis and checks for potential code defects in the scripts by applying a group of builtin or customized rules on the scripts being analyzed.'

0 commit comments

Comments
 (0)