背景
在 #3004 修复(PR #3018)中,plugin-security 中间件新增了 step 3.5 的 owner_id 属主守卫:非特权写入者不能在 insert 时伪造属主。但该守卫位于中间件后段。
问题
publicFormGrant 路径在中间件前段就 return next()(packages/plugins/plugin-security/src/security-plugin.ts 约 566–578 行),整段 CRUD/FLS/owner 守卫都不执行。因此匿名的公开表单提交(ADR-0056 Option A)插入记录时完全不经过 step 3.5 的 owner_id 伪造检查。
字段侧的唯一防线是 REST 路由的“已声明字段白名单”,但在 packages/rest/src/rest-server.ts(约 4613 行 Object.assign(filteredData, rawBody))存在一条:当表单声明零个 section 字段时会回退为原样合并 rawBody。
影响
一个 FormView 若未声明 section 字段(或显式声明了 owner_id),未认证访客可以 POST /forms/:slug/submit 携带 owner_id=<victim>,把记录挂到受害者名下——正是 #3004 的 insert-forge,但无需任何凭证。
期望
公开表单的 create 也应受属主锚点约束:匿名/公开表单上下文没有 transfer 授权,owner_id 应被强制回填/剥离而非原样接收。可选方案:
- 在
publicFormGrant 分支放行前,对目标对象的 owner_id(以及其它系统托管锚点)做同样的强制托管;或
- 在表单提交路由把
owner_id 从可写字段中无条件排除(表单永远不允许客户端指定属主)。
关联
背景
在 #3004 修复(PR #3018)中,
plugin-security中间件新增了 step 3.5 的owner_id属主守卫:非特权写入者不能在 insert 时伪造属主。但该守卫位于中间件后段。问题
publicFormGrant路径在中间件前段就return next()(packages/plugins/plugin-security/src/security-plugin.ts约 566–578 行),整段 CRUD/FLS/owner 守卫都不执行。因此匿名的公开表单提交(ADR-0056 Option A)插入记录时完全不经过 step 3.5 的owner_id伪造检查。字段侧的唯一防线是 REST 路由的“已声明字段白名单”,但在
packages/rest/src/rest-server.ts(约 4613 行Object.assign(filteredData, rawBody))存在一条:当表单声明零个 section 字段时会回退为原样合并rawBody。影响
一个 FormView 若未声明 section 字段(或显式声明了
owner_id),未认证访客可以POST /forms/:slug/submit携带owner_id=<victim>,把记录挂到受害者名下——正是 #3004 的 insert-forge,但无需任何凭证。期望
公开表单的 create 也应受属主锚点约束:匿名/公开表单上下文没有 transfer 授权,
owner_id应被强制回填/剥离而非原样接收。可选方案:publicFormGrant分支放行前,对目标对象的owner_id(以及其它系统托管锚点)做同样的强制托管;或owner_id从可写字段中无条件排除(表单永远不允许客户端指定属主)。关联