Skip to content

Commit ec4afc6

Browse files
committed
Merge pull request #108 from PowerShell/BugFixes
Take Bug fixes to Master
2 parents b98774c + f12e076 commit ec4afc6

19 files changed

+388
-141
lines changed

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/ScriptAnalyzer.cs

Lines changed: 58 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public void Initilaize(Dictionary<string, List<string>> result)
142142
paths = result.ContainsKey("ValidDllPaths") ? result["ValidDllPaths"] : result["ValidPaths"];
143143
foreach (string path in paths)
144144
{
145-
if (Path.GetExtension(path).ToLower(CultureInfo.CurrentCulture) == ".dll")
145+
if (String.Equals(Path.GetExtension(path),".dll",StringComparison.OrdinalIgnoreCase))
146146
{
147147
catalog.Catalogs.Add(new AssemblyCatalog(path));
148148
}
@@ -241,8 +241,8 @@ public List<ExternalRule> GetExternalRule(string[] moduleNames)
241241

242242
FunctionInfo funcInfo = (FunctionInfo)psobject.ImmediateBaseObject;
243243
ParameterMetadata param = funcInfo.Parameters.Values
244-
.First<ParameterMetadata>(item => item.Name.ToLower(CultureInfo.CurrentCulture).EndsWith("ast", StringComparison.CurrentCulture) ||
245-
item.Name.ToLower(CultureInfo.CurrentCulture).EndsWith("token", StringComparison.CurrentCulture));
244+
.First<ParameterMetadata>(item => item.Name.EndsWith("ast", StringComparison.OrdinalIgnoreCase) ||
245+
item.Name.EndsWith("token", StringComparison.OrdinalIgnoreCase));
246246

247247
//Only add functions that are defined as rules.
248248
if (param != null)
@@ -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.Name,
254+
rules.Add(new ExternalRule(funcInfo.Name, funcInfo.Name, desc, param.Name,param.ParameterType.FullName,
255255
funcInfo.ModuleName, funcInfo.Module.Path));
256256
}
257257
}
@@ -289,13 +289,13 @@ 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))
293-
.GroupBy<ExternalRule, string>(item => item.GetParameter())
292+
.Where<ExternalRule>(item => item.GetParameter().EndsWith("ast", StringComparison.OrdinalIgnoreCase))
293+
.GroupBy<ExternalRule, string>(item => item.GetParameterType())
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))
298-
.GroupBy<ExternalRule, string>(item => item.GetParameter())
297+
.Where<ExternalRule>(item => item.GetParameter().EndsWith("token", StringComparison.OrdinalIgnoreCase))
298+
.GroupBy<ExternalRule, string>(item => item.GetParameterType())
299299
.ToDictionary(item => item.Key, item => item.ToList());
300300

301301
using (rsp)
@@ -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+
(astRuleGroup.Key.IndexOf(testAst.GetType().FullName,StringComparison.OrdinalIgnoreCase) != -1)), false);
341341

342342
foreach (Ast childAst in childAsts)
343343
{
@@ -365,49 +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;
383-
384-
// Because error stream is merged to output stream,
385-
// we need to handle the error records.
386-
if (psobject.ImmediateBaseObject is ErrorRecord)
387-
{
388-
ErrorRecord record = (ErrorRecord)psobject.ImmediateBaseObject;
389-
command.WriteError(record);
390-
continue;
391-
}
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]);
392377

393-
// DiagnosticRecord may not be correctly returned from external rule.
394-
try
378+
foreach (var psobject in psobjects)
395379
{
396-
Enum.TryParse<DiagnosticSeverity>(psobject.Properties["Severity"].Value.ToString().ToUpper(), out severity);
397-
message = psobject.Properties["Message"].Value.ToString();
398-
extent = (IScriptExtent)psobject.Properties["Extent"].Value;
399-
ruleName = psobject.Properties["RuleName"].Value.ToString();
380+
DiagnosticSeverity severity;
381+
IScriptExtent extent;
382+
string message = string.Empty;
383+
string ruleName = string.Empty;
384+
385+
if (psobject != null && psobject.ImmediateBaseObject != null)
386+
{
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+
}
414+
}
400415
}
401-
catch (Exception ex)
402-
{
403-
command.WriteError(new ErrorRecord(ex, ex.HResult.ToString("X"), ErrorCategory.NotSpecified, this));
404-
continue;
405-
}
406-
407-
if (!string.IsNullOrEmpty(message)) yield return new DiagnosticRecord(message, extent, ruleName, severity, null);
408416
}
409417
}
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+
}
410423

424+
return diagnostics;
411425
#endregion
412426
}
413427
}
@@ -478,7 +492,7 @@ public Dictionary<string, List<string>> CheckRuleExtension(string[] path, PSCmdl
478492

479493
cmdlet.WriteDebug(string.Format(CultureInfo.CurrentCulture, Strings.CheckAssemblyFile, resolvedPath));
480494

481-
if (Path.GetExtension(resolvedPath).ToLower(CultureInfo.CurrentCulture) == ".dll")
495+
if (String.Equals(Path.GetExtension(resolvedPath),".dll", StringComparison.OrdinalIgnoreCase))
482496
{
483497
if (!File.Exists(resolvedPath))
484498
{
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#AvoidUsingWMICmdlet
2+
**Severity Level: Warning**
3+
4+
5+
##Description
6+
7+
Avoid Using Get-WMIObject, Remove-WMIObject, Invoke-WmiMethod, Register-WmiEvent, Set-WmiInstance
8+
9+
For PowerShell 3.0 and above, use CIM cmdlet which perform the same tasks as the WMI cmdlets. The CIM cmdlets comply with WS-Management (WSMan) standards and with the Common Information Model (CIM) standard, which enables the cmdlets to use the same techniques to manage Windows computers and those running other operating systems.
10+
11+
##How to Fix
12+
13+
Use corresponding CIM cmdlets such as Get-CIMInstance, Remove-CIMInstance, Invoke-CIMMethod, Register-CimIndicationEvent, Set-CimInstance
14+
15+
##Example
16+
17+
Wrong:
18+
```
19+
Get-WmiObject -Query 'Select * from Win32_Process where name LIKE "myprocess%"' | Remove-WmiObject
20+
Invoke-WmiMethod –Class Win32_Process –Name "Create" –ArgumentList @{ CommandLine = "notepad.exe" }
21+
```
22+
Correct:
23+
```
24+
Get-CimInstance -Query 'Select * from Win32_Process where name LIKE "myprocess%"' | Remove-CIMInstance
25+
Invoke-CimMethod –ClassName Win32_Process –MethodName "Create" –Arguments @{ CommandLine = "notepad.exe" }
26+
```

Rules/AvoidUsingWMIObjectCmdlet.cs renamed to Rules/AvoidUsingWMICmdlet.cs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,19 @@
2828
namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules
2929
{
3030
/// <summary>
31-
/// AvoidUsingWMIObjectCmdlet: Verify that Get-WMIObject, Remove-WMIObject are not used
31+
/// AvoidUsingWMICmdlet: Avoid Using Get-WMIObject, Remove-WMIObject, Invoke-WmiMethod, Register-WmiEvent, Set-WmiInstance
3232
/// </summary>
3333
[Export(typeof(IScriptRule))]
34-
public class AvoidUsingWMIObjectCmdlet : IScriptRule
34+
public class AvoidUsingWMICmdlet : IScriptRule
3535
{
3636
/// <summary>
37-
/// AnalyzeScript: Verify that Get-WMIObject, Remove-WMIObject are not used
37+
/// AnalyzeScript: Avoid Using Get-WMIObject, Remove-WMIObject, Invoke-WmiMethod, Register-WmiEvent, Set-WmiInstance
3838
/// </summary>
3939
public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
4040
{
4141
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);
4242

43-
// Rule is applicable only when PowerShell Version is < 3.0, since Get-CIMInstance was introduced in 3.0
43+
// Rule is applicable only when PowerShell Version is < 3.0, since CIM cmdlet was introduced in 3.0
4444
int majorPSVersion = GetPSMajorVersion(ast);
4545
if (!(3 > majorPSVersion && 0 < majorPSVersion))
4646
{
@@ -50,9 +50,15 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
5050
// Iterate all CommandAsts and check the command name
5151
foreach (CommandAst cmdAst in commandAsts)
5252
{
53-
if (cmdAst.GetCommandName() != null && (String.Equals(cmdAst.GetCommandName(), "get-wmiobject", StringComparison.OrdinalIgnoreCase) || String.Equals(cmdAst.GetCommandName(), "remove-wmiobject", StringComparison.OrdinalIgnoreCase)))
53+
if (cmdAst.GetCommandName() != null &&
54+
(String.Equals(cmdAst.GetCommandName(), "get-wmiobject", StringComparison.OrdinalIgnoreCase)
55+
|| String.Equals(cmdAst.GetCommandName(), "remove-wmiobject", StringComparison.OrdinalIgnoreCase)
56+
|| String.Equals(cmdAst.GetCommandName(), "invoke-wmimethod", StringComparison.OrdinalIgnoreCase)
57+
|| String.Equals(cmdAst.GetCommandName(), "register-wmievent", StringComparison.OrdinalIgnoreCase)
58+
|| String.Equals(cmdAst.GetCommandName(), "set-wmiinstance", StringComparison.OrdinalIgnoreCase))
59+
)
5460
{
55-
yield return new DiagnosticRecord(String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingWMIObjectCmdletError, System.IO.Path.GetFileName(fileName)),
61+
yield return new DiagnosticRecord(String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingWMICmdletError, System.IO.Path.GetFileName(fileName)),
5662
cmdAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
5763
}
5864
}
@@ -87,7 +93,7 @@ private int GetPSMajorVersion(Ast ast)
8793
/// <returns>The name of this rule</returns>
8894
public string GetName()
8995
{
90-
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.AvoidUsingWMIObjectCmdletName);
96+
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.AvoidUsingWMICmdletName);
9197
}
9298

9399
/// <summary>
@@ -96,7 +102,7 @@ public string GetName()
96102
/// <returns>The common name of this rule</returns>
97103
public string GetCommonName()
98104
{
99-
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingWMIObjectCmdletCommonName);
105+
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingWMICmdletCommonName);
100106
}
101107

102108
/// <summary>
@@ -105,7 +111,7 @@ public string GetCommonName()
105111
/// <returns>The description of this rule</returns>
106112
public string GetDescription()
107113
{
108-
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingWMIObjectCmdletDescription);
114+
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingWMICmdletDescription);
109115
}
110116

111117
/// <summary>

0 commit comments

Comments
 (0)