diff --git a/YLErpDAL/Modules/RiskEngine/Compile/RuleCompiler.cs b/YLErpDAL/Modules/RiskEngine/Compile/RuleCompiler.cs
index e9797395..5d7be9d3 100644
--- a/YLErpDAL/Modules/RiskEngine/Compile/RuleCompiler.cs
+++ b/YLErpDAL/Modules/RiskEngine/Compile/RuleCompiler.cs
@@ -1,4 +1,7 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Scripting;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Scripting;
using Newtonsoft.Json;
using System;
@@ -46,7 +49,119 @@ namespace YLErp.Modules.RiskEngine
///
public static class RuleCompiler
{
- static IYcLogger _logger = LogFactory.GetLogger("RuleCompiler");
+ private static IYcLogger _logger = LogFactory.GetLogger("RuleCompiler");
+
+ ///
+ /// 高风险方法黑名单。
+ /// 这里按方法名做语法层拦截,覆盖数据库写入、原生 SQL、反射、文件、进程、服务定位等入口。
+ ///
+ private static readonly HashSet ForbiddenInvocationNames = new HashSet(StringComparer.OrdinalIgnoreCase)
+ {
+ "SaveChanges",
+ "SaveChangesAsync",
+ "Add",
+ "AddAsync",
+ "AddRange",
+ "AddRangeAsync",
+ "AddOrUpdate",
+ "Update",
+ "UpdateRange",
+ "Remove",
+ "RemoveRange",
+ "Attach",
+ "AttachRange",
+ "Set",
+ "Entry",
+ "ExecuteSqlRaw",
+ "ExecuteSqlRawAsync",
+ "ExecuteSqlInterpolated",
+ "ExecuteSqlInterpolatedAsync",
+ "ExecuteSql",
+ "ExecuteSqlAsync",
+ "ExecuteSqlCommand",
+ "ExecuteSqlCommandAsync",
+ "ExecuteStoreCommand",
+ "ExecuteStoreQuery",
+ "ExecuteDelete",
+ "ExecuteDeleteAsync",
+ "ExecuteUpdate",
+ "ExecuteUpdateAsync",
+ "FromSqlRaw",
+ "FromSqlRawAsync",
+ "FromSqlInterpolated",
+ "FromSqlInterpolatedAsync",
+ "BulkInsert",
+ "BulkUpdate",
+ "BulkDelete",
+ "BulkMerge",
+ "GetType",
+ "Invoke",
+ "InvokeMember",
+ "GetMethod",
+ "GetProperty",
+ "GetField",
+ "CreateInstance",
+ "Load",
+ "LoadFrom",
+ "ReadAllText",
+ "ReadAllLines",
+ "ReadAllBytes",
+ "WriteAllText",
+ "WriteAllLines",
+ "WriteAllBytes",
+ "AppendAllText",
+ "Delete",
+ "Move",
+ "Copy",
+ "Open",
+ "OpenRead",
+ "OpenWrite",
+ "Create",
+ "CreateText",
+ "Start",
+ "Kill",
+ "Exit",
+ "GetEnvironmentVariable",
+ "SetEnvironmentVariable",
+ "GetService",
+ "GetRequiredService",
+ "CreateScope",
+ "Sleep"
+ };
+
+ ///
+ /// 高风险成员黑名单。
+ /// DbContext.Database、ChangeTracker 等成员会绕过只读查询约束或暴露底层状态,禁止脚本访问。
+ ///
+ private static readonly HashSet ForbiddenMemberNames = new HashSet(StringComparer.OrdinalIgnoreCase)
+ {
+ "Database",
+ "ChangeTracker",
+ "Assembly",
+ "AppDomain"
+ };
+
+ ///
+ /// 高风险类型或标识符黑名单。
+ /// 用于拦截 File、Process、Environment 等直接作为类型或变量名出现的绕过方式。
+ ///
+ private static readonly HashSet ForbiddenTypeOrIdentifierNames = new HashSet(StringComparer.OrdinalIgnoreCase)
+ {
+ "Activator",
+ "Assembly",
+ "AppDomain",
+ "Environment",
+ "File",
+ "Directory",
+ "Path",
+ "Process",
+ "HttpClient",
+ "WebClient",
+ "WebRequest",
+ "Socket",
+ "Thread",
+ "GC"
+ };
///
/// 校验并编译规则表达式。
@@ -79,6 +194,109 @@ namespace YLErp.Modules.RiskEngine
}
}
+ ///
+ /// 校验变量取值表达式。
+ /// 变量表达式只要求能编译为 object,不在保存时执行,避免依赖真实交易和数据库数据。
+ ///
+ public static RuleCompileResult ValidateVariableExpression(string variableExpr)
+ {
+ if (string.IsNullOrWhiteSpace(variableExpr))
+ return RuleCompileResult.Fail("变量取值表达式不能为空");
+
+ var compiled = CompileVariableExpression(variableExpr, out var compileErrorMessage);
+ if (compiled == null)
+ return RuleCompileResult.Fail(compileErrorMessage ?? "变量取值表达式编译失败");
+
+ return RuleCompileResult.Ok(null);
+ }
+
+ ///
+ /// 编译变量取值表达式。
+ /// 结构化规则执行时使用,表达式返回值由执行器按变量 DataType 统一转换。
+ ///
+ public static Func CompileValueExpression(string variableExpr, out string errorMessage)
+ {
+ if (string.IsNullOrWhiteSpace(variableExpr))
+ {
+ errorMessage = "变量取值表达式不能为空";
+ return null;
+ }
+
+ return CompileVariableExpression(variableExpr, out errorMessage);
+ }
+
+ ///
+ /// 编译变量取值表达式。优先兼容原有单表达式写法;失败后再按多语句脚本编译。
+ /// 多语句脚本支持用 return 表达最终值,编译前会转换为 Roslyn Script 稳定支持的末尾表达式。
+ ///
+ private static Func CompileVariableExpression(string variableExpr, out string errorMessage)
+ {
+ if (IsLikelyVariableScript(variableExpr))
+ {
+ var normalizedScript = NormalizeVariableScript(variableExpr);
+ var scriptCompiled = CompileScript