Skip to content

Commit be781aa

Browse files
committed
Added unit test
Also added try/catch to capture errors in customized rule modules and throw non-terminating errors
1 parent 89b4e23 commit be781aa

File tree

2 files changed

+94
-38
lines changed

2 files changed

+94
-38
lines changed

Engine/ScriptAnalyzer.cs

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ public List<ExternalRule> GetExternalRule(string[] moduleNames)
251251
string desc =posh.AddScript(script).Invoke()[0].ImmediateBaseObject.ToString()
252252
.Replace("\r\n", " ").Trim();
253253

254-
rules.Add(new ExternalRule(funcInfo.Name, funcInfo.Name, desc, param.ParameterType.FullName,
254+
rules.Add(new ExternalRule(funcInfo.Name, funcInfo.Name, desc, param.ParameterType.Name,
255255
funcInfo.ModuleName, funcInfo.Module.Path));
256256
}
257257
}
@@ -289,12 +289,12 @@ public IEnumerable<DiagnosticRecord> GetExternalRecord(Ast ast, Token[] token, E
289289

290290
// Groups rules by AstType and Tokens.
291291
Dictionary<string, List<ExternalRule>> astRuleGroups = rules
292-
.Where<ExternalRule>(item => item.GetParameter().EndsWith("ast", true, CultureInfo.CurrentCulture))
292+
.Where<ExternalRule>(item => item.GetParameter().EndsWith("ast", StringComparison.OrdinalIgnoreCase))
293293
.GroupBy<ExternalRule, string>(item => item.GetParameter())
294294
.ToDictionary(item => item.Key, item => item.ToList());
295295

296296
Dictionary<string, List<ExternalRule>> tokenRuleGroups = rules
297-
.Where<ExternalRule>(item => item.GetParameter().EndsWith("token", true, CultureInfo.CurrentCulture))
297+
.Where<ExternalRule>(item => item.GetParameter().EndsWith("token", StringComparison.OrdinalIgnoreCase))
298298
.GroupBy<ExternalRule, string>(item => item.GetParameter())
299299
.ToDictionary(item => item.Key, item => item.ToList());
300300

@@ -337,7 +337,7 @@ public IEnumerable<DiagnosticRecord> GetExternalRecord(Ast ast, Token[] token, E
337337
{
338338
// Find all AstTypes that appeared in rule groups.
339339
IEnumerable<Ast> childAsts = ast.FindAll(new Func<Ast, bool>((testAst) =>
340-
(testAst.GetType().Name.ToLower(CultureInfo.CurrentCulture) == astRuleGroup.Key.ToLower(CultureInfo.CurrentCulture))), false);
340+
(String.Equals(testAst.GetType().Name, astRuleGroup.Key, StringComparison.OrdinalIgnoreCase))), false);
341341

342342
foreach (Ast childAst in childAsts)
343343
{
@@ -365,52 +365,63 @@ public IEnumerable<DiagnosticRecord> GetExternalRecord(Ast ast, Token[] token, E
365365
}
366366

367367
#endregion
368-
369368
#region Collects the results from commands.
370-
371-
for (int i = 0; i < powerShellCommands.Count; i++)
369+
List<DiagnosticRecord> diagnostics = new List<DiagnosticRecord>();
370+
try
372371
{
373-
// EndInvoke will wait for each command to finish, so we will be getting the commands
374-
// in the same order that they have been invoked withy BeginInvoke.
375-
PSDataCollection<PSObject> psobjects = powerShellCommands[i].EndInvoke(powerShellCommandResults[i]);
376-
377-
foreach (var psobject in psobjects)
372+
for (int i = 0; i < powerShellCommands.Count; i++)
378373
{
379-
DiagnosticSeverity severity;
380-
IScriptExtent extent;
381-
string message = string.Empty;
382-
string ruleName = string.Empty;
374+
// EndInvoke will wait for each command to finish, so we will be getting the commands
375+
// in the same order that they have been invoked withy BeginInvoke.
376+
PSDataCollection<PSObject> psobjects = powerShellCommands[i].EndInvoke(powerShellCommandResults[i]);
383377

384-
if (psobject != null && psobject.ImmediateBaseObject != null)
378+
foreach (var psobject in psobjects)
385379
{
386-
// Because error stream is merged to output stream,
387-
// we need to handle the error records.
388-
if (psobject.ImmediateBaseObject is ErrorRecord)
389-
{
390-
ErrorRecord record = (ErrorRecord)psobject.ImmediateBaseObject;
391-
command.WriteError(record);
392-
continue;
393-
}
380+
DiagnosticSeverity severity;
381+
IScriptExtent extent;
382+
string message = string.Empty;
383+
string ruleName = string.Empty;
394384

395-
// DiagnosticRecord may not be correctly returned from external rule.
396-
try
385+
if (psobject != null && psobject.ImmediateBaseObject != null)
397386
{
398-
Enum.TryParse<DiagnosticSeverity>(psobject.Properties["Severity"].Value.ToString().ToUpper(), out severity);
399-
message = psobject.Properties["Message"].Value.ToString();
400-
extent = (IScriptExtent)psobject.Properties["Extent"].Value;
401-
ruleName = psobject.Properties["RuleName"].Value.ToString();
387+
// Because error stream is merged to output stream,
388+
// we need to handle the error records.
389+
if (psobject.ImmediateBaseObject is ErrorRecord)
390+
{
391+
ErrorRecord record = (ErrorRecord)psobject.ImmediateBaseObject;
392+
command.WriteError(record);
393+
continue;
394+
}
395+
396+
// DiagnosticRecord may not be correctly returned from external rule.
397+
try
398+
{
399+
Enum.TryParse<DiagnosticSeverity>(psobject.Properties["Severity"].Value.ToString().ToUpper(), out severity);
400+
message = psobject.Properties["Message"].Value.ToString();
401+
extent = (IScriptExtent)psobject.Properties["Extent"].Value;
402+
ruleName = psobject.Properties["RuleName"].Value.ToString();
403+
}
404+
catch (Exception ex)
405+
{
406+
command.WriteError(new ErrorRecord(ex, ex.HResult.ToString("X"), ErrorCategory.NotSpecified, this));
407+
continue;
408+
}
409+
410+
if (!string.IsNullOrEmpty(message))
411+
{
412+
diagnostics.Add(new DiagnosticRecord(message, extent, ruleName, severity, null));
413+
}
402414
}
403-
catch (Exception ex)
404-
{
405-
command.WriteError(new ErrorRecord(ex, ex.HResult.ToString("X"), ErrorCategory.NotSpecified, this));
406-
continue;
407-
}
408-
409-
if (!string.IsNullOrEmpty(message)) yield return new DiagnosticRecord(message, extent, ruleName, severity, null);
410415
}
411416
}
412417
}
418+
//Catch exception where customized defined rules have exceptins when doing invoke
419+
catch(Exception ex)
420+
{
421+
command.WriteError(new ErrorRecord(ex, ex.HResult.ToString("X"), ErrorCategory.NotSpecified, this));
422+
}
413423

424+
return diagnostics;
414425
#endregion
415426
}
416427
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#Requires -Version 3.0
2+
3+
<#
4+
.SYNOPSIS
5+
Uses #Requires -RunAsAdministrator instead of your own methods.
6+
.DESCRIPTION
7+
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.
8+
From Windows PowerShell 4.0, the #Requires statement let script developers require that sessions be run with elevated user rights (run as Administrator).
9+
Script developers does not need to write their own methods any more.
10+
To fix a violation of this rule, please consider to use #Requires -RunAsAdministrator instead of your own methods.
11+
.EXAMPLE
12+
Measure-RequiresRunAsAdministrator -ScriptBlockAst $ScriptBlockAst
13+
.INPUTS
14+
[System.Management.Automation.Language.ScriptBlockAst]
15+
.OUTPUTS
16+
[OutputType([PSCustomObject[])]
17+
.NOTES
18+
None
19+
#>
20+
function Measure-RequiresRunAsAdministrator
21+
{
22+
[CmdletBinding()]
23+
[OutputType([Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]])]
24+
Param
25+
(
26+
[Parameter(Mandatory = $true)]
27+
[ValidateNotNullOrEmpty()]
28+
[System.Management.Automation.Language.ScriptBlockAst]
29+
$Ast
30+
)
31+
32+
33+
$results = @()
34+
35+
$result = [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]]@{"Message" = "this is help";
36+
"Extent" = $FunctionDefinitionAst.Extent;
37+
"RuleName" = $PSCmdlet.MyInvocation.InvocationName;
38+
"Severity" = "Warning"}
39+
40+
$results += $result
41+
return $results
42+
43+
44+
}
45+
Export-ModuleMember -Function Measure*

0 commit comments

Comments
 (0)