Skip to content

Commit

Permalink
Users/abcy/libupdate (#496)
Browse files Browse the repository at this point in the history
* updated dependencies

* - Improved global param compilation for multiple rules

* update version number

* updated the changelog
  • Loading branch information
abbasc52 committed Jul 8, 2023
1 parent caf41e3 commit 47e7809
Show file tree
Hide file tree
Showing 14 changed files with 103 additions and 28 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All notable changes to this project will be documented in this file.

## [5.0.0]
- Fixed security bug related to System.Dynamic.Linq.Core

### Breaking Changes
- As a part of security bug fix, method call for only registered types via reSettings will be allowed. This only impacts strongly typed inputs and nested types


## [4.0.0]
- RulesEngine is now available in both dotnet 6 and netstandard 2.0
- Dependency on ILogger, MemoryCache have been removed
Expand Down
2 changes: 1 addition & 1 deletion benchmark/RulesEngineBenchmark/RulesEngineBenchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
<!--<PackageReference Include="RulesEngine" Version="3.0.2" />-->
</ItemGroup>

Expand Down
4 changes: 2 additions & 2 deletions demo/DemoApp.EFDataExample/DemoApp.EFDataExample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.8" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 3 additions & 1 deletion src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ private void PopulateMethodInfo()
}
public Expression Parse(string expression, ParameterExpression[] parameters, Type returnType)
{
var config = new ParsingConfig { CustomTypeProvider = new CustomTypeProvider(_reSettings.CustomTypes) };
var config = new ParsingConfig {
CustomTypeProvider = new CustomTypeProvider(_reSettings.CustomTypes)
};
return new ExpressionParser(parameters, expression, new object[] { }, config).Parse(returnType);

}
Expand Down
19 changes: 19 additions & 0 deletions src/RulesEngine/Extensions/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RulesEngine.Extensions
{
internal static class EnumerableExtensions
{
public static IEnumerable<T> Safe<T>(this IEnumerable<T> enumerable)
{
return enumerable ?? Enumerable.Empty<T>();
}
}
}
17 changes: 17 additions & 0 deletions src/RulesEngine/Models/ReSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,23 @@ namespace RulesEngine.Models
[ExcludeFromCodeCoverage]
public class ReSettings
{

public ReSettings() { }

// create a copy of settings
internal ReSettings(ReSettings reSettings)
{
CustomTypes = reSettings.CustomTypes;
CustomActions = reSettings.CustomActions;
EnableExceptionAsErrorMessage = reSettings.EnableExceptionAsErrorMessage;
IgnoreException = reSettings.IgnoreException;
EnableFormattedErrorMessage = reSettings.EnableFormattedErrorMessage;
EnableScopedParams = reSettings.EnableScopedParams;
NestedRuleExecutionMode = reSettings.NestedRuleExecutionMode;
CacheConfig = reSettings.CacheConfig;
}


/// <summary>
/// Get/Set the custom types to be used in Rule expressions
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions src/RulesEngine/Models/Workflow.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Newtonsoft.Json.Converters;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
Expand Down Expand Up @@ -31,6 +33,8 @@ public IEnumerable<string> WorkflowRulesToInject {
}
public IEnumerable<string> WorkflowsToInject { get; set; }

public RuleExpressionType RuleExpressionType { get; set; } = RuleExpressionType.LambdaExpression;

/// <summary>
/// Gets or Sets the global params which will be applicable to all rules
/// </summary>
Expand Down
14 changes: 11 additions & 3 deletions src/RulesEngine/RuleCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ internal RuleCompiler(RuleExpressionBuilderFactory expressionBuilderFactory, ReS
/// <param name="input"></param>
/// <param name="ruleParam"></param>
/// <returns>Compiled func delegate</returns>
internal RuleFunc<RuleResultTree> CompileRule(Rule rule, RuleParameter[] ruleParams, ScopedParam[] globalParams)
internal RuleFunc<RuleResultTree> CompileRule(Rule rule, RuleExpressionType ruleExpressionType, RuleParameter[] ruleParams, Lazy<RuleExpressionParameter[]> globalParams)
{
if (rule == null)
{
Expand All @@ -56,10 +56,12 @@ internal RuleFunc<RuleResultTree> CompileRule(Rule rule, RuleParameter[] rulePar
}
try
{
var globalParamExp = GetRuleExpressionParameters(rule.RuleExpressionType,globalParams, ruleParams);
var globalParamExp = globalParams.Value;
var extendedRuleParams = ruleParams.Concat(globalParamExp.Select(c => new RuleParameter(c.ParameterExpression.Name,c.ParameterExpression.Type)))
.ToArray();
var ruleExpression = GetDelegateForRule(rule, extendedRuleParams);


return GetWrappedRuleFunc(rule,ruleExpression,ruleParams,globalParamExp);
}
catch (Exception ex)
Expand Down Expand Up @@ -100,7 +102,7 @@ private RuleFunc<RuleResultTree> GetDelegateForRule(Rule rule, RuleParameter[] r
return GetWrappedRuleFunc(rule, ruleFn, ruleParams, scopedParamList);
}

private RuleExpressionParameter[] GetRuleExpressionParameters(RuleExpressionType ruleExpressionType,IEnumerable<ScopedParam> localParams, RuleParameter[] ruleParams)
internal RuleExpressionParameter[] GetRuleExpressionParameters(RuleExpressionType ruleExpressionType,IEnumerable<ScopedParam> localParams, RuleParameter[] ruleParams)
{
if(!_reSettings.EnableScopedParams)
{
Expand Down Expand Up @@ -227,6 +229,12 @@ private RuleFunc<RuleResultTree> BuildNestedRuleFunc(Rule parentRule, Expression
return (isSuccess, resultList);
}

internal Func<object[],Dictionary<string,object>> CompileScopedParams(RuleExpressionType ruleExpressionType, RuleParameter[] ruleParameters,RuleExpressionParameter[] ruleExpParams)
{
return GetExpressionBuilder(ruleExpressionType).CompileScopedParams(ruleParameters, ruleExpParams);

}

private RuleFunc<RuleResultTree> GetWrappedRuleFunc(Rule rule, RuleFunc<RuleResultTree> ruleFunc,RuleParameter[] ruleParameters,RuleExpressionParameter[] ruleExpParams)
{
if(ruleExpParams.Length == 0)
Expand Down
21 changes: 16 additions & 5 deletions src/RulesEngine/RulesEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using RulesEngine.Actions;
using RulesEngine.Exceptions;
using RulesEngine.ExpressionBuilders;
using RulesEngine.Extensions;
using RulesEngine.HelperFunctions;
using RulesEngine.Interfaces;
using RulesEngine.Models;
Expand Down Expand Up @@ -48,7 +49,7 @@ public RulesEngine(Workflow[] Workflows, ReSettings reSettings = null) : this(re

public RulesEngine(ReSettings reSettings = null)
{
_reSettings = reSettings ?? new ReSettings();
_reSettings = reSettings == null ? new ReSettings(): new ReSettings(reSettings);
if(_reSettings.CacheConfig == null)
{
_reSettings.CacheConfig = new MemCacheConfig();
Expand Down Expand Up @@ -286,9 +287,16 @@ private bool RegisterRule(string workflowName, params RuleParameter[] ruleParams
if (workflow != null)
{
var dictFunc = new Dictionary<string, RuleFunc<RuleResultTree>>();
_reSettings.CustomTypes = _reSettings.CustomTypes.Safe().Union(ruleParams.Select(c => c.Type)).ToArray();
// add separate compilation for global params

var globalParamExp = new Lazy<RuleExpressionParameter[]>(
() => _ruleCompiler.GetRuleExpressionParameters(workflow.RuleExpressionType, workflow.GlobalParams, ruleParams)
);

foreach (var rule in workflow.Rules.Where(c => c.Enabled))
{
dictFunc.Add(rule.RuleName, CompileRule(rule, ruleParams, workflow.GlobalParams?.ToArray()));
dictFunc.Add(rule.RuleName, CompileRule(rule,workflow.RuleExpressionType, ruleParams, globalParamExp));
}

_rulesCache.AddOrUpdateCompiledRule(compileRulesKey, dictFunc);
Expand All @@ -313,12 +321,15 @@ private RuleFunc<RuleResultTree> CompileRule(string workflowName, string ruleNam
{
throw new ArgumentException($"Workflow `{workflowName}` does not contain any rule named `{ruleName}`");
}
return CompileRule(currentRule, ruleParameters, workflow.GlobalParams?.ToArray());
var globalParamExp = new Lazy<RuleExpressionParameter[]>(
() => _ruleCompiler.GetRuleExpressionParameters(workflow.RuleExpressionType, workflow.GlobalParams, ruleParameters)
);
return CompileRule(currentRule,workflow.RuleExpressionType, ruleParameters, globalParamExp);
}

private RuleFunc<RuleResultTree> CompileRule(Rule rule, RuleParameter[] ruleParams, ScopedParam[] scopedParams)
private RuleFunc<RuleResultTree> CompileRule(Rule rule, RuleExpressionType ruleExpressionType, RuleParameter[] ruleParams, Lazy<RuleExpressionParameter[]> scopedParams)
{
return _ruleCompiler.CompileRule(rule, ruleParams, scopedParams);
return _ruleCompiler.CompileRule(rule, ruleExpressionType, ruleParams, scopedParams);
}


Expand Down
10 changes: 5 additions & 5 deletions src/RulesEngine/RulesEngine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<Version>4.0.0</Version>
<Version>5.0.0</Version>
<Copyright>Copyright (c) Microsoft Corporation.</Copyright>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageProjectUrl>https://github.com/microsoft/RulesEngine</PackageProjectUrl>
Expand Down Expand Up @@ -31,12 +31,12 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="FastExpressionCompiler" Version="3.3.3" />
<PackageReference Include="FluentValidation" Version="11.2.2" />
<PackageReference Include="FastExpressionCompiler" Version="3.3.4" />
<PackageReference Include="FluentValidation" Version="11.5.2" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Linq" Version="4.3.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.22" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.3.3" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
</ItemGroup>
Expand Down
8 changes: 5 additions & 3 deletions test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -388,10 +388,12 @@ public async Task ExecuteRule_WithInjectedUtils_ReturnsListOfRuleResultTree(stri
[InlineData("rules6.json")]
public async Task ExecuteRule_RuleWithMethodExpression_ReturnsSucess(string ruleFileName)
{
var re = GetRulesEngine(ruleFileName);

Func<bool> func = () => true;

var re = GetRulesEngine(ruleFileName, new ReSettings {
CustomTypes = new[] { typeof(Func<bool>) }
});

dynamic input1 = new ExpandoObject();
input1.Property1 = "hello";
input1.Boolean = false;
Expand Down Expand Up @@ -851,7 +853,7 @@ private static dynamic[] GetInputs4()
}

[ExcludeFromCodeCoverage]
private class TestInstanceUtils
public class TestInstanceUtils
{
public bool CheckExists(string str)
{
Expand Down
4 changes: 2 additions & 2 deletions test/RulesEngine.UnitTest/RuleCompilerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public void RuleCompiler_CompileRule_ThrowsException()
var reSettings = new ReSettings();
var parser = new RuleExpressionParser(reSettings);
var compiler = new RuleCompiler(new RuleExpressionBuilderFactory(reSettings, parser),null);
Assert.Throws<ArgumentNullException>(() => compiler.CompileRule(null, null,null));
Assert.Throws<ArgumentNullException>(() => compiler.CompileRule(null, new RuleParameter[] { null },null));
Assert.Throws<ArgumentNullException>(() => compiler.CompileRule(null, RuleExpressionType.LambdaExpression,null,null));
Assert.Throws<ArgumentNullException>(() => compiler.CompileRule(null, RuleExpressionType.LambdaExpression, new RuleParameter[] { null },null));
}
}
}
10 changes: 5 additions & 5 deletions test/RulesEngine.UnitTest/RulesEngine.UnitTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
<DelaySign>True</DelaySign>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.17.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="System.Text.Json" Version="6.0.6" />
<PackageReference Include="AutoFixture" Version="4.18.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="System.Text.Json" Version="7.0.3" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
7 changes: 6 additions & 1 deletion test/RulesEngine.UnitTest/ScopedParamsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public async Task DisabledScopedParam_ShouldReflect(string workflowName, bool[]
[Theory]
[InlineData("GlobalParamsOnly")]
[InlineData("LocalParamsOnly2")]
[InlineData("GlobalParamsOnlyWithComplexInput")]
public async Task ErrorInScopedParam_ShouldAppearAsErrorMessage(string workflowName)
{
var workflow = GetWorkflowList();
Expand Down Expand Up @@ -386,9 +387,13 @@ private Workflow[] GetWorkflowList()
new Rule {
RuleName = "TrueTest",
Expression = "globalParam1 == \"hello\""
},
new Rule {
RuleName = "TrueTest2",
Expression = "globalParam1.ToUpper() == \"HELLO\""
}
}
},
}
};
}
}
Expand Down

0 comments on commit 47e7809

Please sign in to comment.