Skip to content

Commit 91fe9d0

Browse files
committed
Merge pull request #94 from PowerShell/customizedRuleDocumentati
Added customized rule documentation
2 parents ec4afc6 + 11ced68 commit 91fe9d0

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
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*

0 commit comments

Comments
 (0)