Skip to content

feat(security)!: secure-by-default requireAuth flip + RBAC gates for transfer/restore/purge (#2561 P0)#2562

Merged
os-zhuang merged 4 commits into
mainfrom
claude/production-launch-priorities-ps8hk3
Jul 4, 2026
Merged

feat(security)!: secure-by-default requireAuth flip + RBAC gates for transfer/restore/purge (#2561 P0)#2562
os-zhuang merged 4 commits into
mainfrom
claude/production-launch-priorities-ps8hk3

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

概述

落地 #2561(授权全生命周期缺口地图)framework 侧的两个 P0 上线阻断项:

  1. requireAuth 安全默认翻转(ADR-0056 D2 的 enforce 阶段)— BREAKING
  2. [P0][security] Permission lifecycle ops ungated (allowTransfer/allowRestore/allowPurge) #1883 — transfer/restore/purge 的 RBAC 门预映射

#2561 的第三个 P0(ADR-0086 objectui,越权数据暴露)在 objectstack-ai/objectui 仓库,本会话无权限,需另行处理(见 issue 勾选项)。

1. requireAuth 默认翻转(BREAKING)

变更
spec/api/rest-server.zod.ts requireAuth default(false)default(true)
rest/rest-server.ts normalizeConfig ?? false?? true
rest/rest-api-plugin.ts boot warn 只在显式 requireAuth:false 退出时触发;并修复了原实现读错嵌套路径(config.api.requireAuth 而非 config.api.api.requireAuth)导致即使开了 requireAuth 也误报 warn 的 bug
cli serve 保留刻意的豁免:无 auth tier 的 stack(无人能登录)传显式 false + warn
verify harness 去掉覆盖——整个 dogfood 套件在平台默认姿态下运行,showcase-anonymous-deny 从此证明的是默认值本身
一致性矩阵 requireAuth-default-flip:experimentalenforced(带强制点 + dogfood proof)

翻转的前置条件已满足:公开表单经声明派生的 publicFormGrant 自授权(ADR-0056 Option A,已有 dogfood 证明),share-link 校验 token 后以系统上下文读取,控制面(/auth//health//discovery)豁免。

迁移(一行):刻意公开数据的部署需显式 api: { requireAuth: false }(changeset 已含 FROM→TO 说明)。

2. #1883 — 破坏性生命周期操作 RBAC 门

  • OPERATION_TO_PERMISSION 预映射 transfer→allowTransfer / restore→allowRestore / purge→allowPurge;modifyAllRecords 超级用户旁路覆盖该类操作(对齐 Salesforce "Modify All Data" 语义,管理员不会被锁死)。
  • 操作本身仍未进 ObjectQL(roadmap M2)——但门先于功能就位:操作一旦经安全中间件调度,无对应权限位即拒绝;未映射的破坏性操作继续 fail-closed(ADR-0049)。
  • spec 描述 [EXPERIMENTAL — not enforced][RBAC-gated; operation pending M2];liveness 账本补三条显式条目。
  • 单测:无位拒绝 / 有位放行 / 位不跨操作泄漏 / modifyAllRecords 旁路。

验证

  • 全量 turbo 测试套件绿(129 tasks;含 spec 6669、dogfood 181、rest 198、cli/runtime/client 全部)。
  • check:liveness 门通过(全部属性已分类)。
  • 浏览器实测(真实 showcase 服务器,Playwright 驱动 Chromium):
    • 匿名 GET/POST /api/v1/data/*401
    • 浏览器内登录 admin@objectos.ai 后同一 URL → 200
    • 匿名公开表单 GET /forms/contact-us → 200,submit201;同一匿名会话直打数据 API 仍 401(grant 不外溢)
  • 测试适配:REST/client 单测直接调度 handler、测的是路由机制而非认证门,按「刻意公开部署」显式 requireAuth:false 退出(认证门由 rest-auth-gate 单测 + anonymous-deny dogfood 覆盖)。

关联

🤖 Generated with Claude Code

https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT


Generated by Claude Code

…transfer/restore/purge (#2561 P0)

Two production-launch blockers from the authorization gap map (#2561):

1. requireAuth default flip (ADR-0056 D2, enforce stage) — BREAKING
   - spec: RestApiConfigSchema.requireAuth default(false) → default(true);
     rest-server normalizeConfig mirrors (?? true). Anonymous /data/* is
     now denied (401) unless the deployment explicitly opts out.
   - rest-api-plugin: boot warning now fires only for the EXPLICIT
     requireAuth:false opt-out, and reads the correct nested config path
     (the flat read previously warned even when requireAuth was on).
   - cli serve: keeps the deliberate carve-out — auth-less stacks get an
     explicit false (nothing could authenticate against them), warned.
   - verify harness: no override — every dogfood proof now runs on the
     platform default; anonymous-deny + public-form survival prove the
     flip (publicFormGrant was the pre-req that unblocked it).
   - conformance matrix: requireAuth-default-flip experimental → enforced.
   - REST/client unit tests that dispatch handlers anonymously opt out
     explicitly (they test routing mechanics, not the auth gate).

2. #1883 — destructive lifecycle ops RBAC gate (pre-mapped)
   - permission-evaluator: OPERATION_TO_PERMISSION now maps
     transfer→allowTransfer, restore→allowRestore, purge→allowPurge;
     modifyAllRecords super-user bypass covers the class. Ops still do
     not exist in ObjectQL (M2) — but there is no ungated window when
     they ship; unmapped destructive ops keep failing closed (ADR-0049).
   - spec: bits re-described [EXPERIMENTAL — not enforced] →
     [RBAC-gated; operation pending M2]; liveness ledger entries added.
   - unit tests: deny-without-bit / allow-with-bit / no-cross-leak /
     modifyAllRecords bypass.

Verified: full turbo test suite green (129 tasks); browser-driven
showcase run proves anonymous 401, authenticated 200, anonymous public
form 200/201 with the data API still closed to the same session.

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

vercel Bot commented Jul 4, 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 4, 2026 8:05am

Request Review

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

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 8 package(s): @objectstack/cli, @objectstack/client, @objectstack/dogfood, @objectstack/plugin-dev, @objectstack/plugin-security, @objectstack/rest, @objectstack/spec, @objectstack/verify.

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

  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/cloud-artifact-api.mdx (via packages/cli, packages/spec)
  • content/docs/concepts/cluster-semantics.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/implementation-status.mdx (via @objectstack/cli, @objectstack/client, @objectstack/plugin-security, @objectstack/rest, @objectstack/spec, @objectstack/verify)
  • 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/concepts/packages.mdx (via @objectstack/cli, @objectstack/client, @objectstack/plugin-dev, @objectstack/plugin-security, @objectstack/rest, @objectstack/spec)
  • content/docs/concepts/setup-app.mdx (via @objectstack/spec)
  • content/docs/concepts/skills.mdx (via @objectstack/spec)
  • content/docs/concepts/webhook-delivery.mdx (via @objectstack/spec)
  • content/docs/getting-started/architecture.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/cli, @objectstack/plugin-security, @objectstack/spec)
  • content/docs/getting-started/core-concepts.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/guides/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/guides/ai-capabilities.mdx (via @objectstack/spec)
  • content/docs/guides/airtable-dashboard-analysis.mdx (via @objectstack/spec)
  • content/docs/guides/analytics-datasets.mdx (via @objectstack/spec)
  • content/docs/guides/api-reference.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/guides/authentication.mdx (via @objectstack/cli, @objectstack/client)
  • content/docs/guides/business-logic.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/error-catalog.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-type-gallery.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-validation-rules.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/permissions-matrix.mdx (via packages/plugins/plugin-security, @objectstack/spec)
  • content/docs/guides/cheatsheets/protocol-diagram.mdx (via packages/spec)
  • content/docs/guides/cheatsheets/query-cheat-sheet.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/quick-reference.mdx (via @objectstack/spec)
  • content/docs/guides/client-sdk.mdx (via @objectstack/cli, @objectstack/client, @objectstack/spec)
  • content/docs/guides/common-patterns.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/auth-service.mdx (via packages/spec)
  • content/docs/guides/contracts/cache-service.mdx (via packages/spec)
  • content/docs/guides/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/index.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/guides/contracts/storage-service.mdx (via packages/spec)
  • content/docs/guides/data-modeling.mdx (via @objectstack/spec)
  • content/docs/guides/driver-configuration.mdx (via @objectstack/spec)
  • content/docs/guides/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/guides/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/guides/external-datasources.mdx (via @objectstack/spec)
  • content/docs/guides/formula.mdx (via @objectstack/spec)
  • content/docs/guides/hook-bodies.mdx (via packages/cli, packages/spec)
  • content/docs/guides/kernel-services.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/dashboard.mdx (via @objectstack/plugin-security, @objectstack/spec)
  • content/docs/guides/metadata/field.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/flow.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/index.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/object.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/validation.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/workflow.mdx (via @objectstack/spec)
  • content/docs/guides/packages.mdx (via @objectstack/cli, @objectstack/client, @objectstack/plugin-dev, @objectstack/plugin-security, @objectstack/rest, @objectstack/spec)
  • content/docs/guides/plugin-development.mdx (via @objectstack/spec)
  • content/docs/guides/plugins.mdx (via @objectstack/plugin-security, @objectstack/rest, @objectstack/spec)
  • content/docs/guides/project-scoping.mdx (via @objectstack/cli, @objectstack/client, @objectstack/spec)
  • content/docs/guides/public-forms.mdx (via @objectstack/spec)
  • content/docs/guides/runtime-services/data-service.mdx (via packages/cli, packages/client)
  • content/docs/guides/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/index.mdx (via packages/cli, packages/client, packages/spec)
  • content/docs/guides/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/guides/security.mdx (via @objectstack/plugin-security, @objectstack/spec)
  • content/docs/guides/seed-data.mdx (via @objectstack/spec)
  • content/docs/guides/skills.mdx (via packages/cli, packages/client, @objectstack/spec)
  • content/docs/guides/solutions/approval-workflow.mdx (via packages/spec)
  • content/docs/guides/solutions/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/guides/solutions/data-automation-interface-access.mdx (via packages/plugins/plugin-security)
  • content/docs/guides/solutions/role-based-interfaces.mdx (via packages/plugins/plugin-security)
  • content/docs/guides/standards.mdx (via @objectstack/spec)
  • content/docs/guides/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/guides/validating-metadata.mdx (via @objectstack/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/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/protocol/objectos/realtime-protocol.mdx (via @objectstack/cli, @objectstack/client)
  • 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/index.mdx (via @objectstack/spec)
  • content/docs/releases/v9.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.

…ssion docs

The permissions-matrix cheatsheet and ObjectQL security protocol page listed
allowTransfer/allowRestore/allowPurge without noting the operations do not
exist yet (roadmap M2) — per the declared≠enforced honesty rule, both now
carry the pending-M2 / gate-pre-mapped caveat (#1883). Also documents the
modifyAllRecords bypass extension to the lifecycle class.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT
…in ADR-0066 future refinements

Two findings from the 2026-07 pre-launch authorization assessment that had
no ADR home:
- ⑧ runtime FLS is block-list + most-permissive union — undeclared fields
  are visible by default and a field-level deny cannot be expressed until
  the muting layer (⑦) covers field grants;
- ⑨ capability strings need an authoring/publish-gate lint when the D1
  registry lands (typos currently fail closed but undiscoverably).

Companion engineering issue (not ADR material): #2565 — surface swallowed
dbLoader failures in resolvePermissionSets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT
… destructive-op gates

Correctness (record-level enforcement for the pre-mapped destructive ops):
- security-plugin middleware: extend the write-scope stash, the #1994
  pre-image RLS check, and the ADR-0055 controlled-by-parent write gate to
  cover transfer/restore/purge — the object-level RBAC bit was gated but the
  row-level defenses only listed update/delete/insert, so a granted user
  could have destroyed out-of-scope rows by id when the M2 ops ship. purge
  maps onto the delete RLS class, transfer/restore onto update. New unit
  test proves a not-owned purge is denied by the pre-image check.

Callsite coverage for the default flip:
- cli serve: carve-out now keys on (tierEnabled('auth') || hasAuthPlugin) —
  a stack mounting AuthPlugin explicitly under a minimal tier no longer gets
  an accidental fail-open override.
- plugin-dev: mirror the serve carve-out — an auth-less dev stack gets an
  explicit requireAuth:false so the local data API isn't bricked.
- spec: declare ObjectStackDefinitionSchema.api so the documented opt-out
  (and the enableProjectScoping/projectResolution/enforceProjectMembership
  knobs serve.ts already reads) survives defineStack strict parsing instead
  of being silently stripped. Verified: defineStack now returns the api key.
- rest-api-plugin: warn only on an explicit opt-out; add a misplaced-key
  guard for a flat api.requireAuth (silently ignored under the deny default).
- fix two in-tree tests that passed requireAuth at the wrong nesting.

Cleanup:
- derive the modifyAllRecords write-bypass key set from OPERATION_TO_PERMISSION
  + DESTRUCTIVE_OPERATIONS (module-level Set, no per-call array) so a future
  destructive op is covered automatically.

Docs / provenance:
- regenerate the auto-gen references (requireAuth + allow*{Transfer,Restore,
  Purge} describe text); CHANGELOG entry for the breaking flip; changeset
  migration note updated to the defineStack-level api opt-out + scope note.

Pre-existing anonymous-posture gap on /meta, dispatcher /graphql, and raw
hono /data (surfaces that never call enforceAuth) filed as #2567 — out of
scope for this flip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT
@github-actions github-actions Bot added size/l and removed size/m labels Jul 4, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review July 4, 2026 08:18
@os-zhuang
os-zhuang merged commit 7c09621 into main Jul 4, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/production-launch-priorities-ps8hk3 branch July 4, 2026 08:25
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 size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants