diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs index 3f54beed9..cccb56c48 100644 --- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs +++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq.Dynamic.Core.Extensions; using System.Linq.Dynamic.Core.Validation; using System.Reflection; @@ -13,11 +12,25 @@ namespace System.Linq.Dynamic.Core.CustomTypeProviders; public abstract class AbstractDynamicLinqCustomTypeProvider { /// - /// Finds the unique types marked with DynamicLinqTypeAttribute. + /// Additional types which should also be resolved. + /// + protected readonly IList AdditionalTypes; + + /// + /// Initializes a new instance of the class. + /// + /// A list of additional types (without the DynamicLinqTypeAttribute annotation) which should also be resolved. + protected AbstractDynamicLinqCustomTypeProvider(IList additionalTypes) + { + AdditionalTypes = Check.NotNull(additionalTypes); + } + + /// + /// Finds the unique types annotated with DynamicLinqTypeAttribute. /// /// The assemblies to process. /// - protected IEnumerable FindTypesMarkedWithDynamicLinqTypeAttribute(IEnumerable assemblies) + protected Type[] FindTypesMarkedWithDynamicLinqTypeAttribute(IEnumerable assemblies) { Check.NotNull(assemblies); #if !NET35 @@ -27,7 +40,7 @@ protected IEnumerable FindTypesMarkedWithDynamicLinqTypeAttribute(IEnumera } /// - /// Resolve any type which is registered in the current application domain. + /// Resolve a type which is annotated with DynamicLinqTypeAttribute or when the type is listed in AdditionalTypes. /// /// The assemblies to inspect. /// The typename to resolve. @@ -37,20 +50,13 @@ protected IEnumerable FindTypesMarkedWithDynamicLinqTypeAttribute(IEnumera Check.NotNull(assemblies); Check.NotEmpty(typeName); - foreach (var assembly in assemblies) - { - var resolvedType = assembly.GetType(typeName, false, true); - if (resolvedType != null) - { - return resolvedType; - } - } - - return null; + var types = FindTypesMarkedWithDynamicLinqTypeAttribute(assemblies).Union(AdditionalTypes); + return types.FirstOrDefault(t => t.FullName == typeName); } /// - /// Resolve a type by the simple name which is registered in the current application domain. + /// Resolve a type which is annotated with DynamicLinqTypeAttribute by the simple (short) name. + /// Also when the type is listed in AdditionalTypes. /// /// The assemblies to inspect. /// The simple typename to resolve. @@ -60,22 +66,16 @@ protected IEnumerable FindTypesMarkedWithDynamicLinqTypeAttribute(IEnumera Check.NotNull(assemblies); Check.NotEmpty(simpleTypeName); - foreach (var assembly in assemblies) - { - var fullNames = assembly.GetTypes().Select(t => t.FullName!).Distinct(); - var firstMatchingFullname = fullNames.FirstOrDefault(fn => fn.EndsWith($".{simpleTypeName}")); + var types = FindTypesMarkedWithDynamicLinqTypeAttribute(assemblies); + var fullNames = types.Select(t => t.FullName!).Distinct().ToArray(); + var firstMatchingFullname = fullNames.FirstOrDefault(fn => fn.EndsWith($".{simpleTypeName}")); - if (firstMatchingFullname != null) - { - var resolvedType = assembly.GetType(firstMatchingFullname, false, true); - if (resolvedType != null) - { - return resolvedType; - } - } + if (firstMatchingFullname == null) + { + return null; } - return null; + return types.FirstOrDefault(t => t.FullName == firstMatchingFullname); } #if (UAP10_0 || NETSTANDARD) @@ -147,7 +147,7 @@ protected Type[] GetAssemblyTypesWithDynamicLinqTypeAttribute(IEnumerable().ToArray(); } catch { diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs index f69c9c49f..0e4da9b6f 100644 --- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs +++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs @@ -10,8 +10,6 @@ namespace System.Linq.Dynamic.Core.CustomTypeProviders; /// /// Scans the current AppDomain for all types marked with , and adds them as custom Dynamic Link types. /// -/// Also provides functionality to resolve a Type in the current Application Domain. -/// /// This class is used as default for full .NET Framework and .NET Core App 2.x and higher. /// public class DefaultDynamicLinqCustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider @@ -22,12 +20,12 @@ public class DefaultDynamicLinqCustomTypeProvider : AbstractDynamicLinqCustomTyp private HashSet? _cachedCustomTypes; private Dictionary>? _cachedExtensionMethods; - /// + /// /// Initializes a new instance of the class. /// Backwards compatibility for issue https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/830. /// /// Defines whether to cache the CustomTypes (including extension methods) which are found in the Application Domain. Default set to 'true'. - [Obsolete("Please use the DefaultDynamicLinqCustomTypeProvider(ParsingConfig config, bool cacheCustomTypes = true) constructor.")] + [Obsolete("Please use the DefaultDynamicLinqCustomTypeProvider(ParsingConfig config, IList additionalTypes, bool cacheCustomTypes = true) constructor.")] public DefaultDynamicLinqCustomTypeProvider(bool cacheCustomTypes = true) : this(ParsingConfig.Default, cacheCustomTypes) { } @@ -37,7 +35,17 @@ public DefaultDynamicLinqCustomTypeProvider(bool cacheCustomTypes = true) : this /// /// The parsing configuration. /// Defines whether to cache the CustomTypes (including extension methods) which are found in the Application Domain. Default set to 'true'. - public DefaultDynamicLinqCustomTypeProvider(ParsingConfig config, bool cacheCustomTypes = true) + public DefaultDynamicLinqCustomTypeProvider(ParsingConfig config, bool cacheCustomTypes = true) : this(config, new List(), cacheCustomTypes) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The parsing configuration. + /// A list of additional types (without the DynamicLinqTypeAttribute annotation) which should also be resolved. + /// Defines whether to cache the CustomTypes (including extension methods) which are found in the Application Domain. Default set to 'true'. + public DefaultDynamicLinqCustomTypeProvider(ParsingConfig config, IList additionalTypes, bool cacheCustomTypes = true) : base(additionalTypes) { _assemblyHelper = new DefaultAssemblyHelper(Check.NotNull(config)); _cacheCustomTypes = cacheCustomTypes; @@ -96,7 +104,8 @@ public Dictionary> GetExtensionMethods() private HashSet GetCustomTypesInternal() { IEnumerable assemblies = _assemblyHelper.GetAssemblies(); - return new HashSet(FindTypesMarkedWithDynamicLinqTypeAttribute(assemblies)); + var types = FindTypesMarkedWithDynamicLinqTypeAttribute(assemblies).Union(AdditionalTypes); + return new HashSet(types); } private Dictionary> GetExtensionMethodsInternal() diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs index 83fde4940..ff8e85ff8 100644 --- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs +++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs @@ -2,9 +2,9 @@ { /// /// Interface for providing functionality to find custom types for or resolve any type. - /// Note that this interface will be marked obsolete in the next version. Use instead. /// + [Obsolete("Please use the IDynamicLinqCustomTypeProvider interface instead.")] public interface IDynamicLinkCustomTypeProvider : IDynamicLinqCustomTypeProvider { } -} +} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core/Extensions/LinqExtensions.cs b/src/System.Linq.Dynamic.Core/Extensions/LinqExtensions.cs deleted file mode 100644 index dc563fa8c..000000000 --- a/src/System.Linq.Dynamic.Core/Extensions/LinqExtensions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq.Dynamic.Core.Validation; - -namespace System.Linq.Dynamic.Core.Extensions; - -[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] -internal static class LinqExtensions -{ - public static IEnumerable WhereNotNull(this IEnumerable sequence) - { - Check.NotNull(sequence); - - return sequence.Where(e => e != null)!; - } -} \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core/Parser/PredefinedTypesHelper.cs b/src/System.Linq.Dynamic.Core/Parser/PredefinedTypesHelper.cs index 58cb5f640..e43d676bf 100644 --- a/src/System.Linq.Dynamic.Core/Parser/PredefinedTypesHelper.cs +++ b/src/System.Linq.Dynamic.Core/Parser/PredefinedTypesHelper.cs @@ -28,7 +28,7 @@ internal static class PredefinedTypesHelper public static readonly IDictionary PredefinedTypes = new ConcurrentDictionary(new Dictionary { - { typeof(object), 0 }, + // { typeof(object), 0 }, Removed because of CVE-2024-51417 { typeof(bool), 0 }, { typeof(char), 0 }, { typeof(string), 0 }, diff --git a/src/System.Linq.Dynamic.Core/ParsingConfig.cs b/src/System.Linq.Dynamic.Core/ParsingConfig.cs index 70acd35cc..4886577b3 100644 --- a/src/System.Linq.Dynamic.Core/ParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core/ParsingConfig.cs @@ -71,6 +71,25 @@ public IDynamicLinkCustomTypeProvider? CustomTypeProvider } } + /// + /// Sets the CustomTypeProvider to . + /// + /// Defines whether to cache the CustomTypes (including extension methods) which are found in the Application Domain. Default set to true. + public void UseDefaultDynamicLinqCustomTypeProvider(bool cacheCustomTypes = true) + { + _customTypeProvider = new DefaultDynamicLinqCustomTypeProvider(this, cacheCustomTypes); + } + + /// + /// Sets the CustomTypeProvider to . + /// + /// Defines whether to cache the CustomTypes (including extension methods) which are found in the Application Domain. Default set to true. + /// A list of additional types (without the DynamicLinqTypeAttribute annotation) which should also be resolved. + public void UseDefaultDynamicLinqCustomTypeProvider(IList additionalTypes, bool cacheCustomTypes = true) + { + _customTypeProvider = new DefaultDynamicLinqCustomTypeProvider(this, additionalTypes, cacheCustomTypes); + } + /// /// Load additional assemblies from the current domain base directory. /// Note: only used when full .NET Framework and .NET Core App 2.x and higher. diff --git a/test/System.Linq.Dynamic.Core.Tests/CustomTypeProviders/DefaultDynamicLinqCustomTypeProviderTests.cs b/test/System.Linq.Dynamic.Core.Tests/CustomTypeProviders/DefaultDynamicLinqCustomTypeProviderTests.cs index 6fe19ba87..1b9867b0d 100644 --- a/test/System.Linq.Dynamic.Core.Tests/CustomTypeProviders/DefaultDynamicLinqCustomTypeProviderTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/CustomTypeProviders/DefaultDynamicLinqCustomTypeProviderTests.cs @@ -1,4 +1,5 @@ -using System.IO; +using System.Collections.Generic; +using System.IO; using System.Linq.Dynamic.Core.CustomTypeProviders; using FluentAssertions; using NFluent; @@ -8,11 +9,17 @@ namespace System.Linq.Dynamic.Core.Tests.CustomTypeProviders; public class DefaultDynamicLinqCustomTypeProviderTests { + private readonly IList _additionalTypes = new List + { + typeof(DirectoryInfo), + typeof(DefaultDynamicLinqCustomTypeProviderTests) + }; + private readonly DefaultDynamicLinqCustomTypeProvider _sut; public DefaultDynamicLinqCustomTypeProviderTests() { - _sut = new DefaultDynamicLinqCustomTypeProvider(ParsingConfig.Default); + _sut = new DefaultDynamicLinqCustomTypeProvider(ParsingConfig.Default, _additionalTypes); } [Fact] diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs index ab8bd2894..c020cb94a 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs @@ -281,7 +281,8 @@ public void DynamicClassArray_Issue593_Fails() isValid.Should().BeFalse(); // This should actually be true, but fails. For solution see Issue593_Solution1 and Issue593_Solution2. } - [SkipIfGitHubActions] + // [SkipIfGitHubActions] + [Fact(Skip = "867")] public void DynamicClassArray_Issue593_Solution1() { // Arrange diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs index a0895cc5b..9260cf8d7 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs @@ -1058,7 +1058,7 @@ public void DynamicExpressionParser_ParseLambda_StringLiteral_QuotationMark() Assert.Equal(expectedRightValue, rightValue); } - [Fact] + [Fact(Skip = "867")] public void DynamicExpressionParser_ParseLambda_TupleToStringMethodCall_ReturnsStringLambdaExpression() { var expression = DynamicExpressionParser.ParseLambda( diff --git a/test/System.Linq.Dynamic.Core.Tests/Entities/Worker.cs b/test/System.Linq.Dynamic.Core.Tests/Entities/Worker.cs index 4d5475a35..77bfd548e 100644 --- a/test/System.Linq.Dynamic.Core.Tests/Entities/Worker.cs +++ b/test/System.Linq.Dynamic.Core.Tests/Entities/Worker.cs @@ -1,5 +1,8 @@ -namespace System.Linq.Dynamic.Core.Tests.Entities +using System.Linq.Dynamic.Core.CustomTypeProviders; + +namespace System.Linq.Dynamic.Core.Tests.Entities { + [DynamicLinqType] public class Worker : BaseEmployee { public string Other { get; set; } diff --git a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs index 42b5a743d..74abc9cd7 100644 --- a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs @@ -13,6 +13,7 @@ namespace System.Linq.Dynamic.Core.Tests { + [DynamicLinqType] public enum TestEnumPublic : sbyte { Var1 = 0, @@ -25,6 +26,7 @@ public enum TestEnumPublic : sbyte public partial class ExpressionTests { + [DynamicLinqType] public enum TestEnum2 : sbyte { Var1 = 0, @@ -919,6 +921,9 @@ public void ExpressionTests_Enum_Property_Equality_Using_PublicEnum_And_FullName public void ExpressionTests_Enum_Property_Equality_Using_Enum_And_FullName_Inline() { // Arrange + var config = new ParsingConfig(); + config.UseDefaultDynamicLinqCustomTypeProvider([typeof(TestEnum2)]); + var qry = new List { new TestEnumClass { B = TestEnum2.Var2 } }.AsQueryable(); string enumType = typeof(TestEnum2).FullName!; @@ -948,7 +953,7 @@ public void ExpressionTests_Enum_Property_Equality_Using_PublicEnum_Name_Inline( } [Fact] - public void ExpressionTests_Enum_Property_Equality_Using_Enum_Name_Inline_Should_Throw_Exception() + public void ExpressionTests_Enum_Property_Equality_Using_Enum_Name_Inline_ShouldBeOk() { // Arrange var config = new ParsingConfig @@ -962,7 +967,7 @@ public void ExpressionTests_Enum_Property_Equality_Using_Enum_Name_Inline_Should Action a = () => qry.Where(config, $"{enumType}.Var2 == it.B").ToDynamicArray(); // Assert - a.Should().Throw(); + a.Should().NotThrow(); } [Fact] @@ -1031,7 +1036,10 @@ public void ExpressionTests_Enum_NullableProperty() [Fact] public void ExpressionTests_Enum_MoreTests() { - var config = new ParsingConfig(); + var config = new ParsingConfig + { + ResolveTypesBySimpleName = true + }; // Arrange var lst = new List { TestEnum.Var1, TestEnum.Var2, TestEnum.Var3, TestEnum.Var4, TestEnum.Var5, TestEnum.Var6 }; diff --git a/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/AppSettings.cs b/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/AppSettings.cs new file mode 100644 index 000000000..260ffef25 --- /dev/null +++ b/test/System.Linq.Dynamic.Core.Tests/Helpers/Models/AppSettings.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Linq.Dynamic.Core.CustomTypeProviders; + +namespace System.Linq.Dynamic.Core.Tests.Helpers.Models +{ + public static class AppSettings + { + public static Dictionary SettingsProp { get; } = new() + { + { "jwt", "test" } + }; + + public static Dictionary SettingsField = new() + { + { "jwt", "test" } + }; + } + + [DynamicLinqType] + public static class AppSettings2 + { + public static Dictionary SettingsProp { get; } = new() + { + { "jwt", "test" } + }; + + public static Dictionary SettingsField = new() + { + { "jwt", "test" } + }; + } + + public class AppSettings3 + { + public static Dictionary SettingsProp { get; } = new() + { + { "jwt", "test" } + }; + + public static Dictionary SettingsField = new() + { + { "jwt", "test" } + }; + } +} \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs index 22283e6ba..644f6ef1e 100644 --- a/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs @@ -346,7 +346,7 @@ public void Parse_NullableShouldReturnNullable(string expression, object resultT [Theory] [InlineData("it.MainCompany.Name != null", "(company.MainCompany.Name != null)")] [InlineData("@MainCompany.Companies.Count() > 0", "(company.MainCompany.Companies.Count() > 0)")] - [InlineData("Company.Equals(null, null)", "Equals(null, null)")] + // [InlineData("Company.Equals(null, null)", "Equals(null, null)")] issue 867 [InlineData("MainCompany.Name", "company.MainCompany.Name")] [InlineData("Name", "company.Name")] [InlineData("company.Name", "company.Name")] diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/MethodFinderTest.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/MethodFinderTest.cs index a1afadd49..c4b318e1e 100644 --- a/test/System.Linq.Dynamic.Core.Tests/Parser/MethodFinderTest.cs +++ b/test/System.Linq.Dynamic.Core.Tests/Parser/MethodFinderTest.cs @@ -7,14 +7,14 @@ namespace System.Linq.Dynamic.Core.Tests.Parser; public class MethodFinderTest { - [Fact] + [Fact(Skip = "867")] public void MethodsOfDynamicLinqAndSystemLinqShouldBeEqual() { Expression> expr = x => x.ToString(); var selector = "ToString()"; var prm = Parameter(typeof(int?)); - var parser = new ExpressionParser(new[] { prm }, selector, new object[] { }, ParsingConfig.Default); + var parser = new ExpressionParser([prm], selector, [], ParsingConfig.Default); var expr1 = parser.Parse(null); Assert.Equal(((MethodCallExpression)expr.Body).Method.DeclaringType, ((MethodCallExpression)expr1).Method.DeclaringType); diff --git a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Is,OfType,As,Cast.cs b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Is,OfType,As,Cast.cs index 93e7c1e92..1b41ad25f 100644 --- a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Is,OfType,As,Cast.cs +++ b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Is,OfType,As,Cast.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq.Dynamic.Core.CustomTypeProviders; using System.Linq.Dynamic.Core.Exceptions; using System.Linq.Dynamic.Core.Tests.Entities; using FluentAssertions; @@ -98,6 +99,7 @@ public void OfType_Dynamic_WithFullName_UseParameterizedNamesInDynamicQuery(bool internal class Base { } + [DynamicLinqType] internal class DerivedA : Base { } internal class DerivedB : Base { } @@ -294,7 +296,8 @@ public void As_Dynamic_ActingOnProperty_NullableInt() countAsDynamic.Should().Be(count); } - public enum TestEnum + [DynamicLinqType] + public enum TestEnumForThisTest { None = 0, @@ -305,10 +308,10 @@ public enum TestEnum public void As_Dynamic_ActingOnProperty_NullableEnum() { // Assign - var nullableEnumType = $"{typeof(TestEnum).FullName}?"; + var nullableEnumType = $"{typeof(TestEnumForThisTest).FullName}?"; var qry = new[] { - new { Value = TestEnum.X } + new { Value = TestEnumForThisTest.X } }.AsQueryable(); // Act @@ -365,7 +368,10 @@ public void As_Dynamic_ActingOnProperty_WithType() countAsDynamic.Should().Be(1); } + [DynamicLinqType] public class AS_A { } + + [DynamicLinqType] public class AS_B : AS_A { public string MyProperty { get; set; } diff --git a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs index f1d3a428f..0383b1c29 100644 --- a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs +++ b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; +using System.Linq.Dynamic.Core.CustomTypeProviders; using System.Linq.Dynamic.Core.Exceptions; using System.Linq.Dynamic.Core.Tests.Helpers.Models; using FluentAssertions; @@ -19,6 +20,7 @@ namespace System.Linq.Dynamic.Core.Tests { public partial class QueryableTests { + [DynamicLinqType] public class Example { public int Field; @@ -29,10 +31,12 @@ public class Example public int Sec { get; set; } public int? SecNull { get; set; } + [DynamicLinqType] public class NestedDto { public string Name { get; set; } + [DynamicLinqType] public class NestedDto2 { public string Name2 { get; set; } @@ -324,7 +328,11 @@ public void Select_Dynamic_IntoTypeWithNullableParameterInConstructor() public void Select_Dynamic_SystemType1() { // Arrange - var config = new ParsingConfig { AllowNewToEvaluateAnyType = true }; + var config = new ParsingConfig + { + AllowNewToEvaluateAnyType = true + }; + config.UseDefaultDynamicLinqCustomTypeProvider([typeof(DirectoryInfo)]); var queryable = new[] { "test" }.AsQueryable(); // Act diff --git a/test/System.Linq.Dynamic.Core.Tests/SecurityTests.cs b/test/System.Linq.Dynamic.Core.Tests/SecurityTests.cs index 77fa0c762..a16f259af 100644 --- a/test/System.Linq.Dynamic.Core.Tests/SecurityTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/SecurityTests.cs @@ -1,6 +1,5 @@ using System.IO; using System.Linq.Dynamic.Core.Exceptions; -using System.Net; using System.Reflection; using FluentAssertions; using Xunit; @@ -26,13 +25,13 @@ public void MethodsShouldOnlyBeCallableOnPredefinedTypes_Test1() { // Arrange var baseQuery = new[] { 1, 2, 3 }.AsQueryable(); - string predicate = "\"\".GetType().Assembly.DefinedTypes.Where(it.name == \"Assembly\").First().DeclaredMethods.Where(it.Name == \"GetName\").First().Invoke(\"\".GetType().Assembly, new Object[] {} ).Name.ToString() != \"Test\""; + var predicate = "\"\".GetType().Assembly.DefinedTypes.Where(it.name == \"Assembly\").First().DeclaredMethods.Where(it.Name == \"GetName\").First().Invoke(\"\".GetType().Assembly, new Object[] {} ).Name.ToString() != \"Test\""; // Act Action action = () => baseQuery.OrderBy(predicate); // Assert - action.Should().Throw().WithMessage("Methods on type 'MethodBase' are not accessible"); + action.Should().Throw().WithMessage("Methods on type 'Object' are not accessible"); } [Fact] @@ -41,8 +40,7 @@ public void MethodsShouldOnlyBeCallableOnPredefinedTypes_Test2() // Arrange var messages = new[] { - new Message("Alice", "Bob"), - new Message("Bob", "Alice") + new Message("Alice", "Bob") }.AsQueryable(); Action action = () => messages.Where( @@ -50,7 +48,7 @@ public void MethodsShouldOnlyBeCallableOnPredefinedTypes_Test2() ); // Assert - action.Should().Throw().WithMessage($"Methods on type 'Assembly' are not accessible"); + action.Should().Throw().WithMessage($"Methods on type 'Object' are not accessible"); } [Theory] @@ -64,4 +62,129 @@ public void DynamicExpressionParser_ParseLambda_IllegalMethodCall_ThrowsExceptio // Assert action.Should().Throw().WithMessage($"Methods on type '{type}' are not accessible"); } + + [Theory] + [InlineData("c => string.Join(\"_\", c.GetType().Assembly.DefinedTypes.SelectMany(t => t.CustomAttributes).Select(a => a.AttributeType).Select(t => t.AssemblyQualifiedName))")] + [InlineData("c => string.Join(\"_\", c.GetType().Assembly.DefinedTypes.Select(t => t.BaseType).Select(t => t.AssemblyQualifiedName))")] + [InlineData("c => string.Join(\"_\", c.GetType().Assembly.FullName))")] + public void UsingSystemReflectionAssembly_ThrowsException(string selector) + { + // Arrange + var queryable = new[] + { + new Message("Alice", "Bob") + }.AsQueryable(); + + // Act + Action action = () => queryable.Select(selector); + + // Assert + action.Should().Throw().WithMessage("Methods on type 'Object' are not accessible"); + } + + [Theory] + [InlineData("System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings.SettingsProp[\"jwt\"]")] + [InlineData("System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings.SettingsField[\"jwt\"]")] + [InlineData("c => System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings.SettingsProp[\"jwt\"]")] + [InlineData("c => System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings.SettingsField[\"jwt\"]")] + public void UsingStaticClassAsType_ThrowsException(string selector) + { + // Arrange + var queryable = new[] + { + new Message("Alice", "Bob") + }.AsQueryable(); + + // Act + Action action = () => queryable.Select(selector); + + // Assert + action.Should().Throw().WithMessage("Type 'System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings' not found"); + } + + [Theory] + [InlineData("new System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings3().SettingsProp[\"jwt\"]")] + [InlineData("new System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings3().SettingsField[\"jwt\"]")] + [InlineData("c => new System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings3().SettingsProp[\"jwt\"]")] + [InlineData("c => new System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings3().SettingsField[\"jwt\"]")] + public void UsingClassAsType_ThrowsException(string selector) + { + // Arrange + var queryable = new[] + { + new Message("Alice", "Bob") + }.AsQueryable(); + + // Act + Action action = () => queryable.Select(selector); + + // Assert + action.Should().Throw().WithMessage("Type 'System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings3' not found"); + } + + [Theory] + [InlineData("System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings.SettingsProp[\"jwt\"]")] + [InlineData("System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings.SettingsField[\"jwt\"]")] + [InlineData("c => System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings.SettingsProp[\"jwt\"]")] + [InlineData("c => System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings.SettingsField[\"jwt\"]")] + public void UsingStaticClassAsType_WhenAddedToDefaultDynamicLinqCustomTypeProvider_ShouldBeOk(string selector) + { + // Arrange + var config = new ParsingConfig(); + config.UseDefaultDynamicLinqCustomTypeProvider([typeof(Helpers.Models.AppSettings), typeof(Helpers.Models.AppSettings3)]); + + var queryable = new[] + { + new Message("Alice", "Bob") + }.AsQueryable(); + + // Act + Action action = () => queryable.Select(config, selector); + + // Assert + action.Should().NotThrow(); + } + + [Theory(Skip = "873")] + [InlineData("new System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings3()", "SettingsProp[\"jwt\"]")] + [InlineData("new System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings3()", "SettingsField[\"jwt\"]")] + [InlineData("c => new System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings3()", "SettingsProp[\"jwt\"]")] + [InlineData("c => new System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings3()", "SettingsField[\"jwt\"]")] + public void UsingClassAsType_WhenAddedToDefaultDynamicLinqCustomTypeProvider_ShouldBeOk(string selector1, string selector2) + { + // Arrange + var config = new ParsingConfig(); + config.UseDefaultDynamicLinqCustomTypeProvider([typeof(Helpers.Models.AppSettings), typeof(Helpers.Models.AppSettings3)]); + + var queryable = new[] + { + new Message("Alice", "Bob") + }.AsQueryable(); + + // Act + Action action = () => queryable.Select(config, selector1).Select(config, selector2); + + // Assert + action.Should().NotThrow(); + } + + [Theory] + [InlineData("System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings2.SettingsProp[\"jwt\"]")] + [InlineData("System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings2.SettingsField[\"jwt\"]")] + [InlineData("c => System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings2.SettingsProp[\"jwt\"]")] + [InlineData("c => System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings2.SettingsField[\"jwt\"]")] + public void UsingStaticClassWithDynamicTypeAttribute_ShouldBeOk(string selector) + { + // Arrange + var queryable = new[] + { + new Message("Alice", "Bob") + }.AsQueryable(); + + // Act + Action action = () => queryable.Select(selector); + + // Assert + action.Should().NotThrow(); + } } \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.Tests/TestClasses/TestCustomTypeProvider.cs b/test/System.Linq.Dynamic.Core.Tests/TestClasses/TestCustomTypeProvider.cs index 5ae6565b2..ccb883c18 100644 --- a/test/System.Linq.Dynamic.Core.Tests/TestClasses/TestCustomTypeProvider.cs +++ b/test/System.Linq.Dynamic.Core.Tests/TestClasses/TestCustomTypeProvider.cs @@ -9,6 +9,10 @@ public class TestCustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDy { private HashSet? _customTypes; + public TestCustomTypeProvider() : base([]) + { + } + public virtual HashSet GetCustomTypes() { if (_customTypes != null) @@ -16,12 +20,13 @@ public virtual HashSet GetCustomTypes() return _customTypes; } - _customTypes = new HashSet(FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly })) + _customTypes = new HashSet(FindTypesMarkedWithDynamicLinqTypeAttribute([GetType().GetTypeInfo().Assembly])) { typeof(CustomClassWithStaticMethod), typeof(StaticHelper), typeof(StaticHelper.Nested) }; + return _customTypes; }