Skip to content

driver-sql: the #7929 withhold covers the cross-field family only — every other INVALID_FILTER refusal still names the target field, which is admin-authored on a read-scope predicate #8197

Description

@os-zhuang

Filed unassigned, unlabeled for triage to grade and route — a concrete defect measured while implementing #7929's ruling B (driver-sql stops echoing $field operands), deliberately NOT fixed in that PR because it is outside what B rules on.

What B closed, and what it did not

B's ruling is operand-shaped: "driver-sql's INVALID_FILTER refusal stops echoing $field operands". Implemented, that covers the three cross-field builders plus five operators whose own comparand-shape gates rendered a reference into their message. It does not touch the rest of the INVALID_FILTER family, and every one of those still names the target field of the refused constraint.

That matters for exactly the reason #7929 exists. The predicate a driver refuses is frequently not the caller's: the security middleware ANDs an administrator's compiled CEL rule into opCtx.ast.where (plugin-security/src/security-plugin.ts, the $and merge), and the driver cannot tell the two subtrees apart. On such a predicate the target column is as admin-authored as the referent — which is the argument B accepted for withholding both operands of a cross-field comparison. The same argument applies one step out, to every refusal that names a column.

Measured

Real SqlDriver (better-sqlite3, :memory:), one object declaring secret_policy_col as multiple: true, driven through driver.find. Each of these is a predicate an RLS rule can plausibly produce, and each answers INVALID_FILTER / 400 naming the column:

REFUSED | JSON column + $in    code=INVALID_FILTER  names target? true
  Operator "$in" on field "secret_policy_col" WAS NOT APPLIED: "secret_policy_col" is a
  multi-value (or otherwise JSON-valued) field, stored by this driver as a JSON TEXT column …

REFUSED | empty field constraint    code=INVALID_FILTER  names target? true
  Field constraint at filter.secret_policy_col carries zero operators
  ({ "secret_policy_col": {} }). …

REFUSED | unbindable comparand    code=INVALID_FILTER  names target? true
  Operator "$gt" on field "secret_policy_col" requires a single comparable value, but received
  an object ({"a":1}), which cannot be bound as a SQL parameter. …

The first is the most reachable of the three: a CEL permission rule over a multi-select field lowers to a membership test on a JSON-stored column, and #7398's gate refuses it — correctly — while naming the column the administrator wrote.

One residual inside the cross-field family itself, for completeness: a MALFORMED reference ({ "$gt": { "$field": 42 } } — not a FieldReferenceSchema, so the cross-field arm does not recognise it) falls to the generic unbindable-comparand refusal, which prints the target column and the comparand's JSON. No column name is disclosed by the comparand there, but the target is.

Explicitly NOT claimed

Refs

Blocked-by: #8220

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions