Catch and log deserialization errors in StringToRules (#486)

This commit is contained in:
Gabe Stocco 2022-08-01 21:26:12 -07:00 коммит произвёл GitHub
Родитель 949273cf04
Коммит 7dfcecdd40
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 159 добавлений и 106 удалений

Просмотреть файл

@ -23,113 +23,25 @@ namespace Microsoft.ApplicationInspector.RulesEngine
protected readonly List<ConvertedOatRule> _oatRules = new();
protected IEnumerable<Rule> _rules { get => _oatRules.Select(x => x.AppInspectorRule); }
private readonly Regex _searchInRegex = new("\\((.*),(.*)\\)", RegexOptions.Compiled);
public void AddPath(string path, string? tag = null)
{
if (Directory.Exists(path))
{
AddDirectory(path, tag);
}
else if (File.Exists(path))
{
AddFile(path, tag);
}
else
{
throw new ArgumentException("The path must exist.", nameof(path));
}
}
/// <summary>
/// Parse a directory with rule files and loads the rules
/// Filters rules within Ruleset by language
/// </summary>
/// <param name="path"> Path to rules folder </param>
/// <param name="tag"> Tag for the rules </param>
public void AddDirectory(string path, string? tag = null)
{
if (!Directory.Exists(path))
throw new DirectoryNotFoundException();
foreach (string filename in Directory.EnumerateFileSystemEntries(path, "*.json", SearchOption.AllDirectories))
{
AddFile(filename, tag);
}
}
/// <summary>
/// Load rules from a file
/// </summary>
/// <param name="filename"> Filename with rules </param>
/// <param name="tag"> Tag for the rules </param>
public void AddFile(string? filename, string? tag = null)
{
if (string.IsNullOrEmpty(filename))
throw new ArgumentException(null, nameof(filename));
if (!File.Exists(filename))
throw new FileNotFoundException();
using StreamReader file = File.OpenText(filename);
AddString(file.ReadToEnd(), filename, tag);
}
/// <summary>
/// Adds the elements of the collection to the Ruleset
/// </summary>
/// <param name="collection"> Collection of rules </param>
public void AddRange(IEnumerable<Rule>? collection)
{
foreach (Rule rule in collection ?? Array.Empty<Rule>())
{
AddRule(rule);
}
}
/// <summary>
/// Add rule into Ruleset
/// </summary>
/// <param name="rule"> </param>
public void AddRule(Rule rule)
{
if (AppInspectorRuleToOatRule(rule) is ConvertedOatRule oatRule)
{
_logger.LogTrace("Attempting to add rule: {RuleId}:{RuleName}", rule.Id, rule.Name);
_oatRules.Add(oatRule);
}
else
{
_logger.LogError("Rule '{RuleId}:{RuleName}' could not be converted into an OAT rule. There may be message in the logs indicating why. You can run rule verification to identify the issue", rule.Id, rule.Name);
}
}
/// <summary>
/// Load rules from JSON string
/// </summary>
/// <param name="jsonstring"> JSON string </param>
/// <param name="sourcename"> Name of the source (file, stream, etc..) </param>
/// <param name="tag"> Tag for the rules </param>
public void AddString(string jsonstring, string sourcename, string? tag = null)
{
AddRange(StringToRules(jsonstring ?? string.Empty, sourcename ?? string.Empty, tag));
}
/// <summary>
/// Filters rules within Ruleset by languages
/// </summary>
/// <param name="languages"> Languages </param>
/// <param name="language"></param>
/// <returns> Filtered rules </returns>
public IEnumerable<ConvertedOatRule> ByLanguage(string language)
{
if (!string.IsNullOrEmpty(language))
{
return _oatRules.Where(x => x.AppInspectorRule.AppliesTo is string[] appliesList && appliesList.Contains(language));
return _oatRules.Where(x => x.AppInspectorRule.AppliesTo is { } appliesList && appliesList.Contains(language));
}
return Array.Empty<ConvertedOatRule>();
}
/// <summary>
/// Filters rules within Ruleset by applies to regexes
/// Filters rules within Ruleset filename
/// </summary>
/// <param name="languages"> Languages </param>
/// <param name="input"></param>
/// <returns> Filtered rules </returns>
public IEnumerable<ConvertedOatRule> ByFilename(string input)
{
@ -140,6 +52,10 @@ namespace Microsoft.ApplicationInspector.RulesEngine
return Array.Empty<ConvertedOatRule>();
}
/// <summary>
/// Get the set of rules that apply to all files
/// </summary>
/// <returns></returns>
public IEnumerable<ConvertedOatRule> GetUniversalRules()
{
return _oatRules.Where(x => (x.AppInspectorRule.FileRegexes is null || x.AppInspectorRule.FileRegexes.Length == 0) && (x.AppInspectorRule.AppliesTo is null || x.AppInspectorRule.AppliesTo.Length == 0));
@ -354,10 +270,16 @@ namespace Microsoft.ApplicationInspector.RulesEngine
};
}
/// <summary>
/// Get the OAT Rules used in this RuleSet.
/// </summary>
/// <returns></returns>
public IEnumerable<ConvertedOatRule> GetOatRules() => _oatRules;
/// <summary>
/// Get the AppInspector Rules contained in this RuleSet.
/// </summary>
/// <returns></returns>
public IEnumerable<Rule> GetAppInspectorRules() => _rules;
internal abstract IEnumerable<Rule> StringToRules(string jsonstring, string sourcename, string? tag = null);
}
}

