| Version | Supported |
|---|---|
| latest | ✅ |
If you discover a security vulnerability in RoslynRules, please report it by opening a private security advisory on GitHub or emailing the maintainers directly.
Please do not open public issues for security vulnerabilities.
RoslynRules compiles user-supplied C# expression strings into executable code at runtime. This is a powerful feature, but it carries inherent security risks similar to SQL injection or eval-based attacks.
Starting with v1.0.3, RoslynRules ships with a default assembly whitelist that restricts what compiled expressions can access. The compiler only references assemblies matching the whitelist and explicitly excludes known dangerous assemblies.
Default whitelisted assemblies:
System.Runtime,System.Private.CoreLib— core .NET typesSystem.Linq,System.Linq.Expressions— LINQ and expression treesSystem.Collections— collectionsSystem.Text.Json— JSON serializationSystem.Text.RegularExpressions— regexRoslynRules— the rule engine itselfMicrosoft.CodeAnalysis,Microsoft.CodeAnalysis.CSharp,Microsoft.CSharp— Roslyn compiler internals
Blocked (dangerous) assemblies:
System.IO/System.IO.FileSystem— file system accessSystem.Diagnostics.Process— process spawningSystem.Net.Http/System.Net.Sockets— network accessSystem.Security.Cryptography— cryptographic operationsSystem.Reflection.Emit— dynamic code generationSystem.Runtime.Loader— assembly loadingSystem.Data.SqlClient/System.Data.OleDb/System.Data.Odbc— database accessMicrosoft.Win32.Registry— registry access
Customizing the whitelist:
You can supply your own AssemblyReferenceProvider when creating an ExpressionCompiler:
var whitelist = new[] { "MyApp.Domain", "MyApp.Services" };
var blocked = new[] { "System.IO" }; // extra blocks beyond defaults
var provider = new AssemblyReferenceProvider(whitelist, blocked);
var workflow = new Workflow
{
Rules = { new Rule { Expression = "MyApp.Domain.IsValid(data)" } }
};
// The workflow owns its compiler — pass the custom provider to Compile.
workflow.Compile(parameters, referenceProvider: provider);
⚠️ Important: Sandboxing prevents accidental access but is not a complete security boundary. Determined attackers with deep .NET knowledge may still find escape routes. Combine sandboxing with the other mitigations below.
Because expressions are compiled as C# code, they can invoke any method available from whitelisted assemblies:
- The
Systemnamespace (e.g.,DateTime.Now,Guid.NewGuid()) - LINQ (
System.Linq) - Collections (
System.Collections.Generic) - JSON (
System.Text.Json) - Any custom assemblies you explicitly whitelist
// DON'T DO THIS with untrusted input
var rule = new Rule
{
Expression = @"System.IO.File.Delete(@""C:\\important.txt""); true"
};This expression would compile and execute, deleting a file.
-
Use the default sandboxing. Do not override the
AssemblyReferenceProviderunless you understand the security implications. -
Never compile expressions from untrusted sources (user input, external APIs, uploaded files) without validation.
-
Validate expressions before compilation. Use
rule.Validate()to check syntax, but note that syntax validation does NOT prevent malicious code — it only checks for valid C#. -
Use a whitelist approach. Parse the expression's syntax tree and reject expressions containing forbidden method calls or types.
-
Run in a sandboxed environment. Consider executing rules in a separate process or container with limited permissions.
-
Prefer static rule definitions. Define rules in code or configuration files rather than accepting dynamic expressions at runtime.
- Denial of Service: An expression like
while(true) {}or heavy CPU computation can hang or degrade performance. - Resource exhaustion: Expressions can allocate large amounts of memory.
- Side effects: Expressions can mutate objects passed as parameters if those objects are mutable.
See issue #24 for the sandboxing implementation and related discussions.