You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在编译阶段校验NCalc表达式及引用参数的合法性

NCalc条件表达式参数合法性编译期校验方案

核心逻辑是绕开默认求值的短路逻辑,直接遍历解析完成的语法树提取所有引用的参数,和合法参数白名单做比对,全程不触发表达式求值,从根源避免漏判。

具体实现步骤

  • 关闭表达式默认优化
    初始化NCalc表达式实例时传入EvaluateOptions.NoOptimize参数,禁用内置的短路求值、常量折叠等优化逻辑,保证语法树完整保留所有用户编写的节点:
    var expr = new Expression(userInputCondition, EvaluateOptions.NoOptimize);
    
  • 递归遍历语法树提取所有参数引用
    直接访问表达式解析完成后生成的ParsedExpression语法树根节点,递归遍历所有子节点,把所有标识符(即用户引用的参数)全部收集起来:
    HashSet<string> GetAllReferencedParameters(LogicalNode rootNode)
    {
        var paramSet = new HashSet<string>(StringComparer.Ordinal);
        void Traverse(LogicalNode node)
        {
            switch (node)
            {
                case IdentifierNode idNode:
                    paramSet.Add(idNode.Name);
                    break;
                case BinaryExpressionNode binaryNode:
                    Traverse(binaryNode.LeftExpression);
                    Traverse(binaryNode.RightExpression);
                    break;
                case UnaryExpressionNode unaryNode:
                    Traverse(unaryNode.Expression);
                    break;
                case FunctionNode funcNode:
                    foreach (var p in funcNode.Parameters) Traverse(p);
                    break;
                case TernaryExpressionNode ternaryNode:
                    Traverse(ternaryNode.LeftExpression);
                    Traverse(ternaryNode.MiddleExpression);
                    Traverse(ternaryNode.RightExpression);
                    break;
                case ValuesListNode listNode:
                    foreach (var p in listNode.Values) Traverse(p);
                    break;
                // 字面量、常量节点无参数引用,直接跳过
            }
        }
        Traverse(rootNode);
        return paramSet;
    }
    
  • 执行参数合法性校验
    拿到所有引用的参数集合后,和主程序预先提供的合法参数白名单做差集比对,只要存在差集内容,直接判定为非法表达式,返回对应错误即可,不需要进入后续求值流程:
    var referencedParams = GetAllReferencedParameters(expr.ParsedExpression);
    var invalidParams = referencedParams.Except(allowedParamList).ToList();
    if (invalidParams.Any())
    {
        throw new ArgumentException($"条件表达式存在非法参数:{string.Join(",", invalidParams)}");
    }
    

注意:所有校验操作必须在调用Evaluate()方法之前完成,整个遍历过程仅做静态语法分析,不会执行任何表达式逻辑,性能开销极低,完全满足编译阶段校验的要求。

原有方案缺陷说明

NCalc自带的HasErrors()方法仅做语法格式校验,不会检查引用参数的合法性;默认开启的求值优化会对and/or逻辑运算做短路处理,例如执行true or XXXX时,判定左侧为true可直接得到整个表达式结果,就不会解析右侧的XXXX节点,自然无法识别非法参数。
不要尝试通过EvaluateParameter事件做参数校验,短路分支下根本不会触发对应参数的请求事件,依然会存在漏判。

额外扩展

遍历语法树的过程中还可以同步实现其他自定义校验规则,比如限制可用函数列表、禁止特定运算符、校验值类型范围等,完全适配自定义步骤语言的安全管控要求。

内容的提问来源于stack exchange,提问作者Marian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 17:39:18