From 6ef590dee361b8f338c82faa008d01141a8f7de4 Mon Sep 17 00:00:00 2001 From: iSazonov Date: Sat, 8 Oct 2016 00:48:08 +0600 Subject: [PATCH] Fix Get-WinEvent -FilterHashtable to work with named event data field (#2373) Fix Get-WinEvent -FilterHashtable to work with named event data field Now FilterHashtable parameter in Get-WinEvent for named data field: 1. generates a valid query, ex. @{Logname="System";Param="a"} 2. generates a valid query for multiple values, ex. @{Logname="System";Param="a","b"} Also removed double cast in the original code and added more tests to cover the fixed scenarios. --- .../GetEventCommand.cs | 84 ++++++++++++++----- .../Get-WinEvent.Tests.ps1 | 24 +++++- 2 files changed, 85 insertions(+), 23 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs index 87d64e54d8..f8dcb20e02 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs @@ -360,6 +360,7 @@ namespace Microsoft.PowerShell.Commands private const string propOpen = "["; private const string propClose = "]"; private const string filePrefix = "file://"; + private const string NamedDataTemplate = "((EventData[Data[@Name='{0}']='{1}']) or (UserData/*/{0}='{1}'))"; // // Other private members and constants @@ -1240,12 +1241,16 @@ namespace Microsoft.PowerShell.Commands default: { // - // None of the recognized values: this must be a named payload field + // None of the recognized values: this must be a named event data field // - ExtendPredicate(ref xpathString); - xpathString += string.Format(CultureInfo.InvariantCulture, - "([EventData[Data[@Name='{0}']='{1}']] or [UserData/*/{0}='{1}'])", - key, hash[key]); + // Fix Issue #2327 + added = HandleNamedDataHashValue(key, hash[key]); + if (added.Length > 0) + { + ExtendPredicate(ref xpathString); + xpathString += added; + } + } break; } @@ -1299,9 +1304,9 @@ namespace Microsoft.PowerShell.Commands private string HandleEventIdHashValue(Object value) { string ret = ""; - if (value is Array) + Array idsArray = value as Array; + if (idsArray != null) { - Array idsArray = (Array)(value); ret += "("; for (int i = 0; i < idsArray.Length; i++) { @@ -1328,10 +1333,9 @@ namespace Microsoft.PowerShell.Commands private string HandleLevelHashValue(Object value) { string ret = ""; - - if (value is Array) + Array levelsArray = value as Array; + if (levelsArray != null) { - Array levelsArray = (Array)(value); ret += "("; for (int i = 0; i < levelsArray.Length; i++) { @@ -1360,9 +1364,10 @@ namespace Microsoft.PowerShell.Commands Int64 keywordsMask = 0; Int64 keywordLong = 0; - if (value is Array) + Array keywordArray = value as Array; + if (keywordArray != null) { - foreach (Object keyword in (Array)value) + foreach (Object keyword in keywordArray) { if (KeywordStringToInt64(keyword.ToString(), ref keywordLong)) { @@ -1514,9 +1519,9 @@ namespace Microsoft.PowerShell.Commands private string HandleDataHashValue(Object value) { string ret = ""; - if (value is Array) + Array dataArray = value as Array; + if (dataArray != null) { - Array dataArray = (Array)(value); ret += "("; for (int i = 0; i < dataArray.Length; i++) { @@ -1537,6 +1542,41 @@ namespace Microsoft.PowerShell.Commands } + // + // HandleNamedDataHashValue helper for hashtable structured query builder. + // Constructs and returns named event data field XPath portion as a string. + // Fix Issue #2327 + // + private string HandleNamedDataHashValue(String key, Object value) + { + string ret = ""; + Array dataArray = value as Array; + if (dataArray != null) + { + ret += "("; + for (int i = 0; i < dataArray.Length; i++) + { + ret += string.Format(CultureInfo.InvariantCulture, + NamedDataTemplate, + key, dataArray.GetValue(i).ToString()); + if (i < (dataArray.Length - 1)) + { + ret += " or "; + } + } + ret += ")"; + } + else + { + ret += string.Format(CultureInfo.InvariantCulture, + NamedDataTemplate, + key, value); + } + + return ret; + } + + // // Helper checking whether at least one of log, _path, provider is specified. // It will ThrowTerminatingError in case none of those keys are present. @@ -1766,15 +1806,19 @@ namespace Microsoft.PowerShell.Commands Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, key)); ThrowTerminatingError(new ErrorRecord(exc, "NullNotAllowedInHashtable", ErrorCategory.InvalidArgument, key)); } - else if (value is Array) + else { - foreach (Object elt in (Array)value) + Array eltArray = value as Array; + if (eltArray != null) { - if (elt == null) + foreach (Object elt in eltArray) { - string msg = _resourceMgr.GetString("NullNotAllowedInHashtable"); - Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, key)); - ThrowTerminatingError(new ErrorRecord(exc, "NullNotAllowedInHashtable", ErrorCategory.InvalidArgument, key)); + if (elt == null) + { + string msg = _resourceMgr.GetString("NullNotAllowedInHashtable"); + Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, key)); + ThrowTerminatingError(new ErrorRecord(exc, "NullNotAllowedInHashtable", ErrorCategory.InvalidArgument, key)); + } } } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Diagnostics/Get-WinEvent.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Diagnostics/Get-WinEvent.Tests.ps1 index 77cfca7489..e86bdc487a 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Diagnostics/Get-WinEvent.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Diagnostics/Get-WinEvent.Tests.ps1 @@ -85,8 +85,7 @@ Describe 'Get-WinEvent' -Tags "CI" { @($results).Count | Should be 1 $results.RecordId | should be 10 } - <# - It 'Get-WinEvent can retrieve events with UserData queries using FilterHashtable' { + It 'Get-WinEvent can retrieve events with UserData queries using FilterHashtable (one value)' { # this relies on apriori knowledge about the log file # the provided log file has been edited to remove MS PII, so we must use -ea silentlycontinue $eventLogFile = [io.path]::Combine($PSScriptRoot, "assets", "Saved-Events.evtx") @@ -95,7 +94,26 @@ Describe 'Get-WinEvent' -Tags "CI" { @($results).Count | Should be 1 $results.RecordId | should be 10 } - #> + It 'Get-WinEvent can retrieve events with UserData queries using FilterHashtable (array of values)' { + # this relies on apriori knowledge about the log file + # the provided log file has been edited to remove MS PII, so we must use -ea silentlycontinue + $eventLogFile = [io.path]::Combine($PSScriptRoot, "assets", "Saved-Events.evtx") + $filter = @{ path = "$eventLogFile"; DriverName = "Remote Desktop Easy Print", "Microsoft enhanced Point and Print compatibility driver" } + $results = Get-WinEvent -filterHashtable $filter -ea silentlycontinue + @($results).Count | Should be 2 + ($results.RecordId -contains 9) | should be $true + ($results.RecordId -contains 11) | should be $true + } + It 'Get-WinEvent can retrieve events with UserData queries using FilterHashtable (multiple named params)' { + # this relies on apriori knowledge about the log file + # the provided log file has been edited to remove MS PII, so we must use -ea silentlycontinue + $eventLogFile = [io.path]::Combine($PSScriptRoot, "assets", "Saved-Events.evtx") + $filter = @{ path = "$eventLogFile"; PackageAware="Not package aware"; DriverName = "Remote Desktop Easy Print", "Microsoft enhanced Point and Print compatibility driver" } + $results = Get-WinEvent -filterHashtable $filter -ea silentlycontinue + @($results).Count | Should be 2 + ($results.RecordId -contains 9) | should be $true + ($results.RecordId -contains 11) | should be $true + } It 'Get-WinEvent can retrieve events with UserData queries using FilterXPath' { # this relies on apriori knowledge about the log file # the provided log file has been edited to remove MS PII, so we must use -ea silentlycontinue