Просмотреть файл

@ -18,7 +18,7 @@ namespace Microsoft.ApplicationInspector.RulesEngine;
/// The TypedRuleSet allows you to extend the Application Inspector Rule format with your own custom fields and have them be deserialized. They won't be used in processing, but may be used for additional follow up actions.
/// </summary>
/// <typeparam name="T">The Type of the Rule this set holds. It must inherit from <see cref="Rule"/></typeparam>
public class TypedRuleSet<T> : AbstractRuleSet,IEnumerable<T> where T : Rule
public class TypedRuleSet<T> : AbstractRuleSet, IEnumerable<T> where T : Rule
{
/// <summary>
/// Creates instance of TypedRuleSet
@ -32,31 +32,162 @@ public class TypedRuleSet<T> : AbstractRuleSet,IEnumerable<T> where T : Rule
/// Returns an enumerator that iterates through the AI Formatted <see cref="Rule"/>.
/// </summary>
/// <returns> Enumerator </returns>
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetAppInspectorRules().Select(x => x as T).GetEnumerator();
IEnumerator<T> IEnumerable<T>.GetEnumerator() => AppInspectorRulesAsEnumerableT().GetEnumerator();
/// <summary>
/// Returns an enumerator that iterates through the AI Formatted <see cref="Rule"/>.
/// </summary>
/// <returns> Enumerator </returns>
public IEnumerator GetEnumerator() => GetAppInspectorRules().GetEnumerator();
public IEnumerator GetEnumerator() => AppInspectorRulesAsEnumerableT().GetEnumerator();
/// <summary>
/// Returns the set of rules as an <see cref="IEnumerable{T}"/>
/// </summary>
/// <returns></returns>
private IEnumerable<T> AppInspectorRulesAsEnumerableT()
{
foreach (var rule in _rules)
{
if (rule is T ruleAsT)
{
yield return ruleAsT;
}
}
}
/// <summary>
/// Load rules from a file or directory
/// </summary>
/// <param name="path"> File or directory path containing rules</param>
/// <param name="tag"> Tag for the rules </param>
/// <exception cref="ArgumentException">Thrown if the filename is null or empty</exception>
/// <exception cref="FileNotFoundException">Thrown if the specified file cannot be found on the file system</exception>
/// <exception cref="Newtonsoft.Json.JsonSerializationException">Thrown if the specified file cannot be deserialized as a <see cref="List{T}"/></exception>
public void AddPath(string path, string? tag = null)
{
if (Directory.Exists(path))
{
AddDirectory(path, tag);
}
else if (File.Exists(path))
{
AddFile(path, tag);
}
else
{
throw new ArgumentException("The path must exist.", nameof(path));
}
}
/// <summary>
/// Parse a directory with rule files and attempts to load all .json files in the directory as rules
/// </summary>
/// <param name="path"> Path to rules folder </param>
/// <param name="tag"> Tag for the rules </param>
/// <exception cref="ArgumentException">Thrown if the filename is null or empty</exception>
/// <exception cref="FileNotFoundException">Thrown if the specified file cannot be found on the file system</exception>
/// <exception cref="Newtonsoft.Json.JsonSerializationException">Thrown if the specified file cannot be deserialized as a <see cref="List{T}"/></exception>
public void AddDirectory(string path, string? tag = null)
{
if (!Directory.Exists(path))
throw new DirectoryNotFoundException();
foreach (string filename in Directory.EnumerateFileSystemEntries(path, "*.json", SearchOption.AllDirectories))
{
AddFile(filename, tag);
}
}
/// <summary>
/// Load rules from a file
/// </summary>
/// <param name="filename"> Filename with rules </param>
/// <param name="tag"> Tag for the rules </param>
/// <exception cref="ArgumentException">Thrown if the filename is null or empty</exception>
/// <exception cref="FileNotFoundException">Thrown if the specified file cannot be found on the file system</exception>
/// <exception cref="Newtonsoft.Json.JsonSerializationException">Thrown if the specified file cannot be deserialized as a <see cref="List{T}"/></exception>
public void AddFile(string? filename, string? tag = null)
{
if (string.IsNullOrEmpty(filename))
throw new ArgumentException(null, nameof(filename));
if (!File.Exists(filename))
throw new FileNotFoundException();
using StreamReader file = File.OpenText(filename);
AddString(file.ReadToEnd(), filename, tag);
}
/// <summary>
/// Load rules from JSON string
/// </summary>
/// <param name="jsonString"> JSON string </param>
/// <param name="sourceName"> Name of the source (file, stream, etc..) </param>
/// <param name="tag">Additional runtime tag for the rules </param>
/// <returns>If the rules were added successfully</returns>
public void AddString(string jsonString, string sourceName, string? tag = null)
{
if (StringToRules(jsonString ?? string.Empty, sourceName ?? string.Empty, tag) is { } deserializedList)
{
AddRange(deserializedList);
}
}
/// <summary>
/// Adds the elements of the collection to the Ruleset
/// </summary>
/// <param name="collection"> Collection of rules </param>
public void AddRange(IEnumerable<T>? collection)
{
foreach (T rule in collection ?? Array.Empty<T>())
{
AddRule(rule);
}
}
/// <summary>
/// Add rule into Ruleset
/// </summary>
/// <param name="rule"> </param>
public void AddRule(T rule)
{
if (AppInspectorRuleToOatRule(rule) is { } oatRule)
{
_logger.LogTrace("Attempting to add rule: {RuleId}:{RuleName}", rule.Id, rule.Name);
_oatRules.Add(oatRule);
}
else
{
_logger.LogError("Rule '{RuleId}:{RuleName}' could not be converted into an OAT rule. There may be message in the logs indicating why. You can run rule verification to identify the issue", rule.Id, rule.Name);
}
}
/// <summary>
/// Deserialize a string into rules and enumerate them.
/// </summary>
/// <param name="jsonstring"></param>
/// <param name="sourcename"></param>
/// <param name="tag"></param>
/// <param name="jsonString"></param>
/// <param name="sourceName"></param>
/// <param name="tag">Add an additional tag to the rules when added.</param>
/// <returns></returns>
internal override IEnumerable<Rule> StringToRules(string jsonstring, string sourcename, string? tag = null)
internal IEnumerable<T> StringToRules(string jsonString, string sourceName, string? tag = null)
{
List<T>? ruleList = JsonConvert.DeserializeObject<List<T>>(jsonstring);
List<T>? ruleList = null;
try
{
ruleList = JsonConvert.DeserializeObject<List<T>>(jsonString);
}
catch (JsonSerializationException jsonSerializationException)
{
_logger.LogError("Failed to deserialize '{0}' at Line {1} Column {2}", sourceName, jsonSerializationException.LineNumber, jsonSerializationException.LinePosition);
throw;
}
if (ruleList is not null)
{
foreach (T r in ruleList)
{
r.Source = sourcename;
r.RuntimeTag = tag ?? "";
r.Source = sourceName;
r.RuntimeTag = tag;
yield return r;
}
}