Skip to content

fix(driver-sql): fail-loud on unknown filter operators; real IS NULL / IS NOT NULL; $not (#2704) - #2872

Merged
os-zhuang merged 1 commit into
mainfrom
claude/github-issue-2704-0d903e
Jul 13, 2026
Merged

fix(driver-sql): fail-loud on unknown filter operators; real IS NULL / IS NOT NULL; $not (#2704)#2872
os-zhuang merged 1 commit into
mainfrom
claude/github-issue-2704-0d903e

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Closes#2704.

问题

driver-sql 把它不认识的 filter 算子直接原样转发给 Knex。当比较值是 null 时,这会静默编译成整表匹配——于是一个按权限/负责人范围过滤的列表视图会把所有行都返回出去(数据泄漏)。触发场景很现实:ObjectUI 客户端对空值筛选发的是 is_null / isnull / is_empty 这类拼写。

具体三个坑:

  1. 数组格式 where 的 default 分支透传未知算子 → is_null + null 比较值 → 整表匹配;is_empty → Knex 直接抛 400。
  2. 对象格式({field:{$op:value}})把未知 $op静默降级成相等比较,且完全没有 $null 处理。
  3. 没有真正的空值判断:field = null 在 SQL 里永远渲染不出 IS NULL

改动

  • 空值谓词渲染成真正的 SQL:is_null/isnull/is_empty(及否定式)→ IS NULL/IS NOT NULL,与 equals+null 语义对齐;!= nullIS NOT NULL
  • 两条 filter 路径都补齐 spec 全量算子 + 客户端别名:$between/$startsWith/$endsWith/$notContains/$null/$exists,以及逻辑算子 $not(取反子条件——CEL !expr 的权限 scope 会编成它,driver-mongodb/driver-memory 都已实现,driver-sql 此前是缺的)。
  • contains/startsWith/endsWith 的值做 LIKE 转义并显式 ESCAPE '\',防止用户输入里的 %/_ 扩大匹配范围。
  • 未知算子在两条路径都抛错(fail-loud),不再静默返回整表。
  • @objectstack/spec:VALID_AST_OPERATORS/AST_OPERATOR_MAP 识别客户端别名拼写并映射到 $null,保证数组-AST → 对象-filter 转换与驱动一致。

顺带修掉的潜在 bug:CEL 派生的 $startsWith/$endsWith/$null 范围过滤(此前对 SQL 静默错误)、better-auth $regex contains(此前退化成精确匹配)。

测试

新增 sql-driver-null-operators.test.ts(17 个用例,覆盖两条路径的空值算子、!=+null、$not$startsWith$regex、未知算子抛错、count 范围正确)。

  • driver-sql: 275 passed
  • spec: 6716 passed
  • plugin-auth: 322 passed(2 个预存在的无关 import 失败)
  • driver-sql / spec build 均成功

范围说明

本 PR 只覆盖 SQL 驱动。理想上"未知算子就报错"应在 parseFilterAST 边界统一做,但 objectql 引擎目前把 ast.where 原样透传给驱动、绕过了 parseFilterAST,所以驱动层是当前唯一能拦截的地方。让所有驱动统一 fail-loud 是一个更大的独立改动,单独开 issue 跟踪。

@vercel

vercelBot commented Jul 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJul 13, 2026 4:00am

Request Review

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation protocol:data tests tooling size/m labels Jul 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/driver-sql, @objectstack/spec.

99 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx(via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx(via @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/spec)
  • content/docs/api/environment-routing.mdx(via @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/spec)
  • content/docs/automation/approvals.mdx(via packages/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via packages/spec)
  • content/docs/automation/hooks.mdx(via @objectstack/spec)
  • content/docs/automation/index.mdx(via @objectstack/spec)
  • content/docs/automation/webhooks.mdx(via @objectstack/spec)
  • content/docs/automation/workflows.mdx(via @objectstack/spec)
  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via packages/spec)
  • content/docs/concepts/north-star.mdx(via packages/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx(via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx(via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx(via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx(via @objectstack/spec)
  • content/docs/data-modeling/index.mdx(via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx(via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx(via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx(via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx(via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/cli.mdx(via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/glossary.mdx(via @objectstack/driver-sql)
  • content/docs/getting-started/quick-reference.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/getting-started/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/kernel/cluster.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx(via packages/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via packages/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/spec)
  • content/docs/permissions/authorization.mdx(via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/permissions/positions.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/anatomy.mdx(via @objectstack/driver-sql)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/protocol/diagram.mdx(via packages/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/index.mdx(via @objectstack/driver-sql)
  • content/docs/protocol/objectos/lifecycle.mdx(via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/runtime-capabilities.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via packages/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via packages/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx(via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/spec)
  • content/docs/releases/v13.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@baozhoutao

Copy link
Copy Markdown
ContributorAuthor

范围说明里提到的"让所有驱动统一 fail-loud"的更大改动,已单独开:#2873

@os-zhuang
os-zhuang merged commit ce6d151 into mainJul 13, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/github-issue-2704-0d903e branch July 13, 2026 05:48
…/ IS NOT NULL; $not (#2704)
The array-format where path forwarded any unrecognised operator straight to
Knex; on a null comparand that silently became a whole-table match, so a
permission/assignment-scoped list view could leak every row (an is_null /
is_empty operator from the client). The object-format path silently degraded an
unknown $op to equality and had no $null handling.
- Render null predicates as real IS NULL / IS NOT NULL, unified with equals+null;
!= null -> IS NOT NULL.
- Support the full spec operator set + client aliases in both filter shapes,
incl. $between / $startsWith / $endsWith / $notContains / $null / $exists
and the logical $not (negated sub-condition; CEL !expr scopes compile to it).
- LIKE-escape contains/startsWith/endsWith with explicit ESCAPE '\'.
- Throw on a genuinely unknown operator in BOTH paths (fail-loud, no leak).
- spec: recognise client alias spellings (isnull/is_empty/...) -> $null.
Adds sql-driver-null-operators.test.ts (17 cases across both paths).
Closes#2704
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:datasize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[driver-sql] 未知查询 operator(is/is_null)静默透传返整表,应报错;缺 $null 处理

2 participants

@baozhoutao@os-zhuang