Add support <Suppress> in Get-WinEvent -FilterHashtable (#2506)

* Add support <Suppress> in Get-WinEvent -FilterHashtable

1. Add support <Suppress>
2. Refacrtoring BuildStructuredQueryFromHashTable() to use StringBuilder
3. Add tests

* Changelog
This commit is contained in:
iSazonov
2016-10-27 14:54:14 -07:00
committed by Mike Richmond
parent 5c6a11213b
commit baeec066d9
3 changed files with 170 additions and 126 deletions
+5
View File
@@ -4,6 +4,11 @@ Changelog
Unreleased
----------
v6.0.0-alpha.12 - 2016-10-31
----------------------------
- Add support <Suppress> in Get-WinEvent -FilterHashtable
v6.0.0-alpha.11 - 2016-10-17
----------------------------
- Add '-Title' to 'Get-Credential' and unify the prompt experience
@@ -357,7 +357,10 @@ namespace Microsoft.PowerShell.Commands
private const string queryListClose = "</QueryList>";
private const string queryTemplate = "<Query Id=\"{0}\" Path=\"{1}\"><Select Path=\"{1}\">{2}</Select></Query>";
private const string queryOpenerTemplate = "<Query Id=\"{0}\" Path=\"{1}\"><Select Path=\"{1}\">*";
private const string queryCloser = "</Select></Query>";
private const string queryCloser = "</Query>";
private const string SelectCloser = "</Select>";
private const string suppressOpener = "<Suppress>*";
private const string suppressCloser = "</Suppress>";
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";
/// <summary>
@@ -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<string, string> queriedLogsQueryMap = new Dictionary<string, string>();
//
// queriedLogsQueryMapSuppress is the same as queriedLogsQueryMap but for <Suppress>
//
Dictionary<string, string> queriedLogsQueryMapSuppress = new Dictionary<string, string>();
//
// 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 <Suppress>
//
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 <Suppress> 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 <Suppress>*xpathStringSuppress</Suppress> 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.
@@ -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
}