Repository navigation
fix(runtime, metadata-protocol): the permission-set clone door copies a set that grants on an object this deployment does not install, and member_default's feed grants re-land - #30310
Conversation
…vity grants This reverts the revert 119c2db, restoring 490d136 byte for byte: the platform baseline member_default grants sys_comment (read, create, edit) and sys_activity (read), with its tests and changeset. It lands together with the permission-set clone door fix that the revert was waiting for. Claude-Session: https://claude.ai/code/session_01Utp6CatT2ajve5PJf1Hhzj Co-authored-by: Claude <noreply@anthropic.com>
… a set whose grants name an object this deployment does not install The save door's unknown-grant refusal already leaves alone a grant the set itself already holds. A clone's grants are the source's, copied verbatim, so the clone door now passes the copy's grant keys to saveMetaItem as inheritedGrantObjects, and the refusal does not judge those. An unknown object the save does not say it inherits is still refused. member_default, which grants sys_comment and sys_activity, copies into its package's customization pair again on a boot without @objectstack/plugin-audit. Claude-Session: https://claude.ai/code/session_01Utp6CatT2ajve5PJf1Hhzj Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 3 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 42 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c66f171f0b5680a2e17282e0c8f20eb8b38a57b8 && git checkout c66f171f0b5680a2e17282e0c8f20eb8b38a57b8
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin bfca0efb79ac5df2049f392cdecd3bbeeea9ec61 d770fe2150f293162ce906de2fb910370edcc6fc && git checkout -B drift-repro bfca0efb79ac5df2049f392cdecd3bbeeea9ec61 && git merge --no-ff d770fe2150f293162ce906de2fb910370edcc6fc
node scripts/docs-audit/affected-docs.mjs --json bfca0efb79ac5df2049f392cdecd3bbeeea9ec61
|
|
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL Contract reviewServed-tier: Read-only review of the PR body, file list, the net diff ① Derived judgments
② Semver level
③ Boundary flagsFrom the #30108 dev report (6647979158), all re-landed unchanged in commit 1:
For #30255 (this PR's second commit): the card's claim (6649260029) declared ④ Design judgmentSecurity first — can any external caller set
So the key is server-derived only, as the design requires, and the refusal that #28990 put in front of hand-authored sets still fronts every path a person or client can reach. Does the clone door pass only keys the source holds? Yes (① third bullet). Should-fix S1: Is the alternative (treat optional-plugin platform objects as known) better? No. A boot without Does the fix leave non-clone paths refused, and is that intended? Yes and yes. A fresh hand-authored set granting Should-fix S2 (follow-up card, not this PR): the package duplicate door ( Tests against the code they cite: the new metadata-protocol case drives the real Check-runs on this head, polled four times over eight minutes, last read at 2026-11-05T05:56Z: Implemented-by: VERDICT: PASS Should-fix (neither blocks): S1 derive the inherited set from Generated by Claude Code |
…source set, not from the copy Only a key the source already holds is ever passed to the save as inheritedGrantObjects, whatever the copy step does to the copy's objects map (contract review S1). Claude-Session: https://claude.ai/code/session_01Utp6CatT2ajve5PJf1Hhzj Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL 维护者速读
席位核对 — ACCEPT · domain:server#2 · head
|
Requested by Bill · project thread
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL
维护者速读
member_default授予sys_comment读建改、sys_activity读,与 fix(plugin-security): every member reads and posts comments, and reads the activity history, of the records they can read #30222 逐字节相同);二是修权限集克隆门:副本从源集合继承来的授权键交给保存门,保存门的未知对象拒收不再审这些键。member_default会被 422 拒收。上个 PR revert: PR #30222 (main red #30255) #30262 先回退止血,这个 PR 把功能和修复一起带回来。/meta保存、草稿发布,照拒不误。saveMetaItem的请求多了一个可选键,在 metadata-protocol 的公开类型里可达,所以申报 Clause-② yes (widening)。回滚即撤本 PR。packages/metadata-protocol/src/protocol-save-door-refusals.ts新增的过滤,再看packages/runtime/src/domains/permission-set-clone.ts第 7 步保存。packages/qa/dogfood跑pnpm exec vitest run test/permission-set-clone-door.dogfood.test.ts(依赖先构建),预期 11 条全绿。Fixes #30255
Fixes #30108
Clause-②: yes (widening) —
ObjectStackProtocolImplementation.saveMetaItem's request gains one optional key,inheritedGrantObjects, reachable from@objectstack/metadata-protocol's.entry typesMetadata-protocol: none
Why main went red, measured
Run 41961254209 (
797f9257),Dogfood Regression Gate (2/3), casepermission-set-clone-door.dogfood.test.ts"a platform set (the lock-and-clone rule's commonest source) copies into its shipping package's pair":422 INVALID_METADATAwith two findings,objects.sys_commentandobjects.sys_activity, each "no package registers an object of that name and none is saved here".@objectstack/verify'sbootStackconstructs noAuditPlugin(fact 3 ofpackages/cli/src/commands/serve-audit-registration.contract.test.ts), and the showcase'srequiresdoes not nameaudit. So neither object is registered on that boot.sys_inbox_messageandsys_notification_receipt, whichmember_defaultalready granted, pass because the showcase requiresmessaging.@objectstack/plugin-auditcould not clonemember_defaultonce fix(plugin-security): every member reads and posts comments, and reads the activity history, of the records they can read #30222 landed.What changes
The clone door is told which grants it inherits (
packages/runtime/src/domains/permission-set-clone.ts). Step 7'ssaveMetaItemcall passesinheritedGrantObjects: inheritedGrantObjectsOf(clone), which is the keys of the copy'sobjectsmap.clonePermissionSetDefinitioncarries every one of them unchanged from the source.The refusal leaves inherited grants to the source's save (
@objectstack/metadata-protocol):protocol-door-requests.ts:SaveMetaItemRequestgainsinheritedGrantObjects?: readonly string[], documented as server-set only.protocol-save.ts: the save path forwards it to the refusal. Draft and publish mode are both covered, and amigrate-storedsave is still exempt as before.protocol.tsandprotocol-save-door-refusals.ts:unknownGrantObjectSaveRefusaltakes it as a trailing parameter and drops those keys from the unknown list, after the existing "the set's current definition already grants on it" step. This is that rule applied to a copy: the grant was introduced by the source and is judged where the source introduced it. The method's docblock gains the bullet./metasave, a re-save that adds a grant, a copy that adds one, and a draft's publish (promoteDraftForPublishand the restore doors pass no inherited set). No HTTP door forwards the key:PUT /metabuilds its request field by field.#30108 re-landed: reverting
119c2db1(PR #30262, the revert) restoresmember_default's two grants, their tests and their changeset.git diff 490d136f HEADover those five paths is empty apart from the changeset's one added bullet, which says the grant now lands with this fix. #30108's measurements are in PR #30222's body.Changesets:
.changeset/30108-member-default-feed-grants.md(restored,@objectstack/plugin-securitypatch, with one bullet added that names this fix) and.changeset/30255-clone-inherits-held-grants.md(@objectstack/metadata-protocoland@objectstack/runtimepatch, both declaration lines).Clause-②, measured
@objectstack/metadata-protocol'sexportsmap has one entry,., typed by./dist/index.d.tsand./dist/index.d.cts. Built on this tree,dist/index.d.tsdeclaresSaveMetaItemRequest(a non-exported alias) withinheritedGrantObjects?: readonly string[], and the exported classObjectStackProtocolImplementation'ssaveMetaItem(request: SaveMetaItemRequest)references it. So the key is reachable from the entry's type graph: a widening of the published surface. Nothing a metadata author writes changes, soMetadata-protocol: none.Verification
On head
591198e4(branch offmainef2ad31c), underscripts/pm/os-verify-lock.sh:pnpm installexit 0, then the dogfood dependency closure build (pnpm --filter '@objectstack/dogfood^...' build) exit 0.packages/qa/dogfood, run withvitest run:permission-set-clone-door.dogfood.test.ts,permission-unknown-grant-object-refusal.dogfood.test.ts,comments-permission-matrix.dogfood.test.ts,me-apps-and-everyone-baseline.dogfood.test.ts. Result: Test Files 4 passed (4), Tests 34 passed (34). This includes the case that was red onmain, and the metadata: a runtime permission-set save accepts a grant on an object that does not exist (200) — the build refuses the same grant (#18202), so a typo silently grants nothing #28990 refusal's own dogfood file.@objectstack/plugin-security:member-default-feed-grants.integration.test.ts,member-default-explicit-allow.test.ts,comment-reaction-own-record-floor.test.ts. Result: Test Files 3 passed (3), Tests 70 passed (70).pnpm --filter @objectstack/metadata-protocol test: Test Files 312 passed, 4 skipped; Tests 22356 passed, 26 skipped.pnpm --filter @objectstack/runtime test: Test Files 541 passed (541); Tests 8620 passed, 19 skipped.typecheckexit 0 for@objectstack/metadata-protocol,@objectstack/runtimeand@objectstack/plugin-security.dispatch-gates --commands, 65 on this change set) andpnpm lint: their readings are in the seat hand-off.CI does not run the dogfood shards on a pull request or in the merge queue. The runs above are this PR's dogfood evidence.
Ablations (4 legs, each restored and proven by blob hash against HEAD)
Driven through
scripts/ablation-replace.mjs(WRAP mode, underos-verify-lock), on head591198e4.inheritedrequest.inheritedGrantObjectsinheritedGrantObjectsinheritedwidened to every grant of the itemThe dogfood half of F3 is
main's own red: with the grants landed and no fix, the clone-door case answered 422 (runs 41960290148 and 41961254209).Full output: project file
server-seat2/30255/ablations-ran.txt.Human Minimum
packages/specholds no runtime logic (packages/specnot touched).pnpm lint's reading is in the seat hand-off.main, one pull request.packages/spectouched: n/a.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code