feat(security)!: secure-by-default requireAuth flip + RBAC gates for transfer/restore/purge (#2561 P0)#2562
Merged
Conversation
…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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 8 package(s): 97 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
18 tasks
…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
This was referenced Jul 4, 2026
feat(security): permission-set package provenance + bootstrapDeclaredPermissions (ADR-0086 P1)
#2566
Merged
… 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
os-zhuang
marked this pull request as ready for review
July 4, 2026 08:18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概述
落地 #2561(授权全生命周期缺口地图)framework 侧的两个 P0 上线阻断项:
requireAuth安全默认翻转(ADR-0056 D2 的 enforce 阶段)— BREAKING1. requireAuth 默认翻转(BREAKING)
spec/api/rest-server.zod.tsrequireAuthdefault(false)→default(true)rest/rest-server.tsnormalizeConfig?? false→?? truerest/rest-api-plugin.tsrequireAuth:false退出时触发;并修复了原实现读错嵌套路径(config.api.requireAuth而非config.api.api.requireAuth)导致即使开了 requireAuth 也误报 warn 的 bugcli serveauthtier 的 stack(无人能登录)传显式false+ warnverifyharnessshowcase-anonymous-deny从此证明的是默认值本身requireAuth-default-flip:experimental→enforced(带强制点 + 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" 语义,管理员不会被锁死)。[EXPERIMENTAL — not enforced]→[RBAC-gated; operation pending M2];liveness 账本补三条显式条目。验证
check:liveness门通过(全部属性已分类)。GET/POST /api/v1/data/*→ 401admin@objectos.ai后同一 URL → 200GET /forms/contact-us→ 200,submit→ 201;同一匿名会话直打数据 API 仍 401(grant 不外溢)requireAuth:false退出(认证门由 rest-auth-gate 单测 + anonymous-deny dogfood 覆盖)。关联
implementation-status.mdx、guides/security.mdx、changesets ×2(major: spec/rest;minor: plugin-security)🤖 Generated with Claude Code
https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT
Generated by Claude Code