Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Users/abcy/libupdate #496

Merged
merged 4 commits into from
Jul 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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