Some considerations regarding security
We are going to provide the dynamic query language to our end-users. That causes us to double-check potential pitfalls in security. Since the string presenting the query translated into the code which can be executed inside the context of our system we have to be sure the user is very restricted in their usage of types, operators, etc., etc.
After the research, we have found some issues I would like to discuss.
- Enums are used without any restriction. It might seem to be safe, but I wouldn't like my end-user could potentially analyze what enums with what values are used in our code which is irrelevant to his query. Consider the example:
namespace IrrelevantNamespace
{
public enum SecurityLevels
{
Weak = 1,
Strong = 10
}
}
[Test]
public void TestIrrelevantEnums()
{
var entities = new Entity[]
{
new("KnownName1", 1),
new("KnownName2", 10),
}.AsQueryable();
var query = entities.Where("ExperienceInYears = IrrelevantNamespace.SecurityLevels.Strong").Select(e => e.Name);
var result = query.ToList();
TestContext.WriteLine(string.Join(",", result));
}
- Parameterless constructors of any entity (at least of the same namespace) can be called. It might be useful but, obviously, constructor calls could have side effects so there should be an ability to turn that feature off. Even in regards to the 'it' type the possibility to use its properties doesn't cause the safety of calling its constructor in all circumstances. Consider the example:
namespace MyNamespace
{
public class IrrelevantDataClass
{
public IrrelevantDataClass()
{
TestContext.WriteLine($"{typeof(IrrelevantDataClass)} unexpected ctor is called");
}
}
public class Entity
{
public string Name { get; }
public int ExperienceInYears { get; }
public Entity()
{
TestContext.WriteLine($"{typeof(Entity)} unexpected ctor is called");
}
public Entity(string name, int experienceInYears)
{
Name = name;
ExperienceInYears = experienceInYears;
}
}
public class DynamicLinqTests
{
[Test]
public void TestCtor()
{
var entities = new Entity[]
{
new("KnownName1", 1),
new("KnownName2", 10),
}.AsQueryable();
var query = entities.Where(new ParsingConfig { AllowNewToEvaluateAnyType = false, ResolveTypesBySimpleName = true },
"it eq object(new IrrelevantDataClass())").Select(e => e.Name);
var result = query.ToList();
//Even Entity ctor call might be not good in some circumstances
query = entities.Where(new ParsingConfig { AllowNewToEvaluateAnyType = false }, "it eq new Entity()").Select(e => e.Name);
result = query.ToList();
}
}
- And the last question is not about the security but about addressing. Why, in the following case, cannot I use a full type name (i.e. with namespaces)? It is important when several types with the same name but from different namespaces are added as custom.
namespace IrrelevantNamespace
{
public class SameNameClass
{
public static int UserExperience() => 10;
}
}
[Test]
public void TestAddressing()
{
var entities = new Entity[]
{
new("KnownName1", 1),
new("KnownName2", 10),
}.AsQueryable();
var query = entities.Where(new ParsingConfig
{
ResolveTypesBySimpleName = false,
CustomTypeProvider = new TestCustomTypesProvider()
}, "ExperienceInYears == IrrelevantNamespace.SameNameClass.UserExperience()").Select(e => e.Name);
var result = query.ToList();
TestContext.WriteLine(string.Join(",", result));
}
public class TestCustomTypesProvider : DefaultDynamicLinqCustomTypeProvider
{
public override HashSet<Type> GetCustomTypes()
{
var customTypes = base.GetCustomTypes();
customTypes.Add(typeof(IrrelevantNamespace.SameNameClass));
return customTypes;
}
}
causes the exception:
System.Linq.Dynamic.Core.Exceptions.ParseException : Enum value 'UserExperience' is not defined in enum type 'IrrelevantNamespace.SameNameClass'
1] If you expose it to end-users, all linq queries on all your database entities are possible, so indeed also using enums if you use enums.
Can you add a simpler example where you write the code and explain in detail what could go wrong?
2] Full contructor support is only made possible when using linq-to-objects.
Adding a configuration setting to disallow all constructors could be added, I'll investigate this.
3] Thanks for the question, indeed it seems that full namespace enums cannot be handled, I'll take a look.
Issue 2: see https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/732
@ademchenko Is the answer enough for you? Or do you need more clarification?
@ademchenko Is the answer enough for you? Or do you need more clarification?
Closing