Repository navigation
fix(plugin-security)!: an update of a controlled_by_parent detail also asks for edit access on the master its body names (ADR-0055 §3) - #25066
Conversation
…for edit access on every master it involves ADR-0055 §3 requires edit access to the master for every detail write and reads a create's master from the master id in the incoming body. The master-detail write gate now runs its one chain walk on every master the write involves: the master(s) its rows are stored under, as before, and the master id its body names when that is not one of them. A refusal is the by-id leg's envelope and nothing is written; an insert's master is asked once; a write that selects no row involves no master. New real-stack pins in controlled-by-parent-every-master-edit.test.ts and a BREAKING minor changeset. Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 4 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 — 16 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 59fafbf4ea764b5012fbd684ca64b2f50a3c35ff && git checkout 59fafbf4ea764b5012fbd684ca64b2f50a3c35ff
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2b1382a5b3887e4abd194411ef77a7cd368c95c2 4b4cfed7af4166806b44fcc6a4ec23c75c520300 && git checkout -B drift-repro 2b1382a5b3887e4abd194411ef77a7cd368c95c2 && git merge --no-ff 4b4cfed7af4166806b44fcc6a4ec23c75c520300
node scripts/docs-audit/affected-docs.mjs --json 2b1382a5b3887e4abd194411ef77a7cd368c95c2
|
Contract reviewServed-tier: Inputs: card #25044 (body and every comment through the dev report ① Derived judgmentsGoverning text: ADR-0055 §3 (
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
Changeset text only; no code or test change. Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK Co-authored-by: Claude <noreply@anthropic.com>
席位核对 · ACCEPT ·
|
Claude-Session: https://claude.ai/code/session_018EszgtA3V2KXBX5tf59Hi4 Co-authored-by: Claude <noreply@anthropic.com>
…-title, keyed-text, webhook-report cases) meet a NOT NULL tenant column - plugin-security: an organization-less permission-set catalogue row is legacy residue, so six suites relax only sys_permission_set to its pre-18 shape. Driver-written seeds name the rig's organization (readable-ids-nested-compose, controlled-by-parent-master-object-write). The lean person-title engine gets the Default Organization. - rest (stored-expression dropped fields), plugin-webhooks (auto-enqueuer report), plugin-approvals (reader-locale labels, keyed text): each lean engine gets the Default Organization. The keyed-text out-of-office delegation names the deals' organization, where its request opens. No assertion changed. Claude-Session: https://claude.ai/code/session_018EszgtA3V2KXBX5tf59Hi4 Co-authored-by: Claude <noreply@anthropic.com>
Fixes #25044
Clause-②: no (narrowing)
What changed
ADR-0055 §3 requires edit access to the master for every write to a
controlled_by_parentdetail, and it reads a create's master from the master id in the incoming body. A write can involve more than one master. For one shape of write, the master-detail write gate (assertControlledByParentWrite, step 2.8) asked the master-edit question of only one of the masters the write involves. It now asks every one of them.packages/plugins/plugin-security/src/security-plugin.ts, insideassertControlledByParentWriteonly. The gate first asks about the master or masters it already asked about, unchanged. Then it runs the same chain walk on the master id the write's body names, unless that master was already asked. The walk isassertControlledByParentChainEditable, the one walk PR fix(plugin-security)!: an update or delete of a controlled_by_parent detail asks for edit access to its master on every write shape (ADR-0055 §3) #25042 extracted; no second copy exists.403 PERMISSION_DENIED, naming the caller's detail and operation, with its leg recorded. It is thrown before the write runs, so nothing is written.computeControlledByParentFilter,packages/objectql/**andpackages/spec/**are untouched.packages/plugins/plugin-security/src/controlled-by-parent-every-master-edit.test.ts(new): 17 cases on a real stack. That isObjectQLover better-sqlite3, the realSecurityPlugin, the real sharing service and middleware, and contexts built byresolveAuthzContextandassembleExecutionContext..changeset/25044-cbp-every-master-edit.md:@objectstack/plugin-securityminor. It is BREAKING under the launch-window convention for an accept-set narrowing. It carries theClause-②line and the ADR-0087 dispositionnot-required (no-migration-prescription).Reach
Reach is stated abstractly here, because the disclosure ruling recorded on #9387 applies. The reading was re-measured on a real
ObjectQL+SqlDriver+SecurityPlugin, for a member and for an organization admin. The write landed onorigin/maineddffc3390(before PR #25042) and again on PR #25042's head033fc75a09(after it). The probes were throwaway and are not committed. The detail is held privately.Verification
Every run below is on this branch's head
a447bfdb8f, after the final commit, unless it is marked otherwise. Each one ran under the shared verify lock.Build. The dependency closure was built with
pnpm --filter '@objectstack/dogfood^...' build(a superset of@objectstack/plugin-security^..., plugin-security itself included). It exited 0.The new pins.
controlled-by-parent-every-master-edit.test.tspasses 17/17, beside PR fix(plugin-security)!: an update or delete of a controlled_by_parent detail asks for edit access to its master on every write shape (ADR-0055 §3) #25042'scontrolled-by-parent-write-shape-master-edit.test.ts(32/32 for the two files together).The package.
pnpm --filter @objectstack/plugin-security exec vitest run: Test Files 202 passed (202), Tests 4156 passed and 45 skipped (4201).pnpm --filter @objectstack/plugin-security typecheckexited 0. That coverstscon both programs andcheck:test-typecheck, and the new test file is in thetsconfig.test.jsonprogram (--listFiles, 1 hit).Dogfood battery (
pnpm --filter @objectstack/dogfood test, no shard and no path, owed by the!title). The lock's verdict was command-exit 0. Test Files: 285 passed, 3 skipped (288). Tests: 2375 passed, 18 skipped (2393).Derived gate families.
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives 69 families. All 69 ran, and each exit code was captured before any pipe.--ranreports: 69 run, 0 NOT-MEASURED, 0 UNRUN.Ablation, one leg per pinned behaviour. The fix was committed first. Each leg is a mutation made through
scripts/ablation-replace.mjs, where the anchor must hit and the blob must change. Each restore isgit checkout HEAD --, proven by blob equality withHEADand an emptygit diff HEAD. The subject is imported as./security-plugin.jsfrom source, so nodist/sits on the path. The result:The first L4 attempt was refused by the tool: its replacement contained its anchor, so the anchor count did not move. It measured nothing and was rerun with a non-overlapping anchor. The unmutated control passed 17/17. The final tree is byte-identical to
HEAD.Base. The branch is
mainatb2890909aa(PR fix(plugin-security)!: an update or delete of a controlled_by_parent detail asks for edit access to its master on every write shape (ADR-0055 §3) #25042 landed) plus this change, merged withorigin/main72518d20f5before the runs above. Since thenmainhas gained one commit (4bf079c693, plugin-auth), which is disjoint from this diff.Acceptance notes
Seat's append (
domain:servicesseat 1, from the dev's patch-round report6310152347on #25044; the dev writes its PR body once):4b4cfed7af): only the changeset headline changes, narrowed to the update shape this diff covers, per contract review6310100720③.1. No code or test line moved.git diff --stat a447bfdb8f 4b4cfed7aftouches only.changeset/25044-cbp-every-master-edit.md(+1/−1). Code is byte-identical, so the dogfood battery and the package and gate runs ata447bfdb8fstand.Generated by Claude Code