Users/abcy/libupdate (#496)

* updated dependencies

* - Improved global param compilation for multiple rules

* update version number

* updated the changelog
pull/498/head v5.0.0
Abbas Cyclewala 2023-07-08 15:35:01 +05:30 committed by GitHub
parent caf41e3cd3
commit 47e7809591
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 103 additions and 28 deletions

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -37,7 +37,9 @@ namespace RulesEngine.ExpressionBuilders
}
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);
}

View File

@ -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>();
}
}
}

View File

@ -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>

View File

@ -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;
@ -31,6 +33,8 @@ namespace RulesEngine.Models
}
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>

View File

@ -47,7 +47,7 @@ namespace RulesEngine
/// <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)
{
@ -56,10 +56,12 @@ namespace RulesEngine
}
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)
@ -100,7 +102,7 @@ namespace RulesEngine
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)
{
@ -227,6 +229,12 @@ namespace RulesEngine
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)

View File

@ -7,6 +7,7 @@ using Newtonsoft.Json.Linq;
using RulesEngine.Actions;
using RulesEngine.Exceptions;
using RulesEngine.ExpressionBuilders;
using RulesEngine.Extensions;
using RulesEngine.HelperFunctions;
using RulesEngine.Interfaces;
using RulesEngine.Models;
@ -48,7 +49,7 @@ namespace RulesEngine
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();
@ -286,9 +287,16 @@ namespace RulesEngine
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);
@ -313,12 +321,15 @@ namespace RulesEngine
{
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);
}

View File

@ -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>
@ -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>

View File

@ -388,10 +388,12 @@ namespace RulesEngine.UnitTest
[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;
@ -851,7 +853,7 @@ namespace RulesEngine.UnitTest
}
[ExcludeFromCodeCoverage]
private class TestInstanceUtils
public class TestInstanceUtils
{
public bool CheckExists(string str)
{

View File

@ -28,8 +28,8 @@ namespace RulesEngine.UnitTest
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));
}
}
}

View File

@ -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>

View File

@ -101,6 +101,7 @@ namespace RulesEngine.UnitTest
[Theory]
[InlineData("GlobalParamsOnly")]
[InlineData("LocalParamsOnly2")]
[InlineData("GlobalParamsOnlyWithComplexInput")]
public async Task ErrorInScopedParam_ShouldAppearAsErrorMessage(string workflowName)
{
var workflow = GetWorkflowList();
@ -386,9 +387,13 @@ namespace RulesEngine.UnitTest
new Rule {
RuleName = "TrueTest",
Expression = "globalParam1 == \"hello\""
},
new Rule {
RuleName = "TrueTest2",
Expression = "globalParam1.ToUpper() == \"HELLO\""
}
}
},
}
};
}
}