Skip to content

曝露 gate 对未知对象放行所依赖的「data path 会 404」并不成立 —— findData 无存在性校验(#3545 同类前提) #3770

Description

@os-zhuang

按 Prime Directive #10 记录。#3545 复查(PR #3748)时顺带查了 #3391 家族里同一类论证结构的其它前提 —— 「这层放行没问题,因为另一层会兜住」。找到一条不成立的。

前提

packages/rest/src/rest-server.ts:1141,enforceApiAccess 里逐字:

if (!obj) return false; // unknown object → let the data path 404

对象不在元数据 items 里 → 跳过整个 API 曝露 gate,把兜底交给 data path 的 404。

前提不成立

findData 没有存在性校验。 全仓 OBJECT_NOT_FOUND 只有一处抛出(packages/metadata-protocol/src/protocol.ts:2931),在 cloneData 里;背书它的测试(objectql/src/protocol-data.test.ts:589)测的也是 cloneDatafindData 直接 this.engine.find(request.object, options),中间无校验。

② 引擎对未注册对象不拒绝,拿 name 当表名(engine.ts:1693 resolveObjectName):

const schema = this._registry.getObject(name);
if (schema) return StorageNameMapping.resolveTableName(schema);
return StorageNameMapping.resolveTableName({ name });   // ← 未注册:name 即表名

③ 404 只靠匹配驱动错误字符串(rest-server.ts:334-341:no such table / relation ... does not exist / …)。也就是说 404 是驱动报错的副产品,不是 data path 的性质 —— 驱动不报错就没有 404。

实证

真实 ObjectQL 引擎 + 真实 protocol.findData(list 路由实际调用的那个),内存驱动:

案例 结果
A — 对象未注册,物理表也不存在 threw: false → 返回 200 [],不是 404
B — 物理表存在但对象未注册 threw: false返回数据 { id: 'r1', secret: 'classified' }

案例 A 依赖驱动:SQL 驱动会报 no such table → 经 ③ 映射成 404,所以 A 在生产上通常确实是 404。案例 B 与驱动无关,是真正的缺口:元数据注册表与物理表集合一旦发散(带外建表、注册竞态、注册失败但表已 sync),曝露 gate 被静默跳过,且无任何一层把它变成 404。

现状风险(已被 #3545 修复大幅收窄)

PR #3748 之后,getObjectSecurityMeta 对不可解析对象返回 unresolved,中间件 fail-closed。所以:

  • 已装 plugin-security + 已认证且已解析出授权的调用方 → 现在被拒(姿态不可解析)。这条路已经关上了。
  • ⚠️ 匿名 / 无主体上下文 → 中间件在姿态检查之前就短路(positions/permissions 皆空且无 userIdreturn next()),不受保护。实际可达性取决于部署是否放行匿名读(enforceAuth)。
  • ⚠️ 未装 plugin-security 的部署 → 根本没有中间件。

所以当前真正兜住这件事的是 #3545 的修复,不是注释里说的那个 404。这正是要记一笔的原因:注释在引导后来者依赖一个不存在的兜底,下一个人很可能据此做出错误的放宽决定。

建议(未做决策,留给维护者)

  1. 最小且诚实:改掉那句注释 —— 说明放行的真实理由是「未注册对象没有可执行的曝露策略,由安全中间件的姿态 fail-closed 兜底」,而不是「data path 会 404」。
  2. 收紧:给 findData(及同类无校验的 data 入口)加与 cloneData 一致的注册表存在性校验 → 统一 OBJECT_NOT_FOUND 404。好处是不再依赖驱动错误字符串匹配(那本身就脆:换驱动、换语言环境就可能失配),坏处是要确认没有合法路径依赖「查询未注册对象」。
  3. 兼顾匿名面:若采纳 2,案例 B 对匿名与无 plugin-security 部署也一并关闭。

倾向 1 + 2:2 才是把「declared ≠ enforced」真正收口,1 无论如何都该做。

同批复查中成立的前提(记录备查)

关联:#3545#3391#3498、PR #3748、ADR-0049。

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions