Skip to content

fix(plugin-security): #2937 — Layer 0 insert post-image 租户检查#2940

Merged
os-zhuang merged 1 commit into
mainfrom
claude/authz-2937-insert-tenant-guard
Jul 15, 2026
Merged

fix(plugin-security): #2937 — Layer 0 insert post-image 租户检查#2940
os-zhuang merged 1 commit into
mainfrom
claude/authz-2937-insert-tenant-guard

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

背景

Closes #2937(安全修复 + 行为变更)。多组织模式(tenancy.mode='multi' + @objectstack/organizations)下,一个普通用户此前可以 insert 一条伪造 organization_id(指向别的租户)的业务记录并使其落进受害租户。ADR-0095 D1 的 Layer 0 租户墙 AND-composed 到读 + update/delete 的 pre-image,但 insert 没有 pre-image、也不带 AST,从未被门控(是 D1 读侧 W1 的写侧未竟部分)。

配套 cloud PR:objectstack-ai/cloud#829(auto-stamp 权威覆盖,纵深防御)。两处任一被绕过,另一处仍堵住跨租户落地。

改动

  • security-plugin.ts 新增中间件步骤 3.7:对 insert 的 post-image 复用读侧同一套 Layer 0 决策(computeInsertTenantCheckFiltercomputeLayeredRlsFilterlayer0)做校验 —— 一个显式提供organization_id 必须等于调用者的活动组织,否则 fail-closed 拒绝(PermissionDeniedError)。
    • 规则与读侧完全一致:单组织/隔离未激活、非租户对象(无 organization_id 列或 tenancy.enabled:false)、platform-admin 姿态豁免的对象 → 不适用;无活动组织的用户提供任意 org_id → 拒(deny sentinel)。
    • 只校验显式提供的值:缺省(不带 organization_id)的 insert 由 @objectstack/organizations 的 auto-stamp 补全 —— 职责分离,因此本检查与 auto-stamp 中间件的注册顺序无关
  • authz-matrix-gate.test.ts:新增 describe('[#2937] Layer 0 insert post-image tenant guard') 八格。
  • authz-conformance.matrix.ts:新增 multi-tenant-insert-postimage ledger 行(单元证明;多组织为企业版,不进 open-core dogfood boot)。

行为 delta(release-notes callout)

此前能成功的「带跨租户 organization_id 的用户级 insert」现被拒绝。缺省 insert、system-context 写、platform-admin 豁免对象、单组织模式 均不受影响

验证(实测)

  • pnpm --filter @objectstack/plugin-security test391 passed(18 files),含新增 8 格 insert 测试。
  • authz-conformance.test.ts(ledger)→ 1 passed
  • tsc --noEmit(plugin-security)→ clean
  • rls-multitenant.dogfood.test.ts → skipped(企业版 @objectstack/organizations 才激活多组织路径 —— 与 issue 说明一致,故以 unit 矩阵门覆盖)。

Reviewer checklist(重点:合法代客设 org_id 不受影响)

  • 系统上下文豁免isSystemsecurity-plugin.ts 中间件入口(L513)即 return next(),本检查不触及。已确认 cloud 的每-org seed replay / clone-org-seed-data.ts / claim-orphan-org-rows.ts 全部走 SYSTEM_CTX = { isSystem: true },它们故意为新 org 设置 explicit(跨 org)organization_id —— 这些合法写路径保持原样。
  • 缺省 insert 不被误伤suppliedOrg != null && suppliedOrg !== '' 门控,缺省值放行(矩阵门 member inserting with NO organization_id passes the wall)。
  • platform-admin 姿态:public 业务对象上 admin 仍被拒(无豁免),private/tenancy-disabled 对象上豁免 —— 与读侧 D1 同规则。
  • 与 cloud auto-stamp 覆盖为纵深防御,非重复;确认注册顺序无关的论证成立。

🤖 Generated with Claude Code

https://claude.ai/code/session_019QRUvVfpvSycAHMMF2xTxs

多组织模式下用户 insert 可伪造 organization_id 越租户墙 (ADR-0095 D1 读侧
W1 的写侧未竟部分)。Layer 0 的租户墙作用于读 + update/delete 的 pre-image,
但 insert 无 pre-image 且不带 AST,从未被门控。

新增中间件步骤 3.7:对 insert 的 post-image 复用读侧同一套 Layer 0 决策
(computeInsertTenantCheckFilter → computeLayeredRlsFilter 的 layer0)校验 ——
显式提供的 organization_id 必须等于调用者活动组织,否则 fail-closed 拒绝。
缺省值不受影响(由 @objectstack/organizations auto-stamp 补全,故与其注册顺序
无关);isSystem 上下文中间件入口短路,合法代客设 org_id(import/迁移/seed
replay)完全不受影响。matrix gate + 授权一致性 ledger 覆盖。

Closes #2937

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QRUvVfpvSycAHMMF2xTxs
@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 15, 2026 1:36am

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling and removed size/m labels Jul 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/dogfood, @objectstack/plugin-security.

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

  • content/docs/getting-started/cli.mdx (via @objectstack/plugin-security)
  • content/docs/permissions/access-recipes.mdx (via packages/plugins/plugin-security)
  • content/docs/permissions/authorization.mdx (via packages/dogfood, packages/plugins/plugin-security)
  • content/docs/permissions/delegated-administration.mdx (via packages/dogfood)
  • content/docs/permissions/explain.mdx (via @objectstack/plugin-security)
  • content/docs/permissions/permissions-matrix.mdx (via packages/plugins/plugin-security)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/plugin-security)
  • content/docs/plugins/index.mdx (via @objectstack/plugin-security)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-security)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-security)
  • content/docs/ui/audience-based-interfaces.mdx (via packages/plugins/plugin-security)
  • content/docs/ui/dashboards.mdx (via @objectstack/plugin-security)

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.

@os-zhuang os-zhuang marked this pull request as ready for review July 15, 2026 01:45
@os-zhuang os-zhuang merged commit 2ae78c6 into main Jul 15, 2026
17 checks passed
@os-zhuang os-zhuang deleted the claude/authz-2937-insert-tenant-guard branch July 15, 2026 01:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants