Skip to content

formula 内部还剩第三个 CEL 解析入口:cel-to-filter.ts 自建 limitless env,与 celEngine 对「什么能解析」仍不一致 #6132

Description

@baozhoutao

范围外发现,出自 #4812 / PR #6130(把 packages/lint 的解析收敛到 parseCelToAst)。按 Prime Directive #10 记录,不指派。

事实(origin/main bc67c28e2 基线,静态对读 + 实测)

#4812 收敛掉的是 lint 那个自建 env。但 packages/formula 内部还有一个,与被收敛掉的那个逐字同构:

packages/formula/src/cel-to-filter.ts:88-95

// A roots-permissive env: parsing is purely syntactic (we read `.ast`, never
// `.check()`/`.evaluate()`), so any identifier or method call parses. Built once.
let parseEnv: Environment | undefined;
function getParseEnv(): Environment {
  if (!parseEnv) {
    parseEnv = new Environment({ unlistedVariablesAreDyn: true, enableOptionalTypes: true });
  }
  return parseEnv;
}

与 lint 改前那份选项完全相同:没有 limits,没有 stdlib,没有 rewriteNullableTernary。消费它的是两个导出入口:compileCelToFilter(:123)与 isPushdownableCel(:143),都是 getParseEnv().parse(source).ast

于是 DEFAULT_LIMITS 这一格上的分歧原样保留 —— 实测(同 PR #6130 用的探针):

源码形状 cel-to-filter 的 env celEngine.compile()
300 项连加 解析通过 Exceeded maxAstNodes (256)
60 层括号 解析通过 Exceeded maxDepth (32)
200 元素列表 解析通过 Exceeded maxListElements (64)

即:一条超过平台边界的谓词,celEngine 直接拒绝,而 pushdown 编译器照常把它降成 SQL 过滤并下推。

为什么按 finding 记(不代 triage 定级)

可达性我没有量化。 消费 compileCelToFilter / isPushdownableCel 的是 RLS / sharing 下推路径(ADR-0058),那条路径前面还有 isSupportedRlsExpression 等形状闸门,一条 256 节点以上的 RLS 谓词在真实部署里是否写得出来、写出来会不会先被别的闸门拦掉,我没有测。所以按「观察类 / 未被行使的漂移」记,不因为「看着小」就压着不报 —— 定级请按 triage,不代表我判断它低。

需要一并注意的是方向:这一面比 celEngine宽松,而它的产物是安全谓词下推的 SQL。宽松的一侧产出的是"多编译了一条引擎本会拒绝的谓词",不是"漏掉一条" —— 但两个入口对同一条源码给不同答案,本身就是 #4812 要消灭的形状。

为什么 PR #6130 没顺手改

刻意留下,理由记在这里免得被当成遗漏:

  1. packages/lint 绕过 @objectstack/formula 直接 parse CEL —— 两个解析入口对「什么能解析」会给出不同答案 #4812 的 scope 明确写死"只动 packages/formula 新增入口 + packages/lint 改调"。把 compileCelToFilter 的解析 env 换成带 DEFAULT_LIMITS 的那条,是在安全敏感路径上改变行为(超界谓词从"下推成过滤"变成 parse-error,而 parse-error 在该路径上是 fail-closed 拒绝),风险画像与一次 refactor 完全不同,应当单独裁、单独测。
  2. 同理,给它加上 rewriteNullableTernary 会改变喂给 lowerCelAst 的 AST 形状(三元分支会多一层 dyn(...) 包裹),lowerCondition 对此的反应需要单独验证 —— 三元本来就不可下推,但"不可下推的理由从 A 变成 B"仍然是行为变化。

建议

改走 #4812 落地的 parseCelToAst,或明确裁定"下推面按纯语法解析另算"并把理由写进 cel-to-filter.ts 文件头 —— 两条都行,别默认它不存在。若选前者,需要一并回答:超界谓词在 RLS 下推路径上应当 fail-closed 拒绝,还是应当继续下推?这是裁决,不是重构。

关联

#4812(本体裁决 / PR #6130)、ADR-0058(一个 AST 两个后端)、ADR-0056 D4(RLS 谓词形状闸门)。

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions