Repository: microsoft/ApplicationInspector
File: AppInspector.RulesEngine/AbstractRuleSet.cs (C#)

Code before:

    /// <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));
    }

    /// <summary>
    ///     Convert an AppInspector rule into an OAT rule.
    /// </summary>
    /// <param name="rule">The <see cref="Rule" /> to convert.</param>
    /// <returns>A <see cref="ConvertedOatRule" /> if the AI rule was valid otherwise null.</returns>
    public ConvertedOatRule? AppInspectorRuleToOatRule(Rule rule)
    {
        var clauses = new List<Clause>();
        var clauseNumber = 0;
        var expression = new StringBuilder("(");

        foreach (var pattern in rule.Patterns)
        {
            if (EnableNonBacktrackingRegex && !pattern.Modifiers.Contains("b") && !pattern.Modifiers.Contains("nb"))
            {
                pattern.Modifiers.Add("nb");
            }

            if (GenerateClause(pattern, clauseNumber) is { } clause)
            {
                clauses.Add(clause);
                if (clauseNumber > 0)
                {
                    expression.Append(" OR ");
                }

                expression.Append(clauseNumber);
                clauseNumber++;
            }
            else
            {
                _logger.LogWarning("Clause could not be generated from pattern {pattern}", pattern.Pattern);
            }
        }

        if (clauses.Count > 0)
        {
            expression.Append(')');
        }

Review comment (bug):
This is an interesting edge case we probably want to some how catch in the validator. B and nb are exclusive options.  

Code after:

    /// <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.Count == 0) &&
            (x.AppInspectorRule.AppliesTo is null || x.AppInspectorRule.AppliesTo.Count == 0));
    }

    /// <summary>
    ///     Convert an AppInspector rule into an OAT rule.
    /// </summary>
    /// <param name="rule">The <see cref="Rule" /> to convert.</param>
    /// <returns>A <see cref="ConvertedOatRule" /> if the AI rule was valid otherwise null.</returns>
    public ConvertedOatRule? AppInspectorRuleToOatRule(Rule rule)
    {
        var clauses = new List<Clause>();
        var clauseNumber = 0;
        var expression = new StringBuilder("(");

        foreach (var pattern in rule.Patterns)
        {
            // "b" and "nb" can be added manually to rules. Options are exclusive.
            if (EnableNonBacktrackingRegex)
            {
                // non-backtracking on. "b" will override "nb"
                if (pattern.Modifiers.Any(m => m.Equals("b", StringComparison.InvariantCultureIgnoreCase)))
                {
                    pattern.Modifiers.RemoveAll(m => m.Equals("nb", StringComparison.InvariantCultureIgnoreCase));
                }
                else
                {
                    if (!pattern.Modifiers.Any(m => m.Equals("nb", StringComparison.InvariantCultureIgnoreCase)))
                    {
                        pattern.Modifiers.Add("nb");
                    }
                }    
               
            }
            else
            {
                // backtracking on. "nb" will override "b"
                if (pattern.Modifiers.Any(m => m.Equals("nb", StringComparison.InvariantCultureIgnoreCase)))
                {
                    pattern.Modifiers.RemoveAll(m => m.Equals("b", StringComparison.InvariantCultureIgnoreCase));
                }

                // "b" is a default option for regex engine, so no need to add "b" explicitly