Skip to content

driver-memory 的实时查询路径根本不支持 $not —— mingo 抛无 code 的 MingoError,CEL !expr 降下来的 RLS scope 在该驱动上直接 500 #5324

Description

@os-zhuang

修 #5240(四后端对 { field: {} } 拒收)时,为了确认 driver-memory 那一行的现场而实测了驱动的真实查询路径,发现这一处不属于 #5240 范围的缺陷。按 Prime Directive #10 单独记在这里,unassigned。

现象

InMemoryDriver.find 并不走 memory-matcher.ts 的 match() —— 它只从那个文件 import 了 getValueByPath,过滤实际由 convertToMongoQuery() → mingo 完成。而 mingo 是 MongoDB 语义的实现,MongoDB 没有文档级 $not,于是:

WHERE {"$not":{"stage":"won"}}              => THROW MingoError | code= undefined | status= undefined | unknown top level operator: $not
WHERE {"$or":[{"$not":{"stage":"won"}}]}    => THROW MingoError | code= undefined | status= undefined | unknown top level operator: $not
WHERE {"$and":[{"$not":{"stage":"won"}}]}   => THROW MingoError | code= undefined | status= undefined | unknown top level operator: $not

(实测原文,InMemoryDriver + syncSchema('deal') + 两行数据,worktree 基于 26e1029f5。)

normalizeFilterCondition 对 $not 只是递归归一化后原样透传 (result[key] = ...),没有任何 De Morgan 改写,所以 $not 在任何位置都失败,不只是顶层。

为什么是 bug

  1. $not 是已声明的协议算子。 spec LOGICAL_OPERATORS 声明它;driver-sql 实现它([driver-sql] 未知查询 operator(is/is_null)静默透传返整表,应报错;缺 $null 处理 #2704 加的分支,$not 的语义在 driver-sql 与 driver-memory / formula 之间分叉:NULL 行的去留相反,$not: {} 一个是 TRUE 一个是 FALSE #5146 又把它改成 NULL-safe);driver-mongodb 专门有一层翻译(mongodb-filter-logic-translation.test.ts)正是为了绕开「MongoDB 没有文档级 $not」;memory-matcher.match 自己也实现了它。四个后端里只有 driver-memory 的实时路径没有。
  2. 它落在权限路径上。 cel-to-filter.ts 把 CEL !expr 编译成 { $not: {...} },这是 RLS read scope 的常规产物。driver-memory 是默认的开发/测试驱动 —— 一条带否定的 scope 在它上面不是「多返回/少返回几行」,而是整个查询抛异常。
  3. 异常逃出了 ADR-0112 信封。 MingoError 没有 code 也没有 status,于是 mapDataError 走 default 分支,客户端拿到的是 500 形状的裸 { error } —— data: unsupported-filter-operator refusal ships without error.code and leaks the [sql-driver] prefix #4436 为这个驱动建立的 INVALID_FILTER / 400 envelope(见 memory-filter-refusal-envelope.test.ts)对这一类完全没覆盖到。

为什么至今没被测出来

FILTER_LOGIC_CASES 对 driver-memory 只经由 memory-matcher(memory-matcher-or-semantics.test.ts)跑,那是参考实现,不是驱动的实时路径;而 driver-sql / driver-sqlite-wasm / driver-mongodb 三家的同名 conformance 都是穿过真驱动跑的。表里第 $not ANDs with its sibling keys inside a branch 条(filter-logic-conformance.ts:172)因此从未在 InMemoryDriver.find 上执行过一次。

即:一致性表覆盖了这个后端的一半,而没覆盖的那一半正好是用户真正用到的那一半。

建议(不代裁决)

两条路都要维护者/spec 车道拍板方向,故只列不选:

另外无论走哪条,建议补一条 FILTER_LOGIC_CASES 穿过 InMemoryDriver.find 的 conformance 套件 —— 覆盖缺口本身比这个 $not 更值钱(可与 #5239 同批考虑)。

未验证的部分:我没有测 driver-mongodb 的翻译层是否已经覆盖全部 $not 形状,也没有量化今天有多少 RLS 规则真的编译出 $not。严重度请 PM 按 triage 定,不代表我判断它低。

关联:#5240(实测出这一条的那一单)、#5146(driver-sql 的 NULL-safe $not)、#5297(service-analytics 的 $not 分叉)、#5239(一致性表)、#4436(driver-memory 的 refusal envelope)。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions