From baeec066d9636f7769eca3ced7beaae84397ca14 Mon Sep 17 00:00:00 2001 From: iSazonov Date: Fri, 28 Oct 2016 03:54:14 +0600 Subject: [PATCH] Add support in Get-WinEvent -FilterHashtable (#2506) * Add support in Get-WinEvent -FilterHashtable 1. Add support 2. Refacrtoring BuildStructuredQueryFromHashTable() to use StringBuilder 3. Add tests * Changelog --- CHANGELOG.md | 5 + .../GetEventCommand.cs | 265 +++++++++--------- .../Get-WinEvent.Tests.ps1 | 26 +- 3 files changed, 170 insertions(+), 126 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8ba99c1d9..33f45e02cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Changelog Unreleased ---------- +v6.0.0-alpha.12 - 2016-10-31 +---------------------------- +- Add support in Get-WinEvent -FilterHashtable + + v6.0.0-alpha.11 - 2016-10-17 ---------------------------- - Add '-Title' to 'Get-Credential' and unify the prompt experience diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs index cf4a414c58..4c9cc90d28 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs @@ -357,7 +357,10 @@ namespace Microsoft.PowerShell.Commands private const string queryListClose = ""; private const string queryTemplate = ""; private const string queryOpenerTemplate = ""; + private const string queryCloser = ""; + private const string SelectCloser = ""; + private const string suppressOpener = "*"; + private const string suppressCloser = ""; private const string propOpen = "["; private const string propClose = "]"; private const string filePrefix = "file://"; @@ -399,6 +402,7 @@ namespace Microsoft.PowerShell.Commands private const string hashkey_endtime_lc = "endtime"; private const string hashkey_userid_lc = "userid"; private const string hashkey_data_lc = "data"; + private const string hashkey_supress_lc = "suppresshashfilter"; /// @@ -994,21 +998,110 @@ namespace Microsoft.PowerShell.Commands return result.ToString(); } + // + // BuildXPathFromHashTable() build xpath from hashtable + // + private string BuildXPathFromHashTable(Hashtable hash) + { + StringBuilder xpathString = new StringBuilder(""); + bool bDateTimeHandled = false; + + foreach (string key in hash.Keys) + { + string added = ""; + + switch (key.ToLowerInvariant()) + { + case hashkey_logname_lc: + case hashkey_path_lc: + case hashkey_providername_lc: + break; + case hashkey_id_lc: + added = HandleEventIdHashValue(hash[key]); + break; + + case hashkey_level_lc: + added = HandleLevelHashValue(hash[key]); + break; + + case hashkey_keywords_lc: + added = HandleKeywordHashValue(hash[key]); + break; + + case hashkey_starttime_lc: + if (bDateTimeHandled) + { + break; + } + + added = HandleStartTimeHashValue(hash[key], hash); + + bDateTimeHandled = true; + break; + + case hashkey_endtime_lc: + if (bDateTimeHandled) + { + break; + } + + added = HandleEndTimeHashValue(hash[key], hash); + + bDateTimeHandled = true; + break; + + case hashkey_data_lc: + added = HandleDataHashValue(hash[key]); + break; + + case hashkey_userid_lc: + added = HandleContextHashValue(hash[key]); + break; + + case hashkey_supress_lc: + break; + default: + { + // + // None of the recognized values: this must be a named event data field + // + // Fix Issue #2327 + added = HandleNamedDataHashValue(key, hash[key]); + + } + break; + } + + if (added.Length > 0) + { + if (xpathString.Length != 0) + { + xpathString.Append(" and "); + } + xpathString.Append(added); + } + + } + + return xpathString.ToString(); + } + // // BuildStructuredQueryFromHashTable() helper. // Builds a structured query from the hashtable (Selector) argument. // private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession) { - string result = ""; + StringBuilder result = new StringBuilder(""); - result = queryListOpen; + result.Append(queryListOpen); uint queryId = 0; foreach (Hashtable hash in _selector) { string xpathString = ""; + string xpathStringSuppress = ""; CheckHashTableForQueryPathPresence(hash); @@ -1018,6 +1111,11 @@ namespace Microsoft.PowerShell.Commands // Dictionary queriedLogsQueryMap = new Dictionary(); + // + // queriedLogsQueryMapSuppress is the same as queriedLogsQueryMap but for + // + Dictionary queriedLogsQueryMapSuppress = new Dictionary(); + // // Process log, _path, or provider parameters first // to create initial partially-filled query templates. @@ -1046,6 +1144,8 @@ namespace Microsoft.PowerShell.Commands { queriedLogsQueryMap.Add(logName.ToLowerInvariant(), string.Format(CultureInfo.InvariantCulture, queryOpenerTemplate, queryId++, logName)); + queriedLogsQueryMapSuppress.Add(logName.ToLowerInvariant(), + string.Format(CultureInfo.InvariantCulture, suppressOpener, queryId++, logName)); } } if (hash.ContainsKey(hashkey_path_lc)) @@ -1059,6 +1159,8 @@ namespace Microsoft.PowerShell.Commands { queriedLogsQueryMap.Add(filePrefix + resolvedPath.ToLowerInvariant(), string.Format(CultureInfo.InvariantCulture, queryOpenerTemplate, queryId++, filePrefix + resolvedPath)); + queriedLogsQueryMapSuppress.Add(filePrefix + resolvedPath.ToLowerInvariant(), + string.Format(CultureInfo.InvariantCulture, suppressOpener, queryId++, filePrefix + resolvedPath)); } } } @@ -1069,6 +1171,8 @@ namespace Microsoft.PowerShell.Commands { queriedLogsQueryMap.Add(filePrefix + resolvedPath.ToLowerInvariant(), string.Format(CultureInfo.InvariantCulture, queryOpenerTemplate, queryId++, filePrefix + resolvedPath)); + queriedLogsQueryMapSuppress.Add(filePrefix + resolvedPath.ToLowerInvariant(), + string.Format(CultureInfo.InvariantCulture, suppressOpener, queryId++, filePrefix + resolvedPath)); } } } @@ -1101,6 +1205,8 @@ namespace Microsoft.PowerShell.Commands string query = string.Format(CultureInfo.InvariantCulture, queryOpenerTemplate, queryId++, keyLogName); queriedLogsQueryMap.Add(keyLogName.ToLowerInvariant(), query + "[" + providersPredicate); + queriedLogsQueryMapSuppress.Add(keyLogName.ToLowerInvariant(), + string.Format(CultureInfo.InvariantCulture, suppressOpener, queryId++, keyLogName.ToLowerInvariant())); } } else @@ -1124,6 +1230,7 @@ namespace Microsoft.PowerShell.Commands { WriteVerbose(string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("SpecifiedProvidersDontWriteToLog"), queriedLog)); queriedLogsQueryMap.Remove(queriedLog); + queriedLogsQueryMapSuppress.Remove(queriedLog); bRemovedIrrelevantLogs = true; } } @@ -1154,119 +1261,28 @@ namespace Microsoft.PowerShell.Commands // At this point queriedLogsQueryMap contains all the query openings: missing the actual XPaths // Let's build xpathString to attach to each query opening. // - bool bDateTimeHandled = false; - foreach (string key in hash.Keys) + xpathString = BuildXPathFromHashTable(hash); + + // + // Build xpath for + // + Hashtable suppresshash = hash[hashkey_supress_lc] as Hashtable; + if (suppresshash != null) { - string added = ""; - - switch (key.ToLowerInvariant()) - { - case hashkey_logname_lc: - case hashkey_path_lc: - case hashkey_providername_lc: - break; - case hashkey_id_lc: - added = HandleEventIdHashValue(hash[key]); - if (added.Length > 0) - { - ExtendPredicate(ref xpathString); - xpathString += added; - } - break; - - case hashkey_level_lc: - added = HandleLevelHashValue(hash[key]); - if (added.Length > 0) - { - ExtendPredicate(ref xpathString); - xpathString += added; - } - break; - - case hashkey_keywords_lc: - added = HandleKeywordHashValue(hash[key]); - if (added.Length > 0) - { - ExtendPredicate(ref xpathString); - xpathString += added; - } - break; - - case hashkey_starttime_lc: - if (bDateTimeHandled) - { - break; - } - added = HandleStartTimeHashValue(hash[key], hash); - if (added.Length > 0) - { - ExtendPredicate(ref xpathString); - xpathString += added; - } - - bDateTimeHandled = true; - break; - - case hashkey_endtime_lc: - if (bDateTimeHandled) - { - break; - } - - added = HandleEndTimeHashValue(hash[key], hash); - if (added.Length > 0) - { - ExtendPredicate(ref xpathString); - xpathString += added; - } - - bDateTimeHandled = true; - break; - - case hashkey_data_lc: - added = HandleDataHashValue(hash[key]); - if (added.Length > 0) - { - ExtendPredicate(ref xpathString); - xpathString += added; - } - break; - - case hashkey_userid_lc: - added = HandleContextHashValue(hash[key]); - if (added.Length > 0) - { - ExtendPredicate(ref xpathString); - xpathString += added; - } - break; - - default: - { - // - // None of the recognized values: this must be a named event data field - // - // Fix Issue #2327 - added = HandleNamedDataHashValue(key, hash[key]); - if (added.Length > 0) - { - ExtendPredicate(ref xpathString); - xpathString += added; - } - - } - break; - } + xpathStringSuppress = BuildXPathFromHashTable(suppresshash); } // // Complete each query with the XPath. // Handle the case where the query opener already has provider predicate(s). // Add the queries from queriedLogsQueryMap into the resulting string. + // Add from queriedLogsQueryMapSuppress into the resulting string. // - foreach (string query in queriedLogsQueryMap.Values) + foreach (string keyLogName in queriedLogsQueryMap.Keys) { - result += query; + // For every Log a separate query is + string query = queriedLogsQueryMap[keyLogName]; + result.Append(query); if (query.EndsWith("*", StringComparison.OrdinalIgnoreCase)) { @@ -1275,7 +1291,7 @@ namespace Microsoft.PowerShell.Commands // if (xpathString.Length != 0) { - result += propOpen + xpathString + propClose; + result.Append(propOpen).Append(xpathString).Append(propClose); } } else @@ -1285,19 +1301,30 @@ namespace Microsoft.PowerShell.Commands // if (xpathString.Length != 0) { - result += " and " + xpathString; + result.Append(" and ").Append(xpathString); } - result += propClose; + result.Append(propClose); } - result += queryCloser; + result.Append(SelectCloser); + + if (xpathStringSuppress.Length != 0) + { + // Add *xpathStringSuppress into query + string suppress = queriedLogsQueryMapSuppress[keyLogName]; + result.Append(suppress); + result.Append(propOpen).Append(xpathStringSuppress).Append(propClose); + result.Append(suppressCloser); + } + + result.Append(queryCloser); } } //end foreach hashtable - result += queryListClose; + result.Append(queryListClose); - return result; + return result.ToString(); } // @@ -1656,18 +1683,6 @@ namespace Microsoft.PowerShell.Commands return true; } - // - // ExtendPredicate helper for the query builder. - // Extends the XPath predicate string. - // - private void ExtendPredicate(ref string xpathString) - { - if (xpathString.Length != 0) - { - xpathString += " and "; - } - } - // // KeywordStringToInt64 helper converts a string to Int64. 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 e86bdc487a..c16ed2f795 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Diagnostics/Get-WinEvent.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Diagnostics/Get-WinEvent.Tests.ps1 @@ -123,7 +123,31 @@ Describe 'Get-WinEvent' -Tags "CI" { @($results).Count | Should be 1 $results.RecordId | should be 10 } - } # Get-WinEvent works only on windows + } + Context "Get-WinEvent Queries with SuppressHashFilter" { + It 'Get-WinEvent can suppress events by Id' { + # 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"} + $results = Get-WinEvent -filterHashtable $filter -ea silentlycontinue + $filterSuppress = @{ path = "$eventLogFile"; SuppressHashFilter=@{Id=370}} + $resultsSuppress = Get-WinEvent -filterHashtable $filterSuppress -ea silentlycontinue + @($results).Count | Should be 3 + @($resultsSuppress).Count | Should be 2 + } + It 'Get-WinEvent can suppress events by UserData' { + # 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"} + $results = Get-WinEvent -filterHashtable $filter -ea silentlycontinue + $filterSuppress = @{ path = "$eventLogFile"; SuppressHashFilter=@{Param2 = "Windows x64"}} + $resultsSuppress = Get-WinEvent -filterHashtable $filterSuppress -ea silentlycontinue + @($results).Count | Should be 3 + @($resultsSuppress).Count | Should be 2 + } + } It 'can query a System log' { Get-WinEvent -LogName System -MaxEvents 1 | Should Not BeNullOrEmpty }